diff --git a/ExternalRevisions/SPIRV-Cross_repo_revision b/ExternalRevisions/SPIRV-Cross_repo_revision index 91450c61..6ab048fd 100644 --- a/ExternalRevisions/SPIRV-Cross_repo_revision +++ b/ExternalRevisions/SPIRV-Cross_repo_revision @@ -1 +1 @@ -c9210427b9ab547d41f1af804dedae581b382965 +cc5c0204d8bcdadbb4add03e53346df98bf27fa4 diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h index 72de1830..d34b0193 100644 --- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h +++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h @@ -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) diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h index 8e54826f..bd23b840 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h +++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h @@ -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 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 buffer, const MVKShaderAuxBufferBinding& binding, bool needVertexAuxBuffer, bool needFragmentAuxBuffer); + #pragma mark Construction @@ -442,6 +457,8 @@ protected: std::vector _fragmentTextureBindings; std::vector _vertexSamplerStateBindings; std::vector _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 buffer, const MVKShaderAuxBufferBinding& binding); #pragma mark Construction @@ -483,6 +502,7 @@ protected: std::vector _bufferBindings; std::vector _textureBindings; std::vector _samplerStateBindings; + MVKMTLBufferBinding _auxBufferBinding; bool _areBufferBindingsDirty = false; bool _areTextureBindingsDirty = false; diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm index 4c560c1b..a8ae9084 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm +++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm @@ -425,6 +425,15 @@ void MVKGraphicsResourcesCommandEncoderState::bindFragmentSamplerState(const MVK bind(binding, _fragmentSamplerStateBindings, _areFragmentSamplerStateBindingsDirty); } +void MVKGraphicsResourcesCommandEncoderState::bindAuxBuffer(id 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(_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(_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 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(_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(_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; } diff --git a/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h b/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h index 140f12ee..ddb615a4 100644 --- a/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h +++ b/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h @@ -24,6 +24,7 @@ typedef struct { union { id mtlTexture = nil; id mtlResource; }; // aliases uint32_t index = 0; + uint32_t swizzle = 0; bool isDirty = true; } MVKMTLTextureBinding; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h index d2c0f793..71e9f1f5 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h @@ -100,6 +100,7 @@ public: protected: friend class MVKDescriptorBinding; + friend class MVKPipelineLayout; VkResult initMetalResourceIndexOffsets(MVKShaderStageResourceBinding* pBindingIndexes, MVKShaderStageResourceBinding* pDescSetCounts, diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm index a8367e22..909b665c 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm @@ -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(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(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; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h index c68cc774..9c3c98b1 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h @@ -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 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; }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index 8d936a5d..3e0703b2 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -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() { diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h index 951b1530..d6e05573 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h @@ -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 _dslMTLResourceIndexOffsets; std::vector _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 _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 _mtlPipelineState; MTLSize _mtlThreadgroupSize; + bool _needsAuxBuffer; }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm index eeb209d9..c35e66c2 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm @@ -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 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 = 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 = 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; } diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp index 9c131bfa..2b8040a1 100644 --- a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp @@ -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 diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h index 25b6c3ef..751e752d 100644 --- a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h +++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h @@ -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.