Support arbitrary swizzles of image data.

The swizzles are passed to shaders that need them using an "auxiliary
buffer", for which there must be room in the function argument table.
Code is inserted into the shaders to swizzle reads from sampled
textures--per the Vulkan spec, component mappings must only be applied
to sampled images.

Update SPIRV-Cross to pull in some fixes for handling swizzled image
data. Bump MoltenVK version.
This commit is contained in:
Chip Davis 2018-09-30 13:07:28 -05:00
parent 7dd143411a
commit 082d282b28
13 changed files with 256 additions and 41 deletions

View File

@ -1 +1 @@
c9210427b9ab547d41f1af804dedae581b382965
cc5c0204d8bcdadbb4add03e53346df98bf27fa4

View File

@ -48,7 +48,7 @@ extern "C" {
*/
#define MVK_VERSION_MAJOR 1
#define MVK_VERSION_MINOR 0
#define MVK_VERSION_PATCH 23
#define MVK_VERSION_PATCH 24
#define MVK_MAKE_VERSION(major, minor, patch) (((major) * 10000) + ((minor) * 100) + (patch))
#define MVK_VERSION MVK_MAKE_VERSION(MVK_VERSION_MAJOR, MVK_VERSION_MINOR, MVK_VERSION_PATCH)

View File

@ -25,6 +25,8 @@
class MVKCommandEncoder;
class MVKOcclusionQueryPool;
struct MVKShaderAuxBufferBinding;
#pragma mark -
#pragma mark MVKCommandEncoderState
@ -388,6 +390,16 @@ protected:
}
}
}
struct AuxBuffer {
uint32_t swizzleConst[1];
};
// Updates the swizzle for an image in the given buffer.
void updateSwizzle(id<MTLBuffer> buffer, uint32_t index, uint32_t swizzle) {
auto* aux = (AuxBuffer*)buffer.contents;
aux->swizzleConst[index] = swizzle;
}
};
@ -425,6 +437,9 @@ public:
_mtlIndexBufferBinding = binding; // No need to track dirty state
}
/** Sets the current auxiliary buffer state. */
void bindAuxBuffer(id<MTLBuffer> buffer, const MVKShaderAuxBufferBinding& binding, bool needVertexAuxBuffer, bool needFragmentAuxBuffer);
#pragma mark Construction
@ -442,6 +457,8 @@ protected:
std::vector<MVKMTLTextureBinding> _fragmentTextureBindings;
std::vector<MVKMTLSamplerStateBinding> _vertexSamplerStateBindings;
std::vector<MVKMTLSamplerStateBinding> _fragmentSamplerStateBindings;
MVKMTLBufferBinding _vertexAuxBufferBinding;
MVKMTLBufferBinding _fragmentAuxBufferBinding;
bool _areVertexBufferBindingsDirty = false;
bool _areFragmentBufferBindingsDirty = false;
@ -469,6 +486,8 @@ public:
/** Binds the specified sampler state. */
void bindSamplerState(const MVKMTLSamplerStateBinding& binding);
/** Sets the current auxiliary buffer state. */
void bindAuxBuffer(id<MTLBuffer> buffer, const MVKShaderAuxBufferBinding& binding);
#pragma mark Construction
@ -483,6 +502,7 @@ protected:
std::vector<MVKMTLBufferBinding> _bufferBindings;
std::vector<MVKMTLTextureBinding> _textureBindings;
std::vector<MVKMTLSamplerStateBinding> _samplerStateBindings;
MVKMTLBufferBinding _auxBufferBinding;
bool _areBufferBindingsDirty = false;
bool _areTextureBindingsDirty = false;

View File

@ -425,6 +425,15 @@ void MVKGraphicsResourcesCommandEncoderState::bindFragmentSamplerState(const MVK
bind(binding, _fragmentSamplerStateBindings, _areFragmentSamplerStateBindingsDirty);
}
void MVKGraphicsResourcesCommandEncoderState::bindAuxBuffer(id<MTLBuffer> buffer, const MVKShaderAuxBufferBinding& binding, bool needVertexAuxBuffer, bool needFragmentAuxBuffer) {
_vertexAuxBufferBinding.mtlBuffer = needVertexAuxBuffer ? buffer : nil;
_vertexAuxBufferBinding.index = binding.vertex;
_vertexAuxBufferBinding.isDirty = needVertexAuxBuffer;
_fragmentAuxBufferBinding.mtlBuffer = needFragmentAuxBuffer ? buffer : nil;
_fragmentAuxBufferBinding.index = binding.fragment;
_fragmentAuxBufferBinding.isDirty = needFragmentAuxBuffer;
}
// Mark everything as dirty
void MVKGraphicsResourcesCommandEncoderState::markDirty() {
MVKCommandEncoderState::markDirty();
@ -434,10 +443,26 @@ void MVKGraphicsResourcesCommandEncoderState::markDirty() {
MVKResourcesCommandEncoderState::markDirty(_fragmentTextureBindings, _areFragmentTextureBindingsDirty);
MVKResourcesCommandEncoderState::markDirty(_vertexSamplerStateBindings, _areVertexSamplerStateBindingsDirty);
MVKResourcesCommandEncoderState::markDirty(_fragmentSamplerStateBindings, _areFragmentSamplerStateBindingsDirty);
_vertexAuxBufferBinding.isDirty = true;
_fragmentAuxBufferBinding.isDirty = true;
}
void MVKGraphicsResourcesCommandEncoderState::encodeImpl() {
if (_vertexAuxBufferBinding.mtlBuffer) {
for (auto& b : _vertexTextureBindings) {
if (b.isDirty)
updateSwizzle(_vertexAuxBufferBinding.mtlBuffer, b.index, b.swizzle);
}
}
if (_fragmentAuxBufferBinding.mtlBuffer) {
for (auto& b : _fragmentTextureBindings) {
if (b.isDirty)
updateSwizzle(_fragmentAuxBufferBinding.mtlBuffer, b.index, b.swizzle);
}
}
encodeBinding<MVKMTLBufferBinding>(_vertexBufferBindings, _areVertexBufferBindingsDirty,
[](MVKCommandEncoder* cmdEncoder, MVKMTLBufferBinding& b)->void {
[cmdEncoder->_mtlRenderEncoder setVertexBuffer: b.mtlBuffer
@ -452,6 +477,20 @@ void MVKGraphicsResourcesCommandEncoderState::encodeImpl() {
atIndex: b.index];
});
if (_vertexAuxBufferBinding.isDirty) {
_vertexAuxBufferBinding.isDirty = false;
[_cmdEncoder->_mtlRenderEncoder setVertexBuffer: _vertexAuxBufferBinding.mtlBuffer
offset: 0
atIndex: _vertexAuxBufferBinding.index];
}
if (_fragmentAuxBufferBinding.isDirty) {
_fragmentAuxBufferBinding.isDirty = false;
[_cmdEncoder->_mtlRenderEncoder setFragmentBuffer: _fragmentAuxBufferBinding.mtlBuffer
offset: 0
atIndex: _fragmentAuxBufferBinding.index];
}
encodeBinding<MVKMTLTextureBinding>(_vertexTextureBindings, _areVertexTextureBindingsDirty,
[](MVKCommandEncoder* cmdEncoder, MVKMTLTextureBinding& b)->void {
[cmdEncoder->_mtlRenderEncoder setVertexTexture: b.mtlTexture
@ -484,6 +523,8 @@ void MVKGraphicsResourcesCommandEncoderState::resetImpl() {
_fragmentTextureBindings.clear();
_vertexSamplerStateBindings.clear();
_fragmentSamplerStateBindings.clear();
_vertexAuxBufferBinding.mtlBuffer = nil;
_fragmentAuxBufferBinding.mtlBuffer = nil;
_areVertexBufferBindingsDirty = false;
_areFragmentBufferBindingsDirty = false;
@ -491,6 +532,8 @@ void MVKGraphicsResourcesCommandEncoderState::resetImpl() {
_areFragmentTextureBindingsDirty = false;
_areVertexSamplerStateBindingsDirty = false;
_areFragmentSamplerStateBindingsDirty = false;
_vertexAuxBufferBinding.isDirty = false;
_fragmentAuxBufferBinding.isDirty = false;
}
@ -509,16 +552,30 @@ void MVKComputeResourcesCommandEncoderState::bindSamplerState(const MVKMTLSample
bind(binding, _samplerStateBindings, _areSamplerStateBindingsDirty);
}
void MVKComputeResourcesCommandEncoderState::bindAuxBuffer(id<MTLBuffer> buffer, const MVKShaderAuxBufferBinding& binding) {
_auxBufferBinding.mtlBuffer = buffer;
_auxBufferBinding.index = binding.compute;
_auxBufferBinding.isDirty = buffer != nil;
}
// Mark everything as dirty
void MVKComputeResourcesCommandEncoderState::markDirty() {
MVKCommandEncoderState::markDirty();
MVKResourcesCommandEncoderState::markDirty(_bufferBindings, _areBufferBindingsDirty);
MVKResourcesCommandEncoderState::markDirty(_textureBindings, _areTextureBindingsDirty);
MVKResourcesCommandEncoderState::markDirty(_samplerStateBindings, _areSamplerStateBindingsDirty);
_auxBufferBinding.isDirty = true;
}
void MVKComputeResourcesCommandEncoderState::encodeImpl() {
if (_auxBufferBinding.mtlBuffer) {
for (auto& b : _textureBindings) {
if (b.isDirty)
updateSwizzle(_auxBufferBinding.mtlBuffer, b.index, b.swizzle);
}
}
encodeBinding<MVKMTLBufferBinding>(_bufferBindings, _areBufferBindingsDirty,
[](MVKCommandEncoder* cmdEncoder, MVKMTLBufferBinding& b)->void {
[cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch) setBuffer: b.mtlBuffer
@ -526,6 +583,13 @@ void MVKComputeResourcesCommandEncoderState::encodeImpl() {
atIndex: b.index];
});
if (_auxBufferBinding.isDirty) {
_auxBufferBinding.isDirty = false;
[_cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch) setBuffer: _auxBufferBinding.mtlBuffer
offset: 0
atIndex: _auxBufferBinding.index];
}
encodeBinding<MVKMTLTextureBinding>(_textureBindings, _areTextureBindingsDirty,
[](MVKCommandEncoder* cmdEncoder, MVKMTLTextureBinding& b)->void {
[cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch) setTexture: b.mtlTexture
@ -543,10 +607,12 @@ void MVKComputeResourcesCommandEncoderState::resetImpl() {
_bufferBindings.clear();
_textureBindings.clear();
_samplerStateBindings.clear();
_auxBufferBinding.mtlBuffer = nil;
_areBufferBindingsDirty = false;
_areTextureBindingsDirty = false;
_areSamplerStateBindingsDirty = false;
_auxBufferBinding.isDirty = false;
}

View File

@ -24,6 +24,7 @@
typedef struct {
union { id<MTLTexture> mtlTexture = nil; id<MTLTexture> mtlResource; }; // aliases
uint32_t index = 0;
uint32_t swizzle = 0;
bool isDirty = true;
} MVKMTLTextureBinding;

View File

@ -100,6 +100,7 @@ public:
protected:
friend class MVKDescriptorBinding;
friend class MVKPipelineLayout;
VkResult initMetalResourceIndexOffsets(MVKShaderStageResourceBinding* pBindingIndexes,
MVKShaderStageResourceBinding* pDescSetCounts,

View File

@ -111,6 +111,11 @@ void MVKDescriptorSetLayoutBinding::bind(MVKCommandEncoder* cmdEncoder,
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: {
tb.mtlTexture = descBinding._mtlTextures[rezIdx];
if (_info.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE && tb.mtlTexture) {
tb.swizzle = ((MVKImageView*)descBinding._imageBindings[rezIdx].imageView)->getPackedSwizzle();
} else {
tb.swizzle = 0;
}
if (_applyToVertexStage) {
tb.index = mtlIdxs.vertexStage.textureIndex + rezIdx;
cmdEncoder->_graphicsResourcesState.bindVertexTexture(tb);
@ -145,6 +150,11 @@ void MVKDescriptorSetLayoutBinding::bind(MVKCommandEncoder* cmdEncoder,
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: {
tb.mtlTexture = descBinding._mtlTextures[rezIdx];
if (tb.mtlTexture) {
tb.swizzle = ((MVKImageView*)descBinding._imageBindings[rezIdx].imageView)->getPackedSwizzle();
} else {
tb.swizzle = 0;
}
sb.mtlSamplerState = descBinding._mtlSamplers[rezIdx];
if (_applyToVertexStage) {
tb.index = mtlIdxs.vertexStage.textureIndex + rezIdx;
@ -243,6 +253,11 @@ void MVKDescriptorSetLayoutBinding::push(MVKCommandEncoder* cmdEncoder,
const auto& imageInfo = get<VkDescriptorImageInfo>(pData, stride, rezIdx - dstArrayElement);
MVKImageView* imageView = (MVKImageView*)imageInfo.imageView;
tb.mtlTexture = imageView->getMTLTexture();
if (_info.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE && imageView) {
tb.swizzle = imageView->getPackedSwizzle();
} else {
tb.swizzle = 0;
}
if (_applyToVertexStage) {
tb.index = mtlIdxs.vertexStage.textureIndex + rezIdx;
cmdEncoder->_graphicsResourcesState.bindVertexTexture(tb);
@ -262,6 +277,7 @@ void MVKDescriptorSetLayoutBinding::push(MVKCommandEncoder* cmdEncoder,
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: {
auto* bufferView = get<MVKBufferView*>(pData, stride, rezIdx - dstArrayElement);
tb.mtlTexture = bufferView->getMTLTexture();
tb.swizzle = 0;
if (_applyToVertexStage) {
tb.index = mtlIdxs.vertexStage.textureIndex + rezIdx;
cmdEncoder->_graphicsResourcesState.bindVertexTexture(tb);
@ -304,6 +320,11 @@ void MVKDescriptorSetLayoutBinding::push(MVKCommandEncoder* cmdEncoder,
MVKImageView* imageView = (MVKImageView*)imageInfo.imageView;
MVKSampler* sampler = _immutableSamplers.empty() ? (MVKSampler*)imageInfo.sampler : _immutableSamplers[rezIdx];
tb.mtlTexture = imageView->getMTLTexture();
if (imageView) {
tb.swizzle = imageView->getPackedSwizzle();
} else {
tb.swizzle = 0;
}
sb.mtlSamplerState = sampler->getMTLSamplerState();
if (_applyToVertexStage) {
tb.index = mtlIdxs.vertexStage.textureIndex + rezIdx;

View File

@ -261,6 +261,9 @@ public:
/** Returns the Metal texture type of this image view. */
inline MTLTextureType getMTLTextureType() { return _mtlTextureType; }
/** Returns the packed component swizzle of this image view. */
inline uint32_t getPackedSwizzle() { return _packedSwizzle; }
/**
* Populates the texture of the specified render pass descriptor
* with the Metal texture underlying this image.
@ -283,10 +286,10 @@ public:
protected:
id<MTLTexture> newMTLTexture();
void initMTLTextureViewSupport();
MTLPixelFormat getSwizzledMTLPixelFormat(VkFormat format, VkComponentMapping components);
MTLPixelFormat getSwizzledMTLPixelFormat(VkFormat format, VkComponentMapping components, bool& useSwizzle);
bool matchesSwizzle(VkComponentMapping components, VkComponentMapping pattern);
const char* getSwizzleName(VkComponentSwizzle swizzle);
void setSwizzleFormatError(VkFormat format, VkComponentMapping components);
uint32_t packSwizzle(VkComponentMapping components);
void validateImageViewConfig(const VkImageViewCreateInfo* pCreateInfo);
MVKImage* _image;
@ -296,6 +299,7 @@ protected:
std::mutex _lock;
MTLPixelFormat _mtlPixelFormat;
MTLTextureType _mtlTextureType;
uint32_t _packedSwizzle;
bool _useMTLTextureView;
};

View File

@ -702,10 +702,12 @@ MVKImageView::MVKImageView(MVKDevice* device, const VkImageViewCreateInfo* pCrea
_subresourceRange.layerCount = _image->getLayerCount() - _subresourceRange.baseArrayLayer;
}
bool useSwizzle;
_mtlTexture = nil;
_mtlPixelFormat = getSwizzledMTLPixelFormat(pCreateInfo->format, pCreateInfo->components);
_mtlPixelFormat = getSwizzledMTLPixelFormat(pCreateInfo->format, pCreateInfo->components, useSwizzle);
_mtlTextureType = mvkMTLTextureTypeFromVkImageViewType(pCreateInfo->viewType, (_image->getSampleCount() != VK_SAMPLE_COUNT_1_BIT));
initMTLTextureViewSupport();
_packedSwizzle = useSwizzle ? packSwizzle(pCreateInfo->components) : 0;
}
// Validate whether the image view configuration can be supported
@ -731,9 +733,10 @@ void MVKImageView::validateImageViewConfig(const VkImageViewCreateInfo* pCreateI
// Metal does not support general per-texture swizzles, and so this function relies on a few coincidental
// alignments of existing MTLPixelFormats of the same structure. If swizzling is not possible for a
// particular combination of format and swizzle spec, the original MTLPixelFormat is returned.
MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format, VkComponentMapping components) {
MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format, VkComponentMapping components, bool& useSwizzle) {
MTLPixelFormat mtlPF = mtlPixelFormatFromVkFormat(format);
useSwizzle = false;
switch (mtlPF) {
case MTLPixelFormatR8Unorm:
if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_R} ) ) {
@ -741,16 +744,6 @@ MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format, VkCompon
}
break;
case MTLPixelFormatR8Snorm:
#if MVK_IOS
case MTLPixelFormatR8Unorm_sRGB:
#endif
if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_R} ) ) {
setSwizzleFormatError(format, components);
return MTLPixelFormatA8Unorm;
}
break;
case MTLPixelFormatA8Unorm:
if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_A, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_ZERO} ) ) {
return MTLPixelFormatR8Unorm;
@ -769,13 +762,6 @@ MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format, VkCompon
}
break;
case MTLPixelFormatRGBA8Snorm:
if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A} ) ) {
setSwizzleFormatError(format, components);
return MTLPixelFormatBGRA8Unorm;
}
break;
case MTLPixelFormatBGRA8Unorm:
if (matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A} ) ) {
return MTLPixelFormatRGBA8Unorm;
@ -789,16 +775,20 @@ MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format, VkCompon
break;
case MTLPixelFormatDepth32Float_Stencil8:
if (_subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT &&
matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM} ) ) {
if (_subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) {
if (!matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM} ) ) {
useSwizzle = true;
}
return MTLPixelFormatX32_Stencil8;
}
break;
#if MVK_MACOS
case MTLPixelFormatDepth24Unorm_Stencil8:
if (_subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT &&
matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM} ) ) {
if (_subresourceRange.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) {
if (!matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM, VK_COMPONENT_SWIZZLE_MAX_ENUM} ) ) {
useSwizzle = true;
}
return MTLPixelFormatX24_Stencil8;
}
break;
@ -809,7 +799,7 @@ MTLPixelFormat MVKImageView::getSwizzledMTLPixelFormat(VkFormat format, VkCompon
}
if ( !matchesSwizzle(components, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A} ) ) {
setSwizzleFormatError(format, components);
useSwizzle = true;
}
return mtlPF;
}
@ -827,17 +817,6 @@ const char* MVKImageView::getSwizzleName(VkComponentSwizzle swizzle) {
}
}
// Sets a standard swizzle format error during instance construction.
void MVKImageView::setSwizzleFormatError(VkFormat format, VkComponentMapping components) {
setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_FORMAT_NOT_SUPPORTED,
"VkImageView format %s and swizzle (%s, %s, %s, %s) does not map to a valid MTLPixelFormat.\n",
mvkVkFormatName(format),
getSwizzleName(components.r),
getSwizzleName(components.g),
getSwizzleName(components.b),
getSwizzleName(components.a)));
}
// Returns whether the swizzle components of the internal VkComponentMapping matches the
// swizzle pattern, by comparing corresponding elements of the two structures. The pattern
// supports wildcards, in that any element of pattern can be set to VK_COMPONENT_SWIZZLE_MAX_ENUM
@ -855,6 +834,12 @@ bool MVKImageView::matchesSwizzle(VkComponentMapping components, VkComponentMapp
return true;
}
// Packs a VkComponentMapping structure into a single 32-bit word.
uint32_t MVKImageView::packSwizzle(VkComponentMapping components) {
return ((components.r & 0xFF) << 0) | ((components.g & 0xFF) << 8) |
((components.b & 0xFF) << 16) | ((components.a & 0xFF) << 24);
}
// Determine whether this image view should use a Metal texture view,
// and set the _useMTLTextureView variable appropriately.
void MVKImageView::initMTLTextureViewSupport() {

View File

@ -33,6 +33,16 @@ class MVKCommandEncoder;
class MVKPipelineCache;
#pragma mark -
#pragma mark MVKShaderAuxBufferBinding
struct MVKShaderAuxBufferBinding {
uint32_t vertex;
uint32_t fragment;
uint32_t compute;
};
#pragma mark -
#pragma mark MVKPipelineLayout
@ -61,6 +71,12 @@ public:
uint32_t set,
const void* pData);
/** Returns the current auxiliary buffer bindings. */
const MVKShaderAuxBufferBinding& getAuxBufferIndex() { return _auxBufferIndex; }
/** Returns the number of textures in this layout. This is used to calculate the size of the auxiliary buffer. */
uint32_t getNumTextures() { return _numTextures; }
/** Constructs an instance for the specified device. */
MVKPipelineLayout(MVKDevice* device, const VkPipelineLayoutCreateInfo* pCreateInfo);
@ -69,6 +85,8 @@ private:
std::vector<MVKShaderResourceBinding> _dslMTLResourceIndexOffsets;
std::vector<VkPushConstantRange> _pushConstants;
MVKShaderResourceBinding _pushConstantsMTLResourceIndexOffsets;
MVKShaderAuxBufferBinding _auxBufferIndex;
uint32_t _numTextures;
};
@ -83,12 +101,17 @@ public:
/** Binds this pipeline to the specified command encoder. */
virtual void encode(MVKCommandEncoder* cmdEncoder) = 0;
/** Returns the current auxiliary buffer bindings. */
const MVKShaderAuxBufferBinding& getAuxBufferIndex() { return _auxBufferIndex; }
/** Constructs an instance for the device. layout, and parent (which may be NULL). */
MVKPipeline(MVKDevice* device, MVKPipelineCache* pipelineCache, MVKPipeline* parent) : MVKBaseDeviceObject(device),
_pipelineCache(pipelineCache) {}
protected:
MVKPipelineCache* _pipelineCache;
MVKShaderAuxBufferBinding _auxBufferIndex;
id<MTLBuffer> _auxBuffer = nil;
};
@ -107,6 +130,12 @@ public:
/** Returns whether this pipeline permits dynamic setting of the specifie state. */
bool supportsDynamicState(VkDynamicState state);
/** Returns whether or not the vertex shader needs a buffer to hold auxiliary state. */
bool needsVertexAuxBuffer() { return _needsVertexAuxBuffer; }
/** Returns whether or not the fragment shader needs a buffer to hold auxiliary state. */
bool needsFragmentAuxBuffer() { return _needsFragmentAuxBuffer; }
/** Constructs an instance for the device and parent (which may be NULL). */
MVKGraphicsPipeline(MVKDevice* device,
MVKPipelineCache* pipelineCache,
@ -138,6 +167,8 @@ protected:
bool _dynamicStateEnabled[VK_DYNAMIC_STATE_RANGE_SIZE];
bool _hasDepthStencilInfo;
bool _needsVertexAuxBuffer;
bool _needsFragmentAuxBuffer;
};
@ -152,6 +183,9 @@ public:
/** Binds this pipeline to the specified command encoder. */
void encode(MVKCommandEncoder* cmdEncoder) override;
/** Returns whether or not the compute shader needs a buffer to hold auxiliary state. */
bool needsAuxBuffer() { return _needsAuxBuffer; }
/** Constructs an instance for the device and parent (which may be NULL). */
MVKComputePipeline(MVKDevice* device,
MVKPipelineCache* pipelineCache,
@ -165,6 +199,7 @@ protected:
id<MTLComputePipelineState> _mtlPipelineState;
MTLSize _mtlThreadgroupSize;
bool _needsAuxBuffer;
};

View File

@ -107,6 +107,46 @@ void MVKPipelineLayout::populateShaderConverterContext(SPIRVToMSLConverterContex
spv::ExecutionModelGLCompute,
kPushConstDescSet,
kPushConstBinding);
// Scan the resource bindings, looking for an unused buffer index.
// FIXME: If we ever encounter a device that supports more than 31 buffer
// bindings, we'll need to update this code.
unordered_map<uint32_t, uint32_t> freeBufferMasks;
freeBufferMasks[spv::ExecutionModelVertex] = freeBufferMasks[spv::ExecutionModelFragment] = freeBufferMasks[spv::ExecutionModelGLCompute] = (1 << _device->_pMetalFeatures->maxPerStageBufferCount) - 1;
_numTextures = 0;
for (auto& binding : context.resourceBindings) {
if (binding.descriptorSet == kPushConstDescSet && binding.binding == kPushConstBinding) {
// This is the special push constant buffer.
freeBufferMasks[binding.stage] &= ~(1 << binding.mslBuffer);
continue;
}
VkDescriptorType descriptorType = _descriptorSetLayouts[binding.descriptorSet]._bindings[binding.binding]._info.descriptorType;
switch (descriptorType) {
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
// This buffer is being used.
freeBufferMasks[binding.stage] &= ~(1 << binding.mslBuffer);
break;
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
// If it results in a texture binding, we need to account for it so
// we know how big to make the auxiliary buffer.
if (binding.mslTexture + 1 > _numTextures)
_numTextures = binding.mslTexture + 1;
break;
default:
break;
}
}
// Pick the lowest index that isn't used.
_auxBufferIndex.vertex = ffs(freeBufferMasks[spv::ExecutionModelVertex]) - 1;
_auxBufferIndex.fragment = ffs(freeBufferMasks[spv::ExecutionModelFragment]) - 1;
_auxBufferIndex.compute = ffs(freeBufferMasks[spv::ExecutionModelGLCompute]) - 1;
}
MVKPipelineLayout::MVKPipelineLayout(MVKDevice* device,
@ -174,6 +214,8 @@ void MVKGraphicsPipeline::encode(MVKCommandEncoder* cmdEncoder) {
if (_device->_pFeatures->depthClamp) {
[mtlCmdEnc setDepthClipMode: _mtlDepthClipMode];
}
cmdEncoder->_graphicsResourcesState.bindAuxBuffer(_auxBuffer, _auxBufferIndex, _needsVertexAuxBuffer, _needsFragmentAuxBuffer);
}
bool MVKGraphicsPipeline::supportsDynamicState(VkDynamicState state) {
@ -295,6 +337,9 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor
SPIRVToMSLConverterContext shaderContext;
initMVKShaderConverterContext(shaderContext, pCreateInfo);
auto* mvkLayout = (MVKPipelineLayout*)pCreateInfo->layout;
_auxBufferIndex = mvkLayout->getAuxBufferIndex();
uint32_t auxBufferSize = sizeof(uint32_t) * mvkLayout->getNumTextures();
// Retrieve the render subpass for which this pipeline is being constructed
MVKRenderPass* mvkRendPass = (MVKRenderPass*)pCreateInfo->renderPass;
@ -302,6 +347,8 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor
MTLRenderPipelineDescriptor* plDesc = [[MTLRenderPipelineDescriptor new] autorelease];
uint32_t vbCnt = pCreateInfo->pVertexInputState->vertexBindingDescriptionCount;
// Add shader stages
for (uint32_t i = 0; i < pCreateInfo->stageCount; i++) {
const VkPipelineShaderStageCreateInfo* pSS = &pCreateInfo->pStages[i];
@ -312,6 +359,7 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor
// Vertex shader
if (mvkAreFlagsEnabled(pSS->stage, VK_SHADER_STAGE_VERTEX_BIT)) {
shaderContext.options.entryPointStage = spv::ExecutionModelVertex;
shaderContext.options.auxBufferIndex = _auxBufferIndex.vertex;
id<MTLFunction> mtlFunction = mvkShdrMod->getMTLFunction(&shaderContext, pSS->pSpecializationInfo, _pipelineCache).mtlFunction;
if ( !mtlFunction ) {
setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "Vertex shader function could not be compiled into pipeline. See previous error."));
@ -319,19 +367,36 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor
}
plDesc.vertexFunction = mtlFunction;
plDesc.rasterizationEnabled = !shaderContext.options.isRasterizationDisabled;
_needsVertexAuxBuffer = shaderContext.options.needsAuxBuffer;
// If we need the auxiliary buffer and there's no place to put it,
// we're in serious trouble.
if (_needsVertexAuxBuffer && (_auxBufferIndex.vertex == ~0u || _auxBufferIndex.vertex >= _device->_pMetalFeatures->maxPerStageBufferCount - vbCnt) ) {
setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "Vertex shader requires auxiliary buffer, but there is no free slot to pass it."));
return nil;
}
}
// Fragment shader
if (mvkAreFlagsEnabled(pSS->stage, VK_SHADER_STAGE_FRAGMENT_BIT)) {
shaderContext.options.entryPointStage = spv::ExecutionModelFragment;
shaderContext.options.auxBufferIndex = _auxBufferIndex.fragment;
id<MTLFunction> mtlFunction = mvkShdrMod->getMTLFunction(&shaderContext, pSS->pSpecializationInfo, _pipelineCache).mtlFunction;
if ( !mtlFunction ) {
setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "Fragment shader function could not be compiled into pipeline. See previous error."));
}
plDesc.fragmentFunction = mtlFunction;
_needsFragmentAuxBuffer = shaderContext.options.needsAuxBuffer;
if (_needsFragmentAuxBuffer && _auxBufferIndex.fragment == ~0u) {
setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "Fragment shader requires auxiliary buffer, but there is no free slot to pass it."));
return nil;
}
}
}
if (_needsVertexAuxBuffer || _needsFragmentAuxBuffer) {
_auxBuffer = [_device->getMTLDevice() newBufferWithLength: auxBufferSize options: MTLResourceStorageModeShared];
}
// Vertex attributes
uint32_t vaCnt = pCreateInfo->pVertexInputState->vertexAttributeDescriptionCount;
for (uint32_t i = 0; i < vaCnt; i++) {
@ -345,7 +410,6 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::getMTLRenderPipelineDescriptor
}
// Vertex buffer bindings
uint32_t vbCnt = pCreateInfo->pVertexInputState->vertexBindingDescriptionCount;
for (uint32_t i = 0; i < vbCnt; i++) {
const VkVertexInputBindingDescription* pVKVB = &pCreateInfo->pVertexInputState->pVertexBindingDescriptions[i];
uint32_t vbIdx = _device->getMetalBufferIndexForVertexAttributeBinding(pVKVB->binding);
@ -481,6 +545,7 @@ MVKGraphicsPipeline::~MVKGraphicsPipeline() {
void MVKComputePipeline::encode(MVKCommandEncoder* cmdEncoder) {
[cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch) setComputePipelineState: _mtlPipelineState];
cmdEncoder->_mtlThreadgroupSize = _mtlThreadgroupSize;
cmdEncoder->_computeResourcesState.bindAuxBuffer(_auxBuffer, _auxBufferIndex);
}
MVKComputePipeline::MVKComputePipeline(MVKDevice* device,
@ -499,6 +564,10 @@ MVKComputePipeline::MVKComputePipeline(MVKDevice* device,
} else {
setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "Compute shader function could not be compiled into pipeline. See previous error."));
}
if (_needsAuxBuffer && _auxBufferIndex.compute == ~0u) {
setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "Compute shader requires auxiliary buffer, but there is no free slot to pass it."));
}
}
// Returns a MTLFunction to use when creating the MTLComputePipelineState.
@ -514,9 +583,17 @@ MVKMTLFunction MVKComputePipeline::getMTLFunction(const VkComputePipelineCreateI
MVKPipelineLayout* layout = (MVKPipelineLayout*)pCreateInfo->layout;
layout->populateShaderConverterContext(shaderContext);
_auxBufferIndex = layout->getAuxBufferIndex();
uint32_t auxBufferSize = sizeof(uint32_t) * layout->getNumTextures();
shaderContext.options.auxBufferIndex = _auxBufferIndex.compute;
MVKShaderModule* mvkShdrMod = (MVKShaderModule*)pSS->module;
return mvkShdrMod->getMTLFunction(&shaderContext, pSS->pSpecializationInfo, _pipelineCache);
auto func = mvkShdrMod->getMTLFunction(&shaderContext, pSS->pSpecializationInfo, _pipelineCache);
_needsAuxBuffer = shaderContext.options.needsAuxBuffer;
if (_needsAuxBuffer) {
_auxBuffer = [_device->getMTLDevice() newBufferWithLength: auxBufferSize options: MTLResourceStorageModeShared];
}
return func;
}

View File

@ -201,9 +201,11 @@ MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverter::convert(SPIRVToMSLConverterContext&
mslOpts.msl_version = context.options.mslVersion;
mslOpts.texel_buffer_texture_width = context.options.texelBufferTextureWidth;
mslOpts.aux_buffer_index = context.options.auxBufferIndex;
mslOpts.enable_point_size_builtin = context.options.isRenderingPoints;
mslOpts.disable_rasterization = context.options.isRasterizationDisabled;
mslOpts.resolve_specialized_array_lengths = true;
mslOpts.swizzle_texture_samples = true;
pMSLCompiler->set_msl_options(mslOpts);
auto scOpts = pMSLCompiler->get_common_options();
@ -229,6 +231,7 @@ MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverter::convert(SPIRVToMSLConverterContext&
// Populate content extracted from the SPRI-V compiler.
populateEntryPoint(_entryPoint, pMSLCompiler, context.options);
context.options.isRasterizationDisabled = pMSLCompiler && pMSLCompiler->get_is_rasterization_disabled();
context.options.needsAuxBuffer = pMSLCompiler && pMSLCompiler->needs_aux_buffer();
delete pMSLCompiler;
// Copy whether the vertex attributes and resource bindings are used by the shader

View File

@ -37,10 +37,12 @@ namespace mvk {
uint32_t mslVersion = makeMSLVersion(2);
uint32_t texelBufferTextureWidth = 4096;
uint32_t auxBufferIndex = 0;
bool shouldFlipVertexY = true;
bool isRenderingPoints = false;
bool isRasterizationDisabled = false;
bool needsAuxBuffer = false;
/**
* Returns whether the specified options match this one.