diff --git a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj index 5cbd12723..2d7bb8050 100644 --- a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj +++ b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj @@ -157,6 +157,10 @@ A95B7D6A1D3EE486003183D3 /* MVKCommandEncoderState.h in Headers */ = {isa = PBXBuildFile; fileRef = A95B7D671D3EE486003183D3 /* MVKCommandEncoderState.h */; }; A95B7D6B1D3EE486003183D3 /* MVKCommandEncoderState.mm in Sources */ = {isa = PBXBuildFile; fileRef = A95B7D681D3EE486003183D3 /* MVKCommandEncoderState.mm */; }; A95B7D6C1D3EE486003183D3 /* MVKCommandEncoderState.mm in Sources */ = {isa = PBXBuildFile; fileRef = A95B7D681D3EE486003183D3 /* MVKCommandEncoderState.mm */; }; + A966A5DF23C535D000BBF9B4 /* MVKDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = A966A5DC23C535D000BBF9B4 /* MVKDescriptor.h */; }; + A966A5E023C535D000BBF9B4 /* MVKDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = A966A5DC23C535D000BBF9B4 /* MVKDescriptor.h */; }; + A966A5E123C535D000BBF9B4 /* MVKDescriptor.mm in Sources */ = {isa = PBXBuildFile; fileRef = A966A5DE23C535D000BBF9B4 /* MVKDescriptor.mm */; }; + A966A5E223C535D000BBF9B4 /* MVKDescriptor.mm in Sources */ = {isa = PBXBuildFile; fileRef = A966A5DE23C535D000BBF9B4 /* MVKDescriptor.mm */; }; A981494D1FB6A3F7005F00B4 /* MVKBaseObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = A98149411FB6A3F7005F00B4 /* MVKBaseObject.mm */; }; A981494E1FB6A3F7005F00B4 /* MVKBaseObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = A98149411FB6A3F7005F00B4 /* MVKBaseObject.mm */; }; A981494F1FB6A3F7005F00B4 /* MVKBaseObject.h in Headers */ = {isa = PBXBuildFile; fileRef = A98149421FB6A3F7005F00B4 /* MVKBaseObject.h */; }; @@ -373,6 +377,8 @@ A95870F71C90D29F009EB096 /* MVKCommandResourceFactory.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKCommandResourceFactory.mm; sourceTree = ""; }; A95B7D671D3EE486003183D3 /* MVKCommandEncoderState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKCommandEncoderState.h; sourceTree = ""; }; A95B7D681D3EE486003183D3 /* MVKCommandEncoderState.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKCommandEncoderState.mm; sourceTree = ""; }; + A966A5DC23C535D000BBF9B4 /* MVKDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKDescriptor.h; sourceTree = ""; }; + A966A5DE23C535D000BBF9B4 /* MVKDescriptor.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKDescriptor.mm; sourceTree = ""; }; A98149411FB6A3F7005F00B4 /* MVKBaseObject.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKBaseObject.mm; sourceTree = ""; }; A98149421FB6A3F7005F00B4 /* MVKBaseObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKBaseObject.h; sourceTree = ""; }; A98149431FB6A3F7005F00B4 /* MVKEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKEnvironment.h; sourceTree = ""; }; @@ -483,6 +489,8 @@ children = ( A94FB77F1C7DFB4800632CA3 /* MVKBuffer.h */, A94FB7801C7DFB4800632CA3 /* MVKBuffer.mm */, + A966A5DC23C535D000BBF9B4 /* MVKDescriptor.h */, + A966A5DE23C535D000BBF9B4 /* MVKDescriptor.mm */, A94FB7811C7DFB4800632CA3 /* MVKDescriptorSet.h */, A94FB7821C7DFB4800632CA3 /* MVKDescriptorSet.mm */, A94FB7831C7DFB4800632CA3 /* MVKDevice.h */, @@ -695,6 +703,7 @@ A94FB7F01C7DFB4800632CA3 /* MVKImage.h in Headers */, 4553AEFD2251617100E8EBCD /* MVKBlockObserver.h in Headers */, A94FB7B81C7DFB4800632CA3 /* MVKCmdTransfer.h in Headers */, + A966A5DF23C535D000BBF9B4 /* MVKDescriptor.h in Headers */, A94FB7C81C7DFB4800632CA3 /* MVKCmdDraw.h in Headers */, A94FB7D01C7DFB4800632CA3 /* MVKCommandBuffer.h in Headers */, A9E53DF32100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.h in Headers */, @@ -763,6 +772,7 @@ A94FB7F11C7DFB4800632CA3 /* MVKImage.h in Headers */, 4553AEFE2251617100E8EBCD /* MVKBlockObserver.h in Headers */, A94FB7B91C7DFB4800632CA3 /* MVKCmdTransfer.h in Headers */, + A966A5E023C535D000BBF9B4 /* MVKDescriptor.h in Headers */, A94FB7C91C7DFB4800632CA3 /* MVKCmdDraw.h in Headers */, A94FB7D11C7DFB4800632CA3 /* MVKCommandBuffer.h in Headers */, A9E53DF42100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.h in Headers */, @@ -1004,6 +1014,7 @@ A98149551FB6A3F7005F00B4 /* MVKFoundation.cpp in Sources */, A94FB7E61C7DFB4800632CA3 /* MVKDevice.mm in Sources */, A9E53DF52100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.m in Sources */, + A966A5E123C535D000BBF9B4 /* MVKDescriptor.mm in Sources */, A94FB7FA1C7DFB4800632CA3 /* MVKPipeline.mm in Sources */, A94FB8021C7DFB4800632CA3 /* MVKQueue.mm in Sources */, A9E53DD72100B197002781DD /* MTLSamplerDescriptor+MoltenVK.m in Sources */, @@ -1060,6 +1071,7 @@ A98149561FB6A3F7005F00B4 /* MVKFoundation.cpp in Sources */, A94FB7E71C7DFB4800632CA3 /* MVKDevice.mm in Sources */, A9E53DF62100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.m in Sources */, + A966A5E223C535D000BBF9B4 /* MVKDescriptor.mm in Sources */, A94FB7FB1C7DFB4800632CA3 /* MVKPipeline.mm in Sources */, A94FB8031C7DFB4800632CA3 /* MVKQueue.mm in Sources */, A9E53DD82100B197002781DD /* MTLSamplerDescriptor+MoltenVK.m in Sources */, diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h new file mode 100644 index 000000000..d4ef426fc --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h @@ -0,0 +1,555 @@ +/* + * MVKDescriptor.h + * + * Copyright (c) 2015-2020 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MVKImage.h" +#include "MVKVector.h" +#include + +class MVKDescriptorSet; +class MVKDescriptorSetLayout; +class MVKCommandEncoder; + + +#pragma mark MVKShaderStageResourceBinding + +/** Indicates the Metal resource indexes used by a single shader stage in a descriptor. */ +typedef struct MVKShaderStageResourceBinding { + uint32_t bufferIndex = 0; + uint32_t textureIndex = 0; + uint32_t samplerIndex = 0; + + MVKShaderStageResourceBinding operator+ (const MVKShaderStageResourceBinding& rhs); + MVKShaderStageResourceBinding& operator+= (const MVKShaderStageResourceBinding& rhs); + +} MVKShaderStageResourceBinding; + + +#pragma mark MVKShaderResourceBinding + +/** Indicates the Metal resource indexes used by each shader stage in a descriptor. */ +typedef struct MVKShaderResourceBinding { + MVKShaderStageResourceBinding stages[kMVKShaderStageMax]; + + uint32_t getMaxBufferIndex(); + uint32_t getMaxTextureIndex(); + uint32_t getMaxSamplerIndex(); + + MVKShaderResourceBinding operator+ (const MVKShaderResourceBinding& rhs); + MVKShaderResourceBinding& operator+= (const MVKShaderResourceBinding& rhs); + +} MVKShaderResourceBinding; + + +#pragma mark - +#pragma mark MVKDescriptorSetLayoutBinding + +/** Represents a Vulkan descriptor set layout binding. */ +class MVKDescriptorSetLayoutBinding : public MVKBaseDeviceObject { + +public: + + /** Returns the Vulkan API opaque object controlling this object. */ + MVKVulkanAPIObject* getVulkanAPIObject() override; + + /** Returns the binding number of this layout. */ + inline uint32_t getBinding() { return _info.binding; } + + /** Returns the number of descriptors in this layout. */ + inline uint32_t getDescriptorCount() { return _info.descriptorCount; } + + /** Returns the descriptor type of this layout. */ + inline VkDescriptorType getDescriptorType() { return _info.descriptorType; } + + /** Returns the immutable sampler at the index, or nullptr if immutable samplers are not used. */ + MVKSampler* getImmutableSampler(uint32_t index); + + /** + * Encodes the descriptors in the descriptor set that are specified by this layout, + * starting with the descriptor at the index, on the the command encoder. + * Returns the number of descriptors that were encoded. + */ + uint32_t bind(MVKCommandEncoder* cmdEncoder, + MVKDescriptorSet* descSet, + uint32_t descStartIndex, + MVKShaderResourceBinding& dslMTLRezIdxOffsets, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex); + + /** Encodes this binding layout and the specified descriptor on the specified command encoder immediately. */ + void push(MVKCommandEncoder* cmdEncoder, + uint32_t& dstArrayElement, + uint32_t& descriptorCount, + uint32_t& descriptorsPushed, + VkDescriptorType descriptorType, + size_t stride, + const void* pData, + MVKShaderResourceBinding& dslMTLRezIdxOffsets); + + /** Populates the specified shader converter context, at the specified descriptor set binding. */ + void populateShaderConverterContext(mvk::SPIRVToMSLConversionConfiguration& context, + MVKShaderResourceBinding& dslMTLRezIdxOffsets, + uint32_t dslIndex); + + MVKDescriptorSetLayoutBinding(MVKDevice* device, + MVKDescriptorSetLayout* layout, + const VkDescriptorSetLayoutBinding* pBinding); + + MVKDescriptorSetLayoutBinding(const MVKDescriptorSetLayoutBinding& binding); + + ~MVKDescriptorSetLayoutBinding() override; + +protected: + void initMetalResourceIndexOffsets(MVKShaderStageResourceBinding* pBindingIndexes, + MVKShaderStageResourceBinding* pDescSetCounts, + const VkDescriptorSetLayoutBinding* pBinding); + bool validate(MVKSampler* mvkSampler); + + MVKDescriptorSetLayout* _layout; + VkDescriptorSetLayoutBinding _info; + std::vector _immutableSamplers; + MVKShaderResourceBinding _mtlResourceIndexOffsets; + bool _applyToStage[kMVKShaderStageMax]; +}; + + +#pragma mark - +#pragma mark MVKDescriptor + +/** Represents a Vulkan descriptor. */ +class MVKDescriptor : public MVKBaseObject { + +public: + + /** Returns the Vulkan API opaque object controlling this object. */ + MVKVulkanAPIObject* getVulkanAPIObject() override { return nullptr; }; + + virtual VkDescriptorType getDescriptorType() = 0; + + /** Encodes this descriptor (based on its layout binding index) on the the command encoder. */ + virtual void bind(MVKCommandEncoder* cmdEncoder, + VkDescriptorType descriptorType, + uint32_t descriptorIndex, + bool stages[], + MVKShaderResourceBinding& mtlIndexes, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) = 0; + + /** + * Updates the internal binding from the specified content. The format of the content depends + * on the descriptor type, and is extracted from pData at the location given by srcIndex * stride. + */ + virtual void write(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t srcIndex, + size_t stride, + const void* pData) = 0; + + /** + * Updates the specified content arrays from the internal binding. + * + * Depending on the descriptor type, the binding content is placed into one of the + * specified pImageInfo, pBufferInfo, or pTexelBufferView arrays, and the other + * arrays are ignored (and may be a null pointer). + * + * The dstIndex parameter indicates the index of the initial descriptor element + * at which to start writing. + */ + virtual void read(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t dstIndex, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* inlineUniformBlock) = 0; + + /** Sets the binding layout. */ + virtual void setLayout(MVKDescriptorSetLayoutBinding* dslBinding, uint32_t index) {} + + /** Resets any internal content. */ + virtual void reset() {} + + ~MVKDescriptor() { reset(); } + +}; + + +#pragma mark - +#pragma mark MVKBufferDescriptor + +/** Represents a Vulkan descriptor tracking a buffer. */ +class MVKBufferDescriptor : public MVKDescriptor { + +public: + void bind(MVKCommandEncoder* cmdEncoder, + VkDescriptorType descriptorType, + uint32_t descriptorIndex, + bool stages[], + MVKShaderResourceBinding& mtlIndexes, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) override; + + void write(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t srcIndex, + size_t stride, + const void* pData) override; + + void read(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t dstIndex, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* inlineUniformBlock) override; + + void reset() override; + + ~MVKBufferDescriptor() { reset(); } + +protected: + MVKBuffer* _mvkBuffer = nullptr; + VkDeviceSize _buffOffset = 0; + VkDeviceSize _buffRange = 0; +}; + + +#pragma mark - +#pragma mark MVKUniformBufferDescriptor + +class MVKUniformBufferDescriptor : public MVKBufferDescriptor { +public: + VkDescriptorType getDescriptorType() override { return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; } +}; + + +#pragma mark - +#pragma mark MVKUniformBufferDynamicDescriptor + +class MVKUniformBufferDynamicDescriptor : public MVKBufferDescriptor { +public: + VkDescriptorType getDescriptorType() override { return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; } +}; + + +#pragma mark - +#pragma mark MVKStorageBufferDescriptor + +class MVKStorageBufferDescriptor : public MVKBufferDescriptor { +public: + VkDescriptorType getDescriptorType() override { return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; } +}; + + +#pragma mark - +#pragma mark MVKStorageBufferDynamicDescriptor + +class MVKStorageBufferDynamicDescriptor : public MVKBufferDescriptor { +public: + VkDescriptorType getDescriptorType() override { return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; } +}; + + +#pragma mark - +#pragma mark MVKInlineUniformBlockDescriptor + +/** Represents a Vulkan descriptor tracking an inline block of uniform data. */ +class MVKInlineUniformBlockDescriptor : public MVKDescriptor { + +public: + VkDescriptorType getDescriptorType() override { return VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT; } + + void bind(MVKCommandEncoder* cmdEncoder, + VkDescriptorType descriptorType, + uint32_t descriptorIndex, + bool stages[], + MVKShaderResourceBinding& mtlIndexes, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) override; + + void write(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t srcIndex, + size_t stride, + const void* pData) override; + + void read(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t dstIndex, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* inlineUniformBlock) override; + + void reset() override; + + ~MVKInlineUniformBlockDescriptor() { reset(); } + +protected: + id _mtlBuffer = nil; + uint32_t _dataSize = 0; +}; + + +#pragma mark - +#pragma mark MVKImageDescriptor + +/** Represents a Vulkan descriptor tracking an image. */ +class MVKImageDescriptor : public MVKDescriptor { + +public: + void bind(MVKCommandEncoder* cmdEncoder, + VkDescriptorType descriptorType, + uint32_t descriptorIndex, + bool stages[], + MVKShaderResourceBinding& mtlIndexes, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) override; + + void write(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t srcIndex, + size_t stride, + const void* pData) override; + + void read(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t dstIndex, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* inlineUniformBlock) override; + + void reset() override; + + ~MVKImageDescriptor() { reset(); } + +protected: + MVKImageView* _mvkImageView = nullptr; + VkImageLayout _imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; +}; + + +#pragma mark - +#pragma mark MVKSampledImageDescriptor + +class MVKSampledImageDescriptor : public MVKImageDescriptor { +public: + VkDescriptorType getDescriptorType() override { return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; } +}; + + +#pragma mark - +#pragma mark MVKStorageImageDescriptor + +class MVKStorageImageDescriptor : public MVKImageDescriptor { +public: + VkDescriptorType getDescriptorType() override { return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; } +}; + + +#pragma mark - +#pragma mark MVKInputAttachmentDescriptor + +class MVKInputAttachmentDescriptor : public MVKImageDescriptor { +public: + VkDescriptorType getDescriptorType() override { return VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; } +}; + + +#pragma mark - +#pragma mark MVKSamplerDescriptorMixin + +/** + * This mixin class adds the ability for a descriptor to track a sampler. + * + * As a mixin, this class should only be used as a component of multiple inheritance. + * Any class that inherits from this class should also inherit from MVKDescriptor. + * This requirement is to avoid the diamond problem of multiple inheritance. + */ +class MVKSamplerDescriptorMixin { + +protected: + void bind(MVKCommandEncoder* cmdEncoder, + VkDescriptorType descriptorType, + uint32_t descriptorIndex, + bool stages[], + MVKShaderResourceBinding& mtlIndexes, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex); + + void write(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t srcIndex, + size_t stride, + const void* pData); + + void read(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t dstIndex, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* inlineUniformBlock); + + void setLayout(MVKDescriptorSetLayoutBinding* dslBinding, uint32_t index); + + void reset(); + + ~MVKSamplerDescriptorMixin() { reset(); } + + MVKSampler* _mvkSampler = nullptr; + bool _hasDynamicSampler = true; +}; + + +#pragma mark - +#pragma mark MVKSamplerDescriptor + +/** Represents a Vulkan descriptor tracking a sampler. */ +class MVKSamplerDescriptor : public MVKDescriptor, public MVKSamplerDescriptorMixin { + +public: + VkDescriptorType getDescriptorType() override { return VK_DESCRIPTOR_TYPE_SAMPLER; } + + void bind(MVKCommandEncoder* cmdEncoder, + VkDescriptorType descriptorType, + uint32_t descriptorIndex, + bool stages[], + MVKShaderResourceBinding& mtlIndexes, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) override; + + void write(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t srcIndex, + size_t stride, + const void* pData) override; + + void read(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t dstIndex, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* inlineUniformBlock) override; + + void setLayout(MVKDescriptorSetLayoutBinding* dslBinding, uint32_t index) override; + + void reset() override; + + ~MVKSamplerDescriptor() { reset(); } + +}; + + +#pragma mark - +#pragma mark MVKCombinedImageSamplerDescriptor + +/** Represents a Vulkan descriptor tracking a combined image and sampler. */ +class MVKCombinedImageSamplerDescriptor : public MVKImageDescriptor, public MVKSamplerDescriptorMixin { + +public: + VkDescriptorType getDescriptorType() override { return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; } + + void bind(MVKCommandEncoder* cmdEncoder, + VkDescriptorType descriptorType, + uint32_t descriptorIndex, + bool stages[], + MVKShaderResourceBinding& mtlIndexes, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) override; + + void write(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t srcIndex, + size_t stride, + const void* pData) override; + + void read(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t dstIndex, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* inlineUniformBlock) override; + + void setLayout(MVKDescriptorSetLayoutBinding* dslBinding, uint32_t index) override; + + void reset() override; + + ~MVKCombinedImageSamplerDescriptor() { reset(); } + +}; + + +#pragma mark - +#pragma mark MVKTexelBufferDescriptor + +/** Represents a Vulkan descriptor tracking a texel buffer. */ +class MVKTexelBufferDescriptor : public MVKDescriptor { + +public: + void bind(MVKCommandEncoder* cmdEncoder, + VkDescriptorType descriptorType, + uint32_t descriptorIndex, + bool stages[], + MVKShaderResourceBinding& mtlIndexes, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) override; + + void write(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t srcIndex, + size_t stride, + const void* pData) override; + + void read(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t dstIndex, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* inlineUniformBlock) override; + + void reset() override; + + ~MVKTexelBufferDescriptor() { reset(); } + +protected: + MVKBufferView* _mvkBufferView = nullptr; +}; + + +#pragma mark - +#pragma mark MVKUniformTexelBufferDescriptor + +class MVKUniformTexelBufferDescriptor : public MVKTexelBufferDescriptor { +public: + VkDescriptorType getDescriptorType() override { return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; } +}; + + +#pragma mark - +#pragma mark MVKStorageTexelBufferDescriptor + +class MVKStorageTexelBufferDescriptor : public MVKTexelBufferDescriptor { +public: + VkDescriptorType getDescriptorType() override { return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; } +}; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm new file mode 100644 index 000000000..74d0b4aa7 --- /dev/null +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm @@ -0,0 +1,1088 @@ +/* + * MVKDescriptor.mm + * + * Copyright (c) 2015-2020 The Brenwill Workshop Ltd. (http://www.brenwill.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MVKDescriptor.h" +#include "MVKDescriptorSet.h" +#include "MVKBuffer.h" + + +#pragma mark MVKShaderStageResourceBinding + +MVKShaderStageResourceBinding MVKShaderStageResourceBinding::operator+ (const MVKShaderStageResourceBinding& rhs) { + MVKShaderStageResourceBinding rslt; + rslt.bufferIndex = this->bufferIndex + rhs.bufferIndex; + rslt.textureIndex = this->textureIndex + rhs.textureIndex; + rslt.samplerIndex = this->samplerIndex + rhs.samplerIndex; + return rslt; +} + +MVKShaderStageResourceBinding& MVKShaderStageResourceBinding::operator+= (const MVKShaderStageResourceBinding& rhs) { + this->bufferIndex += rhs.bufferIndex; + this->textureIndex += rhs.textureIndex; + this->samplerIndex += rhs.samplerIndex; + return *this; +} + + +#pragma mark MVKShaderResourceBinding + +uint32_t MVKShaderResourceBinding::getMaxBufferIndex() { + return std::max({stages[kMVKShaderStageVertex].bufferIndex, stages[kMVKShaderStageTessCtl].bufferIndex, stages[kMVKShaderStageTessEval].bufferIndex, stages[kMVKShaderStageFragment].bufferIndex, stages[kMVKShaderStageCompute].bufferIndex}); +} + +uint32_t MVKShaderResourceBinding::getMaxTextureIndex() { + return std::max({stages[kMVKShaderStageVertex].textureIndex, stages[kMVKShaderStageTessCtl].textureIndex, stages[kMVKShaderStageTessEval].textureIndex, stages[kMVKShaderStageFragment].textureIndex, stages[kMVKShaderStageCompute].textureIndex}); +} + +uint32_t MVKShaderResourceBinding::getMaxSamplerIndex() { + return std::max({stages[kMVKShaderStageVertex].samplerIndex, stages[kMVKShaderStageTessCtl].samplerIndex, stages[kMVKShaderStageTessEval].samplerIndex, stages[kMVKShaderStageFragment].samplerIndex, stages[kMVKShaderStageCompute].samplerIndex}); +} + +MVKShaderResourceBinding MVKShaderResourceBinding::operator+ (const MVKShaderResourceBinding& rhs) { + MVKShaderResourceBinding rslt; + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + rslt.stages[i] = this->stages[i] + rhs.stages[i]; + } + return rslt; +} + +MVKShaderResourceBinding& MVKShaderResourceBinding::operator+= (const MVKShaderResourceBinding& rhs) { + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + this->stages[i] += rhs.stages[i]; + } + return *this; +} + + +#pragma mark - +#pragma mark MVKDescriptorSetLayoutBinding + +MVKVulkanAPIObject* MVKDescriptorSetLayoutBinding::getVulkanAPIObject() { return _layout; }; + +MVKSampler* MVKDescriptorSetLayoutBinding::getImmutableSampler(uint32_t index) { + return (index < _immutableSamplers.size()) ? _immutableSamplers[index] : nullptr; +} + +// A null cmdEncoder can be passed to perform a validation pass +uint32_t MVKDescriptorSetLayoutBinding::bind(MVKCommandEncoder* cmdEncoder, + MVKDescriptorSet* descSet, + uint32_t descStartIndex, + MVKShaderResourceBinding& dslMTLRezIdxOffsets, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) { + + // Establish the resource indices to use, by combining the offsets of the DSL and this DSL binding. + MVKShaderResourceBinding mtlIdxs = _mtlResourceIndexOffsets + dslMTLRezIdxOffsets; + + uint32_t descCnt = _info.descriptorCount; + for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) { + MVKDescriptor* mvkDesc = descSet->getDescriptor(descStartIndex + descIdx); + mvkDesc->bind(cmdEncoder, _info.descriptorType, descIdx, _applyToStage, + mtlIdxs, dynamicOffsets, pDynamicOffsetIndex); + } + return descCnt; +} + +template +static const T& get(const void* pData, size_t stride, uint32_t index) { + return *(T*)((const char*)pData + stride * index); +} + +// A null cmdEncoder can be passed to perform a validation pass +void MVKDescriptorSetLayoutBinding::push(MVKCommandEncoder* cmdEncoder, + uint32_t& dstArrayElement, + uint32_t& descriptorCount, + uint32_t& descriptorsPushed, + VkDescriptorType descriptorType, + size_t stride, + const void* pData, + MVKShaderResourceBinding& dslMTLRezIdxOffsets) { + MVKMTLBufferBinding bb; + MVKMTLTextureBinding tb; + MVKMTLSamplerStateBinding sb; + + if (dstArrayElement >= _info.descriptorCount) { + dstArrayElement -= _info.descriptorCount; + return; + } + + if (descriptorType != _info.descriptorType) { + dstArrayElement = 0; + if (_info.descriptorCount > descriptorCount) + descriptorCount = 0; + else { + descriptorCount -= _info.descriptorCount; + descriptorsPushed = _info.descriptorCount; + } + return; + } + + // Establish the resource indices to use, by combining the offsets of the DSL and this DSL binding. + MVKShaderResourceBinding mtlIdxs = _mtlResourceIndexOffsets + dslMTLRezIdxOffsets; + + for (uint32_t rezIdx = dstArrayElement; + rezIdx < _info.descriptorCount && rezIdx - dstArrayElement < descriptorCount; + rezIdx++) { + switch (_info.descriptorType) { + + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: { + const auto& bufferInfo = get(pData, stride, rezIdx - dstArrayElement); + MVKBuffer* buffer = (MVKBuffer*)bufferInfo.buffer; + bb.mtlBuffer = buffer->getMTLBuffer(); + bb.offset = buffer->getMTLBufferOffset() + bufferInfo.offset; + bb.size = (uint32_t)buffer->getByteCount(); + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (_applyToStage[i]) { + bb.index = mtlIdxs.stages[i].bufferIndex + rezIdx; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } + } + } + } + break; + } + + case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: { + const auto& inlineUniformBlock = get(pData, stride, rezIdx - dstArrayElement); + bb.mtlBytes = inlineUniformBlock.pData; + bb.size = inlineUniformBlock.dataSize; + bb.isInline = true; + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (_applyToStage[i]) { + bb.index = mtlIdxs.stages[i].bufferIndex + rezIdx; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } + } + } + } + break; + } + + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { + 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; + } + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (_applyToStage[i]) { + tb.index = mtlIdxs.stages[i].textureIndex + rezIdx; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } + } + } + } + break; + } + + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { + auto* bufferView = get(pData, stride, rezIdx - dstArrayElement); + tb.mtlTexture = bufferView->getMTLTexture(); + tb.swizzle = 0; + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (_applyToStage[i]) { + tb.index = mtlIdxs.stages[i].textureIndex + rezIdx; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } + } + } + } + break; + } + + case VK_DESCRIPTOR_TYPE_SAMPLER: { + MVKSampler* sampler; + if (_immutableSamplers.empty()) { + sampler = (MVKSampler*)get(pData, stride, rezIdx - dstArrayElement).sampler; + validate(sampler); + } else { + sampler = _immutableSamplers[rezIdx]; + } + sb.mtlSamplerState = sampler->getMTLSamplerState(); + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (_applyToStage[i]) { + sb.index = mtlIdxs.stages[i].samplerIndex + rezIdx; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindSamplerState(sb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindSamplerState(MVKShaderStage(i), sb); } + } + } + } + break; + } + + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + const auto& imageInfo = get(pData, stride, rezIdx - dstArrayElement); + MVKImageView* imageView = (MVKImageView*)imageInfo.imageView; + tb.mtlTexture = imageView->getMTLTexture(); + if (imageView) { + tb.swizzle = imageView->getPackedSwizzle(); + } else { + tb.swizzle = 0; + } + MVKSampler* sampler; + if (_immutableSamplers.empty()) { + sampler = (MVKSampler*)imageInfo.sampler; + validate(sampler); + } else { + sampler = _immutableSamplers[rezIdx]; + } + sb.mtlSamplerState = sampler->getMTLSamplerState(); + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (_applyToStage[i]) { + tb.index = mtlIdxs.stages[i].textureIndex + rezIdx; + sb.index = mtlIdxs.stages[i].samplerIndex + rezIdx; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindSamplerState(sb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindSamplerState(MVKShaderStage(i), sb); } + } + } + } + break; + } + + default: + break; + } + } + + dstArrayElement = 0; + if (_info.descriptorCount > descriptorCount) + descriptorCount = 0; + else { + descriptorCount -= _info.descriptorCount; + descriptorsPushed = _info.descriptorCount; + } +} + +// If depth compare is required, but unavailable on the device, the sampler can only be used as an immutable sampler +bool MVKDescriptorSetLayoutBinding::validate(MVKSampler* mvkSampler) { + if (mvkSampler->getRequiresConstExprSampler()) { + mvkSampler->reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdPushDescriptorSet/vkCmdPushDescriptorSetWithTemplate(): Depth texture samplers using a compare operation can only be used as immutable samplers on this device."); + return false; + } + return true; +} + +void MVKDescriptorSetLayoutBinding::populateShaderConverterContext(mvk::SPIRVToMSLConversionConfiguration& context, + MVKShaderResourceBinding& dslMTLRezIdxOffsets, + uint32_t dslIndex) { + + MVKSampler* mvkSamp = !_immutableSamplers.empty() ? _immutableSamplers.front() : nullptr; + + // Establish the resource indices to use, by combining the offsets of the DSL and this DSL binding. + MVKShaderResourceBinding mtlIdxs = _mtlResourceIndexOffsets + dslMTLRezIdxOffsets; + + static const spv::ExecutionModel models[] = { + spv::ExecutionModelVertex, + spv::ExecutionModelTessellationControl, + spv::ExecutionModelTessellationEvaluation, + spv::ExecutionModelFragment, + spv::ExecutionModelGLCompute + }; + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (_applyToStage[i]) { + mvkPopulateShaderConverterContext(context, + mtlIdxs.stages[i], + models[i], + dslIndex, + _info.binding, + mvkSamp); + } + } +} + +MVKDescriptorSetLayoutBinding::MVKDescriptorSetLayoutBinding(MVKDevice* device, + MVKDescriptorSetLayout* layout, + const VkDescriptorSetLayoutBinding* pBinding) : MVKBaseDeviceObject(device), _layout(layout) { + + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + // Determine if this binding is used by this shader stage + _applyToStage[i] = mvkAreAllFlagsEnabled(pBinding->stageFlags, mvkVkShaderStageFlagBitsFromMVKShaderStage(MVKShaderStage(i))); + // If this binding is used by the shader, set the Metal resource index + if (_applyToStage[i]) { + initMetalResourceIndexOffsets(&_mtlResourceIndexOffsets.stages[i], + &layout->_mtlResourceCounts.stages[i], pBinding); + } + } + + // If immutable samplers are defined, copy them in + if ( pBinding->pImmutableSamplers && + (pBinding->descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || + pBinding->descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) ) { + _immutableSamplers.reserve(pBinding->descriptorCount); + for (uint32_t i = 0; i < pBinding->descriptorCount; i++) { + _immutableSamplers.push_back((MVKSampler*)pBinding->pImmutableSamplers[i]); + _immutableSamplers.back()->retain(); + } + } + + _info = *pBinding; + _info.pImmutableSamplers = nullptr; // Remove dangling pointer +} + +MVKDescriptorSetLayoutBinding::MVKDescriptorSetLayoutBinding(const MVKDescriptorSetLayoutBinding& binding) : + MVKBaseDeviceObject(binding._device), _layout(binding._layout), + _info(binding._info), _immutableSamplers(binding._immutableSamplers), + _mtlResourceIndexOffsets(binding._mtlResourceIndexOffsets) { + + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + _applyToStage[i] = binding._applyToStage[i]; + } + for (MVKSampler* sampler : _immutableSamplers) { + sampler->retain(); + } +} + +MVKDescriptorSetLayoutBinding::~MVKDescriptorSetLayoutBinding() { + for (MVKSampler* sampler : _immutableSamplers) { + sampler->release(); + } +} + +// Sets the appropriate Metal resource indexes within this binding from the +// specified descriptor set binding counts, and updates those counts accordingly. +void MVKDescriptorSetLayoutBinding::initMetalResourceIndexOffsets(MVKShaderStageResourceBinding* pBindingIndexes, + MVKShaderStageResourceBinding* pDescSetCounts, + const VkDescriptorSetLayoutBinding* pBinding) { + switch (pBinding->descriptorType) { + case VK_DESCRIPTOR_TYPE_SAMPLER: + pBindingIndexes->samplerIndex = pDescSetCounts->samplerIndex; + pDescSetCounts->samplerIndex += pBinding->descriptorCount; + + if (pBinding->descriptorCount > 1 && !_device->_pMetalFeatures->arrayOfSamplers) { + _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of samplers.", _device->getName())); + } + break; + + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + pBindingIndexes->textureIndex = pDescSetCounts->textureIndex; + pDescSetCounts->textureIndex += pBinding->descriptorCount; + pBindingIndexes->samplerIndex = pDescSetCounts->samplerIndex; + pDescSetCounts->samplerIndex += pBinding->descriptorCount; + + if (pBinding->descriptorCount > 1) { + if ( !_device->_pMetalFeatures->arrayOfTextures ) { + _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of textures.", _device->getName())); + } + if ( !_device->_pMetalFeatures->arrayOfSamplers ) { + _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of samplers.", _device->getName())); + } + } + break; + + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: + pBindingIndexes->textureIndex = pDescSetCounts->textureIndex; + pDescSetCounts->textureIndex += pBinding->descriptorCount; + + if (pBinding->descriptorCount > 1 && !_device->_pMetalFeatures->arrayOfTextures) { + _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of textures.", _device->getName())); + } + break; + + 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: + case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: + pBindingIndexes->bufferIndex = pDescSetCounts->bufferIndex; + pDescSetCounts->bufferIndex += pBinding->descriptorCount; + break; + + default: + break; + } +} + + +#pragma mark - +#pragma mark MVKBufferDescriptor + +// A null cmdEncoder can be passed to perform a validation pass +void MVKBufferDescriptor::bind(MVKCommandEncoder* cmdEncoder, + VkDescriptorType descriptorType, + uint32_t descriptorIndex, + bool stages[], + MVKShaderResourceBinding& mtlIndexes, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) { + MVKMTLBufferBinding bb; + NSUInteger bufferDynamicOffset = 0; + + switch (descriptorType) { + // After determining dynamic part of offset (zero otherwise), fall through to non-dynamic handling + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + bufferDynamicOffset = dynamicOffsets[*pDynamicOffsetIndex]; + (*pDynamicOffsetIndex)++; // Move on to next dynamic offset (and feedback to caller) + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: { + if (_mvkBuffer) { + bb.mtlBuffer = _mvkBuffer->getMTLBuffer(); + bb.offset = _mvkBuffer->getMTLBufferOffset() + _buffOffset + bufferDynamicOffset; + bb.size = (uint32_t)_mvkBuffer->getByteCount(); + } + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (stages[i]) { + bb.index = mtlIndexes.stages[i].bufferIndex + descriptorIndex; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } + } + } + } + break; + } + + default: + break; + } +} + +void MVKBufferDescriptor::write(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t srcIndex, + size_t stride, + const void* pData) { + 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: { + auto* oldBuff = _mvkBuffer; + + const auto* pBuffInfo = &get(pData, stride, srcIndex); + _mvkBuffer = (MVKBuffer*)pBuffInfo->buffer; + _buffOffset = pBuffInfo->offset; + _buffRange = pBuffInfo->range; + + if (_mvkBuffer) { _mvkBuffer->retain(); } + if (oldBuff) { oldBuff->release(); } + break; + } + + default: + break; + } +} + +void MVKBufferDescriptor::read(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t dstIndex, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock) { + 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: { + auto& buffInfo = pBufferInfo[dstIndex]; + buffInfo.buffer = (VkBuffer)_mvkBuffer; + buffInfo.offset = _buffOffset; + buffInfo.range = _buffRange; + break; + } + + default: + break; + } +} + +void MVKBufferDescriptor::reset() { + if (_mvkBuffer) { _mvkBuffer->release(); } + _mvkBuffer = nullptr; + _buffOffset = 0; + _buffRange = 0; + MVKDescriptor::reset(); +} + + +#pragma mark - +#pragma mark MVKInlineUniformBlockDescriptor + +// A null cmdEncoder can be passed to perform a validation pass +void MVKInlineUniformBlockDescriptor::bind(MVKCommandEncoder* cmdEncoder, + VkDescriptorType descriptorType, + uint32_t descriptorIndex, + bool stages[], + MVKShaderResourceBinding& mtlIndexes, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) { + MVKMTLBufferBinding bb; + + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: { + bb.mtlBuffer = _mtlBuffer; + bb.size = _dataSize; + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (stages[i]) { + bb.index = mtlIndexes.stages[i].bufferIndex + descriptorIndex; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } + } + } + } + break; + } + + default: + break; + } +} + +void MVKInlineUniformBlockDescriptor::write(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t srcIndex, + size_t stride, + const void* pData) { + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: { + const auto& srcInlineUniformBlock = get(pData, stride, srcIndex); + _dataSize = srcInlineUniformBlock.dataSize; + + [_mtlBuffer release]; + if (srcInlineUniformBlock.dataSize > 0) { + MTLResourceOptions mtlBuffOpts = MTLResourceStorageModeShared | MTLResourceCPUCacheModeDefaultCache; + _mtlBuffer = [mvkDescSet->getMTLDevice() newBufferWithBytes: srcInlineUniformBlock.pData + length: srcInlineUniformBlock.dataSize + options:mtlBuffOpts]; // retained + } else { + _mtlBuffer = nil; + } + + break; + } + + default: + break; + } +} + +void MVKInlineUniformBlockDescriptor::read(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t dstIndex, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock) { + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: { + auto& dstInlineUniformBlock = pInlineUniformBlock[dstIndex]; + void* pDstData = const_cast(dstInlineUniformBlock.pData); + void* pSrcData = _mtlBuffer.contents; + if (pSrcData && pDstData) { + memcpy(pDstData, pSrcData, _dataSize); + dstInlineUniformBlock.dataSize = _dataSize; + } else { + dstInlineUniformBlock.dataSize = 0; + } + break; + } + + default: + break; + } +} + +void MVKInlineUniformBlockDescriptor::reset() { + [_mtlBuffer release]; + _mtlBuffer = nil; + _dataSize = 0; + MVKDescriptor::reset(); +} + + +#pragma mark - +#pragma mark MVKImageDescriptor + +// A null cmdEncoder can be passed to perform a validation pass +void MVKImageDescriptor::bind(MVKCommandEncoder* cmdEncoder, + VkDescriptorType descriptorType, + uint32_t descriptorIndex, + bool stages[], + MVKShaderResourceBinding& mtlIndexes, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) { + MVKMTLTextureBinding tb; + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + if (_mvkImageView) { + tb.mtlTexture = _mvkImageView->getMTLTexture(); + } + if (descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE && tb.mtlTexture) { + tb.swizzle = _mvkImageView->getPackedSwizzle(); + } else { + tb.swizzle = 0; + } + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (stages[i]) { + tb.index = mtlIndexes.stages[i].textureIndex + descriptorIndex; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } + } + } + } + break; + } + + default: + break; + } +} + +void MVKImageDescriptor::write(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t srcIndex, + size_t stride, + const void* pData) { + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + auto* oldImgView = _mvkImageView; + + const auto* pImgInfo = &get(pData, stride, srcIndex); + _mvkImageView = (MVKImageView*)pImgInfo->imageView; + _imageLayout = pImgInfo->imageLayout; + + if (_mvkImageView) { _mvkImageView->retain(); } + if (oldImgView) { oldImgView->release(); } + + break; + } + + default: + break; + } +} + +void MVKImageDescriptor::read(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t dstIndex, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock) { + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + auto& imgInfo = pImageInfo[dstIndex]; + imgInfo.imageView = (VkImageView)_mvkImageView; + imgInfo.imageLayout = _imageLayout; + break; + } + + default: + break; + } +} + +void MVKImageDescriptor::reset() { + if (_mvkImageView) { _mvkImageView->release(); } + _mvkImageView = nullptr; + _imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + MVKDescriptor::reset(); +} + + +#pragma mark - +#pragma mark MVKSamplerDescriptorMixin + +// A null cmdEncoder can be passed to perform a validation pass +void MVKSamplerDescriptorMixin::bind(MVKCommandEncoder* cmdEncoder, + VkDescriptorType descriptorType, + uint32_t descriptorIndex, + bool stages[], + MVKShaderResourceBinding& mtlIndexes, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) { + MVKMTLSamplerStateBinding sb; + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_SAMPLER: + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + if (_mvkSampler) { + sb.mtlSamplerState = _mvkSampler->getMTLSamplerState(); + } + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (stages[i]) { + sb.index = mtlIndexes.stages[i].samplerIndex + descriptorIndex; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindSamplerState(sb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindSamplerState(MVKShaderStage(i), sb); } + } + } + } + break; + } + + default: + break; + } +} + +void MVKSamplerDescriptorMixin::write(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t srcIndex, + size_t stride, + const void* pData) { + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_SAMPLER: + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + auto* oldSamp = _mvkSampler; + + const auto* pImgInfo = &get(pData, stride, srcIndex); + if (_hasDynamicSampler) { + _mvkSampler = (MVKSampler*)pImgInfo->sampler; + if (_mvkSampler && _mvkSampler->getRequiresConstExprSampler()) { + _mvkSampler->reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkUpdateDescriptorSets(): Depth texture samplers using a compare operation can only be used as immutable samplers on this device."); + } + } else { + _mvkSampler = nullptr; + } + + if (_mvkSampler) { _mvkSampler->retain(); } + if (oldSamp) { oldSamp->release(); } + + break; + } + + default: + break; + } +} + +void MVKSamplerDescriptorMixin::read(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t dstIndex, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock) { + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_SAMPLER: + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + auto& imgInfo = pImageInfo[dstIndex]; + imgInfo.sampler = (VkSampler)_mvkSampler; + break; + } + + default: + break; + } +} + +void MVKSamplerDescriptorMixin::setLayout(MVKDescriptorSetLayoutBinding* dslBinding, uint32_t index) { + auto* oldSamp = _mvkSampler; + + _mvkSampler = nullptr; + _hasDynamicSampler = true; + + switch (dslBinding->getDescriptorType()) { + case VK_DESCRIPTOR_TYPE_SAMPLER: + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + // If the descriptor set layout binding contains immutable samplers, use them + // Otherwise they will be populated dynamically at a later time. + MVKSampler* imtSamp = dslBinding->getImmutableSampler(index); + if (imtSamp) { + _mvkSampler = imtSamp; + _hasDynamicSampler = false; + } + break; + } + + default: + break; + } + + if (_mvkSampler) { _mvkSampler->retain(); } + if (oldSamp) { oldSamp->release(); } +} + +void MVKSamplerDescriptorMixin::reset() { + if (_mvkSampler) { _mvkSampler->release(); } + _mvkSampler = nullptr; + _hasDynamicSampler = true; +} + + +#pragma mark - +#pragma mark MVKSamplerDescriptor + +// A null cmdEncoder can be passed to perform a validation pass +void MVKSamplerDescriptor::bind(MVKCommandEncoder* cmdEncoder, + VkDescriptorType descriptorType, + uint32_t descriptorIndex, + bool stages[], + MVKShaderResourceBinding& mtlIndexes, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) { + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_SAMPLER: { + MVKSamplerDescriptorMixin::bind(cmdEncoder, descriptorType, descriptorIndex, stages, + mtlIndexes, dynamicOffsets, pDynamicOffsetIndex); + break; + } + + default: + break; + } +} + +void MVKSamplerDescriptor::write(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t srcIndex, + size_t stride, + const void* pData) { + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_SAMPLER: { + MVKSamplerDescriptorMixin::write(mvkDescSet, descriptorType, srcIndex, stride, pData); + break; + } + + default: + break; + } +} + +void MVKSamplerDescriptor::read(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t dstIndex, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock) { + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_SAMPLER: { + MVKSamplerDescriptorMixin::read(mvkDescSet, descriptorType, dstIndex, pImageInfo, + pBufferInfo, pTexelBufferView, pInlineUniformBlock); + break; + } + + default: + break; + } +} + +void MVKSamplerDescriptor::setLayout(MVKDescriptorSetLayoutBinding* dslBinding, uint32_t index) { + MVKDescriptor::setLayout(dslBinding, index); + MVKSamplerDescriptorMixin::setLayout(dslBinding, index); +} + +void MVKSamplerDescriptor::reset() { + MVKSamplerDescriptorMixin::reset(); + MVKDescriptor::reset(); +} + + +#pragma mark - +#pragma mark MVKCombinedImageSamplerDescriptor + +// A null cmdEncoder can be passed to perform a validation pass +void MVKCombinedImageSamplerDescriptor::bind(MVKCommandEncoder* cmdEncoder, + VkDescriptorType descriptorType, + uint32_t descriptorIndex, + bool stages[], + MVKShaderResourceBinding& mtlIndexes, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) { + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + MVKImageDescriptor::bind(cmdEncoder, descriptorType, descriptorIndex, stages, + mtlIndexes, dynamicOffsets, pDynamicOffsetIndex); + MVKSamplerDescriptorMixin::bind(cmdEncoder, descriptorType, descriptorIndex, stages, + mtlIndexes, dynamicOffsets, pDynamicOffsetIndex); + break; + } + + default: + break; + } +} + +void MVKCombinedImageSamplerDescriptor::write(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t srcIndex, + size_t stride, + const void* pData) { + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + MVKImageDescriptor::write(mvkDescSet, descriptorType, srcIndex, stride, pData); + MVKSamplerDescriptorMixin::write(mvkDescSet, descriptorType, srcIndex, stride, pData); + break; + } + + default: + break; + } +} + +void MVKCombinedImageSamplerDescriptor::read(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t dstIndex, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock) { + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { + MVKImageDescriptor::read(mvkDescSet, descriptorType, dstIndex, pImageInfo, + pBufferInfo, pTexelBufferView, pInlineUniformBlock); + MVKSamplerDescriptorMixin::read(mvkDescSet, descriptorType, dstIndex, pImageInfo, + pBufferInfo, pTexelBufferView, pInlineUniformBlock); + break; + } + + default: + break; + } +} + +void MVKCombinedImageSamplerDescriptor::setLayout(MVKDescriptorSetLayoutBinding* dslBinding, uint32_t index) { + MVKImageDescriptor::setLayout(dslBinding, index); + MVKSamplerDescriptorMixin::setLayout(dslBinding, index); +} + +void MVKCombinedImageSamplerDescriptor::reset() { + MVKSamplerDescriptorMixin::reset(); + MVKImageDescriptor::reset(); +} + + +#pragma mark - +#pragma mark MVKTexelBufferDescriptor + +// A null cmdEncoder can be passed to perform a validation pass +void MVKTexelBufferDescriptor::bind(MVKCommandEncoder* cmdEncoder, + VkDescriptorType descriptorType, + uint32_t descriptorIndex, + bool stages[], + MVKShaderResourceBinding& mtlIndexes, + MVKVector& dynamicOffsets, + uint32_t* pDynamicOffsetIndex) { + MVKMTLTextureBinding tb; + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { + if (_mvkBufferView) { + tb.mtlTexture = _mvkBufferView->getMTLTexture(); + } + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { + if (stages[i]) { + tb.index = mtlIndexes.stages[i].textureIndex + descriptorIndex; + if (i == kMVKShaderStageCompute) { + if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } + } else { + if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } + } + } + } + break; + } + + default: + break; + } +} + +void MVKTexelBufferDescriptor::write(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t srcIndex, + size_t stride, + const void* pData) { + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { + auto* oldBuffView = _mvkBufferView; + + const auto* pBuffView = &get(pData, stride, srcIndex); + _mvkBufferView = (MVKBufferView*)*pBuffView; + + if (_mvkBufferView) { _mvkBufferView->retain(); } + if (oldBuffView) { oldBuffView->release(); } + + break; + } + + default: + break; + } +} + +void MVKTexelBufferDescriptor::read(MVKDescriptorSet* mvkDescSet, + VkDescriptorType descriptorType, + uint32_t dstIndex, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock) { + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { + pTexelBufferView[dstIndex] = (VkBufferView)_mvkBufferView; + break; + } + + default: + break; + } +} + +void MVKTexelBufferDescriptor::reset() { + if (_mvkBufferView) { _mvkBufferView->release(); } + _mvkBufferView = nullptr; + MVKDescriptor::reset(); +} diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h index 46a5e2fa3..212fc6377 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h @@ -18,112 +18,16 @@ #pragma once -#include "MVKDevice.h" -#include "MVKImage.h" -#include "MVKVector.h" -#include +#include "MVKDescriptor.h" #include #include #include class MVKDescriptorPool; -class MVKDescriptorBinding; -class MVKDescriptorSet; -class MVKDescriptorSetLayout; class MVKPipelineLayout; class MVKCommandEncoder; -#pragma mark MVKShaderStageResourceBinding - -/** Indicates the Metal resource indexes used by a single shader stage in a descriptor binding. */ -typedef struct MVKShaderStageResourceBinding { - uint32_t bufferIndex = 0; - uint32_t textureIndex = 0; - uint32_t samplerIndex = 0; - - MVKShaderStageResourceBinding operator+ (const MVKShaderStageResourceBinding& rhs); - MVKShaderStageResourceBinding& operator+= (const MVKShaderStageResourceBinding& rhs); - -} MVKShaderStageResourceBinding; - - -#pragma mark MVKShaderResourceBinding - -/** Indicates the Metal resource indexes used by each shader stage in a descriptor binding. */ -typedef struct MVKShaderResourceBinding { - MVKShaderStageResourceBinding stages[kMVKShaderStageMax]; - - uint32_t getMaxBufferIndex(); - uint32_t getMaxTextureIndex(); - uint32_t getMaxSamplerIndex(); - - MVKShaderResourceBinding operator+ (const MVKShaderResourceBinding& rhs); - MVKShaderResourceBinding& operator+= (const MVKShaderResourceBinding& rhs); - -} MVKShaderResourceBinding; - - -#pragma mark - -#pragma mark MVKDescriptorSetLayoutBinding - -/** Represents a Vulkan descriptor set layout binding. */ -class MVKDescriptorSetLayoutBinding : public MVKBaseDeviceObject { - -public: - - /** Returns the Vulkan API opaque object controlling this object. */ - MVKVulkanAPIObject* getVulkanAPIObject() override; - - /** Encodes this binding layout and the specified descriptor set binding on the specified command encoder. */ - void bind(MVKCommandEncoder* cmdEncoder, - MVKDescriptorBinding& descBinding, - MVKShaderResourceBinding& dslMTLRezIdxOffsets, - MVKVector& dynamicOffsets, - uint32_t* pDynamicOffsetIndex); - - /** Encodes this binding layout and the specified descriptor binding on the specified command encoder immediately. */ - void push(MVKCommandEncoder* cmdEncoder, - uint32_t& dstArrayElement, - uint32_t& descriptorCount, - uint32_t& descriptorsPushed, - VkDescriptorType descriptorType, - size_t stride, - const void* pData, - MVKShaderResourceBinding& dslMTLRezIdxOffsets); - - /** Populates the specified shader converter context, at the specified descriptor set binding. */ - void populateShaderConverterContext(mvk::SPIRVToMSLConversionConfiguration& context, - MVKShaderResourceBinding& dslMTLRezIdxOffsets, - uint32_t dslIndex); - - /** Constructs an instance. */ - MVKDescriptorSetLayoutBinding(MVKDevice* device, - MVKDescriptorSetLayout* layout, - const VkDescriptorSetLayoutBinding* pBinding); - - MVKDescriptorSetLayoutBinding(const MVKDescriptorSetLayoutBinding& binding); - - /** Destuctor. */ - ~MVKDescriptorSetLayoutBinding() override; - -protected: - friend class MVKDescriptorBinding; - friend class MVKPipelineLayout; - - void initMetalResourceIndexOffsets(MVKShaderStageResourceBinding* pBindingIndexes, - MVKShaderStageResourceBinding* pDescSetCounts, - const VkDescriptorSetLayoutBinding* pBinding); - bool validate(MVKSampler* mvkSampler); - - MVKDescriptorSetLayout* _layout; - VkDescriptorSetLayoutBinding _info; - std::vector _immutableSamplers; - MVKShaderResourceBinding _mtlResourceIndexOffsets; - bool _applyToStage[kMVKShaderStageMax]; -}; - - #pragma mark - #pragma mark MVKDescriptorSetLayout @@ -169,8 +73,6 @@ class MVKDescriptorSetLayout : public MVKVulkanAPIDeviceObject { MVKDescriptorSetLayout(MVKDevice* device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo); - ~MVKDescriptorSetLayout(); - protected: friend class MVKDescriptorSetLayoutBinding; @@ -179,163 +81,132 @@ class MVKDescriptorSetLayout : public MVKVulkanAPIDeviceObject { friend class MVKDescriptorPool; void propogateDebugName() override {} - void addDescriptorPool(MVKDescriptorPool* mvkDescPool) { _descriptorPools.insert(mvkDescPool); } - void removeDescriptorPool(MVKDescriptorPool* mvkDescPool) { _descriptorPools.erase(mvkDescPool); } + inline uint32_t getDescriptorCount() { return _descriptorCount; } + uint32_t getDescriptorIndex(uint32_t binding, uint32_t elementIndex); + inline MVKDescriptorSetLayoutBinding* getBinding(uint32_t binding) { return &_bindings[_bindingToIndex[binding]]; } - MVKVectorInline _bindings; + std::vector _bindings; std::unordered_map _bindingToIndex; MVKShaderResourceBinding _mtlResourceCounts; - std::unordered_set _descriptorPools; - bool _isPushDescriptorLayout : 1; + uint32_t _descriptorCount; + bool _isPushDescriptorLayout; }; #pragma mark - -#pragma mark MVKDescriptorBinding +#pragma mark MVKDescriptorSet -/** Represents a Vulkan descriptor binding. */ -class MVKDescriptorBinding : public MVKBaseObject { +/** Represents a Vulkan descriptor set. */ +class MVKDescriptorSet : public MVKVulkanAPIDeviceObject { public: - /** Returns the Vulkan API opaque object controlling this object. */ - MVKVulkanAPIObject* getVulkanAPIObject() override; - - /** - * Updates the internal element bindings from the specified content. - * - * Depending on the descriptor type of the descriptor set, the binding content is - * extracted from one of the specified pImageInfo, pBufferInfo, or pTexelBufferView - * arrays, and the other arrays are ignored (and may be a null pointer). - * - * The srcStartIndex parameter indicates the index of the initial pDescriptor element - * at which to start reading, and the dstStartIndex parameter indicates the index of - * the initial internal element at which to start writing. - * - * The count parameter indicates how many internal elements should be updated, and - * may be larger than the number of descriptors that can be updated in this instance. - * If count is larger than the number of internal elements remaining after dstStartIndex, - * only the remaining elements will be updated, and the number of pDescriptors that were - * not read will be returned, so that the remaining unread pDescriptors can be read by - * another MVKDescriptorBinding instance within the same descriptor set. If all of the - * remaining pDescriptors are read by this intance, this function returns zero, indicating - * that there is nothing left to be read by another MVKDescriptorBinding instance. - */ - uint32_t writeBindings(uint32_t srcStartIndex, - uint32_t dstStartIndex, - uint32_t count, - size_t stride, - const void* pData); - - /** - * Updates the specified content arrays from the internal element bindings. - * - * Depending on the descriptor type of the descriptor set, the binding content is - * placed into one of the specified pImageInfo, pBufferInfo, or pTexelBufferView - * arrays, and the other arrays are ignored (and may be a null pointer). - * - * The srcStartIndex parameter indicates the index of the initial internal element - * at which to start reading, and the dstStartIndex parameter indicates the index of - * the initial pDescriptor element at which to start writing. - * - * The count parameter indicates how many internal elements should be read, and may - * be larger than the number of descriptors that can be read from this instance. - * If count is larger than the number of internal elements remaining after srcStartIndex, - * only the remaining elements will be read, and the number of pDescriptors that were not - * updated will be returned, so that the remaining pDescriptors can be updated by another - * MVKDescriptorBinding instance within the same descriptor set. If all of the remaining - * pDescriptors are updated by this intance, this function returns zero, indicating that - * there is nothing left to be updated by another MVKDescriptorBinding instance. - */ - uint32_t readBindings(uint32_t srcStartIndex, - uint32_t dstStartIndex, - uint32_t count, - VkDescriptorType& descType, - VkDescriptorImageInfo* pImageInfo, - VkDescriptorBufferInfo* pBufferInfo, - VkBufferView* pTexelBufferView, - VkWriteDescriptorSetInlineUniformBlockEXT* inlineUniformBlock); + /** Returns the Vulkan type of this object. */ + VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_DESCRIPTOR_SET; } - /** Returns whether this instance represents the specified Vulkan binding point. */ - bool hasBinding(uint32_t binding); + /** Returns the debug report object type of this object. */ + VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT; } - /** Constructs an instance. */ - MVKDescriptorBinding(MVKDescriptorSet* pDescSet, MVKDescriptorSetLayoutBinding* pBindingLayout); + /** Returns the descriptor type for the specified binding number. */ + VkDescriptorType getDescriptorType(uint32_t binding); - /** Destructor. */ - ~MVKDescriptorBinding(); + /** Updates the resource bindings in this instance from the specified content. */ + template + void write(const DescriptorAction* pDescriptorAction, size_t stride, const void* pData); + + /** + * Reads the resource bindings defined in the specified content + * from this instance into the specified collection of bindings. + */ + void read(const VkCopyDescriptorSet* pDescriptorCopies, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock); + + MVKDescriptorSet(MVKDescriptorSetLayout* layout, MVKDescriptorPool* pool); + + ~MVKDescriptorSet() override; protected: friend class MVKDescriptorSetLayoutBinding; + friend class MVKDescriptorPool; + + void propogateDebugName() override {} + inline MVKDescriptor* getDescriptor(uint32_t index) { return _descriptors[index]; } - void initMTLSamplers(MVKDescriptorSetLayoutBinding* pBindingLayout); - bool validate(MVKSampler* mvkSampler) { return _pBindingLayout->validate(mvkSampler); } - - MVKDescriptorSet* _pDescSet; - MVKDescriptorSetLayoutBinding* _pBindingLayout; - std::vector _imageBindings; - std::vector _bufferBindings; - std::vector _inlineBindings; - std::vector _texelBufferBindings; - std::vector> _mtlBuffers; - std::vector _mtlBufferOffsets; - std::vector> _mtlTextures; - std::vector> _mtlSamplers; - bool _hasDynamicSamplers; + MVKDescriptorSetLayout* _layout; + MVKDescriptorPool* _pool; + std::vector _descriptors; }; #pragma mark - -#pragma mark MVKDescriptorSet +#pragma mark MVKDescriptorTypePreallocation -/** Represents a Vulkan descriptor set. */ -class MVKDescriptorSet : public MVKVulkanAPIDeviceObject, public MVKLinkableMixin { +/** Support class for MVKDescriptorPool that holds preallocated instances of a single concrete descriptor class. */ +template +class MVKDescriptorTypePreallocation : public MVKBaseObject { public: - /** Returns the Vulkan type of this object. */ - VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_DESCRIPTOR_SET; } + /** Returns the Vulkan API opaque object controlling this object. */ + MVKVulkanAPIObject* getVulkanAPIObject() override { return nullptr; }; - /** Returns the debug report object type of this object. */ - VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT; } + MVKDescriptorTypePreallocation(const VkDescriptorPoolCreateInfo* pCreateInfo, + VkDescriptorType descriptorType); - /** Updates the resource bindings in this instance from the specified content. */ - template - void writeDescriptorSets(const DescriptorAction* pDescriptorAction, - size_t stride, - const void* pData); +protected: + friend class MVKPreallocatedDescriptors; - /** - * Reads the resource bindings defined in the specified content - * from this instance into the specified collection of bindings. - */ - void readDescriptorSets(const VkCopyDescriptorSet* pDescriptorCopies, - VkDescriptorType& descType, - VkDescriptorImageInfo* pImageInfo, - VkDescriptorBufferInfo* pBufferInfo, - VkBufferView* pTexelBufferView, - VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock); + VkResult allocateDescriptor(MVKDescriptor** pMVKDesc); + bool findDescriptor(uint32_t endIndex, MVKDescriptor** pMVKDesc); + void freeDescriptor(MVKDescriptor* mvkDesc); + + std::vector _descriptors; + std::vector _availability; + uint32_t _nextAvailableIndex; + bool _supportAvailability; +}; + + +#pragma mark - +#pragma mark MVKPreallocatedDescriptors - MVKDescriptorSet(MVKDevice* device) : MVKVulkanAPIDeviceObject(device) {} +/** Support class for MVKDescriptorPool that holds preallocated instances of all concrete descriptor classes. */ +class MVKPreallocatedDescriptors : public MVKBaseObject { + +public: + + /** Returns the Vulkan API opaque object controlling this object. */ + MVKVulkanAPIObject* getVulkanAPIObject() override { return nullptr; }; + + MVKPreallocatedDescriptors(const VkDescriptorPoolCreateInfo* pCreateInfo); protected: - friend class MVKDescriptorSetLayout; friend class MVKDescriptorPool; - void propogateDebugName() override {} - void setLayout(MVKDescriptorSetLayout* layout); - MVKDescriptorBinding* getBinding(uint32_t binding); - - MVKDescriptorSetLayout* _pLayout = nullptr; - MVKVectorInline _bindings; + VkResult allocateDescriptor(VkDescriptorType descriptorType, MVKDescriptor** pMVKDesc); + void freeDescriptor(MVKDescriptor* mvkDesc); + + MVKDescriptorTypePreallocation _uniformBufferDescriptors; + MVKDescriptorTypePreallocation _storageBufferDescriptors; + MVKDescriptorTypePreallocation _uniformBufferDynamicDescriptors; + MVKDescriptorTypePreallocation _storageBufferDynamicDescriptors; + MVKDescriptorTypePreallocation _inlineUniformBlockDescriptors; + MVKDescriptorTypePreallocation _sampledImageDescriptors; + MVKDescriptorTypePreallocation _storageImageDescriptors; + MVKDescriptorTypePreallocation _inputAttachmentDescriptors; + MVKDescriptorTypePreallocation _samplerDescriptors; + MVKDescriptorTypePreallocation _combinedImageSamplerDescriptors; + MVKDescriptorTypePreallocation _uniformTexelBufferDescriptors; + MVKDescriptorTypePreallocation _storageTexelBufferDescriptors; }; #pragma mark - #pragma mark MVKDescriptorPool -typedef MVKDeviceObjectPool MVKDescriptorSetPool; - /** Represents a Vulkan descriptor pool. */ class MVKDescriptorPool : public MVKVulkanAPIDeviceObject { @@ -358,23 +229,22 @@ class MVKDescriptorPool : public MVKVulkanAPIDeviceObject { /** Destoys all currently allocated descriptor sets. */ VkResult reset(VkDescriptorPoolResetFlags flags); - /** Removes the pool associated with a descriptor set layout. */ - void removeDescriptorSetPool(MVKDescriptorSetLayout* mvkDescSetLayout); - - /** Constructs an instance for the specified device. */ MVKDescriptorPool(MVKDevice* device, const VkDescriptorPoolCreateInfo* pCreateInfo); - /** Destructor. */ ~MVKDescriptorPool() override; protected: + friend class MVKDescriptorSet; + void propogateDebugName() override {} - MVKDescriptorSetPool* getDescriptorSetPool(MVKDescriptorSetLayout* mvkDescSetLayout); - void returnDescriptorSet(MVKDescriptorSet* mvkDescSet); + VkResult allocateDescriptorSet(MVKDescriptorSetLayout* mvkDSL, VkDescriptorSet* pVKDS); + void freeDescriptorSet(MVKDescriptorSet* mvkDS); + VkResult allocateDescriptor(VkDescriptorType descriptorType, MVKDescriptor** pMVKDesc); + void freeDescriptor(MVKDescriptor* mvkDesc); uint32_t _maxSets; std::unordered_set _allocatedSets; - std::unordered_map _descriptorSetPools; + MVKPreallocatedDescriptors* _preallocatedDescriptors; }; @@ -438,7 +308,3 @@ void mvkPopulateShaderConverterContext(mvk::SPIRVToMSLConversionConfiguration& c uint32_t descriptorSetIndex, uint32_t bindingIndex, MVKSampler* immutableSampler); - - - - diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm index f2a1b50f9..cfecbfd45 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm @@ -1,5 +1,5 @@ /* - * MVKDescriptorSetLayout.mm + * MVKDescriptorSet.mm * * Copyright (c) 2015-2020 The Brenwill Workshop Ltd. (http://www.brenwill.com) * @@ -17,552 +17,37 @@ */ #include "MVKDescriptorSet.h" -#include "MVKCommandBuffer.h" -#include "MVKBuffer.h" -#include "MVKFoundation.h" -#include "MVKLogging.h" -#include "mvk_datatypes.hpp" -#include - -using namespace std; -using namespace mvk; - - -#pragma mark MVKShaderStageResourceBinding - -MVKShaderStageResourceBinding MVKShaderStageResourceBinding::operator+ (const MVKShaderStageResourceBinding& rhs) { - MVKShaderStageResourceBinding rslt; - rslt.bufferIndex = this->bufferIndex + rhs.bufferIndex; - rslt.textureIndex = this->textureIndex + rhs.textureIndex; - rslt.samplerIndex = this->samplerIndex + rhs.samplerIndex; - return rslt; -} - -MVKShaderStageResourceBinding& MVKShaderStageResourceBinding::operator+= (const MVKShaderStageResourceBinding& rhs) { - this->bufferIndex += rhs.bufferIndex; - this->textureIndex += rhs.textureIndex; - this->samplerIndex += rhs.samplerIndex; - return *this; -} - - -#pragma mark MVKShaderResourceBinding - -uint32_t MVKShaderResourceBinding::getMaxBufferIndex() { - return max({stages[kMVKShaderStageVertex].bufferIndex, stages[kMVKShaderStageTessCtl].bufferIndex, stages[kMVKShaderStageTessEval].bufferIndex, stages[kMVKShaderStageFragment].bufferIndex, stages[kMVKShaderStageCompute].bufferIndex}); -} - -uint32_t MVKShaderResourceBinding::getMaxTextureIndex() { - return max({stages[kMVKShaderStageVertex].textureIndex, stages[kMVKShaderStageTessCtl].textureIndex, stages[kMVKShaderStageTessEval].textureIndex, stages[kMVKShaderStageFragment].textureIndex, stages[kMVKShaderStageCompute].textureIndex}); -} - -uint32_t MVKShaderResourceBinding::getMaxSamplerIndex() { - return max({stages[kMVKShaderStageVertex].samplerIndex, stages[kMVKShaderStageTessCtl].samplerIndex, stages[kMVKShaderStageTessEval].samplerIndex, stages[kMVKShaderStageFragment].samplerIndex, stages[kMVKShaderStageCompute].samplerIndex}); -} - -MVKShaderResourceBinding MVKShaderResourceBinding::operator+ (const MVKShaderResourceBinding& rhs) { - MVKShaderResourceBinding rslt; - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - rslt.stages[i] = this->stages[i] + rhs.stages[i]; - } - return rslt; -} - -MVKShaderResourceBinding& MVKShaderResourceBinding::operator+= (const MVKShaderResourceBinding& rhs) { - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - this->stages[i] += rhs.stages[i]; - } - return *this; -} +#include "MVKOSExtensions.h" #pragma mark - -#pragma mark MVKDescriptorSetLayoutBinding - -MVKVulkanAPIObject* MVKDescriptorSetLayoutBinding::getVulkanAPIObject() { return _layout; }; - -// A null cmdEncoder can be passed to perform a validation pass -void MVKDescriptorSetLayoutBinding::bind(MVKCommandEncoder* cmdEncoder, - MVKDescriptorBinding& descBinding, - MVKShaderResourceBinding& dslMTLRezIdxOffsets, - MVKVector& dynamicOffsets, - uint32_t* pDynamicOffsetIndex) { - MVKMTLBufferBinding bb; - MVKMTLTextureBinding tb; - MVKMTLSamplerStateBinding sb; - NSUInteger bufferDynamicOffset = 0; - - // Establish the resource indices to use, by combining the offsets of the DSL and this DSL binding. - MVKShaderResourceBinding mtlIdxs = _mtlResourceIndexOffsets + dslMTLRezIdxOffsets; - - for (uint32_t rezIdx = 0; rezIdx < _info.descriptorCount; rezIdx++) { - switch (_info.descriptorType) { - - // After determining dynamic part of offset (zero otherwise), fall through to non-dynamic handling - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: - bufferDynamicOffset = dynamicOffsets[*pDynamicOffsetIndex]; - (*pDynamicOffsetIndex)++; // Move on to next dynamic offset (and feedback to caller) - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: { - MVKBuffer* mvkBuff = (MVKBuffer*)descBinding._bufferBindings[rezIdx].buffer; - bb.mtlBuffer = descBinding._mtlBuffers[rezIdx]; - bb.offset = descBinding._mtlBufferOffsets[rezIdx] + bufferDynamicOffset; - bb.size = mvkBuff ? (uint32_t)mvkBuff->getByteCount() : 0; - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (_applyToStage[i]) { - bb.index = mtlIdxs.stages[i].bufferIndex + rezIdx; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } - } - } - } - break; - } - - case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: { - bb.mtlBuffer = descBinding._mtlBuffers[rezIdx]; - bb.offset = descBinding._mtlBufferOffsets[rezIdx]; - bb.size = descBinding._inlineBindings[rezIdx].dataSize; - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (_applyToStage[i]) { - bb.index = mtlIdxs.stages[i].bufferIndex + rezIdx; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } - } - } - } - 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: { - 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; - } - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (_applyToStage[i]) { - tb.index = mtlIdxs.stages[i].textureIndex + rezIdx; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } - } - } - } - break; - } - - case VK_DESCRIPTOR_TYPE_SAMPLER: { - sb.mtlSamplerState = descBinding._mtlSamplers[rezIdx]; - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (_applyToStage[i]) { - sb.index = mtlIdxs.stages[i].samplerIndex + rezIdx; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindSamplerState(sb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindSamplerState(MVKShaderStage(i), sb); } - } - } - } - break; - } - - 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]; - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (_applyToStage[i]) { - tb.index = mtlIdxs.stages[i].textureIndex + rezIdx; - sb.index = mtlIdxs.stages[i].samplerIndex + rezIdx; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindSamplerState(sb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindSamplerState(MVKShaderStage(i), sb); } - } - } - } - break; - } - - default: - break; - } - } -} - -template -static const T& get(const void* pData, size_t stride, uint32_t index) { - return *(T*)((const char*)pData + stride * index); -} - -// A null cmdEncoder can be passed to perform a validation pass -void MVKDescriptorSetLayoutBinding::push(MVKCommandEncoder* cmdEncoder, - uint32_t& dstArrayElement, - uint32_t& descriptorCount, - uint32_t& descriptorsPushed, - VkDescriptorType descriptorType, - size_t stride, - const void* pData, - MVKShaderResourceBinding& dslMTLRezIdxOffsets) { - MVKMTLBufferBinding bb; - MVKMTLTextureBinding tb; - MVKMTLSamplerStateBinding sb; - - if (dstArrayElement >= _info.descriptorCount) { - dstArrayElement -= _info.descriptorCount; - return; - } - - if (descriptorType != _info.descriptorType) { - dstArrayElement = 0; - if (_info.descriptorCount > descriptorCount) - descriptorCount = 0; - else { - descriptorCount -= _info.descriptorCount; - descriptorsPushed = _info.descriptorCount; - } - return; - } - - // Establish the resource indices to use, by combining the offsets of the DSL and this DSL binding. - MVKShaderResourceBinding mtlIdxs = _mtlResourceIndexOffsets + dslMTLRezIdxOffsets; - - for (uint32_t rezIdx = dstArrayElement; - rezIdx < _info.descriptorCount && rezIdx - dstArrayElement < descriptorCount; - rezIdx++) { - switch (_info.descriptorType) { - - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: { - const auto& bufferInfo = get(pData, stride, rezIdx - dstArrayElement); - MVKBuffer* buffer = (MVKBuffer*)bufferInfo.buffer; - bb.mtlBuffer = buffer->getMTLBuffer(); - bb.offset = buffer->getMTLBufferOffset() + bufferInfo.offset; - bb.size = (uint32_t)buffer->getByteCount(); - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (_applyToStage[i]) { - bb.index = mtlIdxs.stages[i].bufferIndex + rezIdx; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } - } - } - } - break; - } - - case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: { - const auto& inlineUniformBlock = get(pData, stride, rezIdx - dstArrayElement); - bb.mtlBytes = inlineUniformBlock.pData; - bb.size = inlineUniformBlock.dataSize; - bb.isInline = true; - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (_applyToStage[i]) { - bb.index = mtlIdxs.stages[i].bufferIndex + rezIdx; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindBuffer(bb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindBuffer(MVKShaderStage(i), bb); } - } - } - } - break; - } - - case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: - case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: - case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { - 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; - } - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (_applyToStage[i]) { - tb.index = mtlIdxs.stages[i].textureIndex + rezIdx; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } - } - } - } - break; - } - - case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { - auto* bufferView = get(pData, stride, rezIdx - dstArrayElement); - tb.mtlTexture = bufferView->getMTLTexture(); - tb.swizzle = 0; - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (_applyToStage[i]) { - tb.index = mtlIdxs.stages[i].textureIndex + rezIdx; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } - } - } - } - break; - } - - case VK_DESCRIPTOR_TYPE_SAMPLER: { - MVKSampler* sampler; - if (_immutableSamplers.empty()) { - sampler = (MVKSampler*)get(pData, stride, rezIdx - dstArrayElement).sampler; - validate(sampler); - } else { - sampler = _immutableSamplers[rezIdx]; - } - sb.mtlSamplerState = sampler->getMTLSamplerState(); - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (_applyToStage[i]) { - sb.index = mtlIdxs.stages[i].samplerIndex + rezIdx; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindSamplerState(sb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindSamplerState(MVKShaderStage(i), sb); } - } - } - } - break; - } - - case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { - const auto& imageInfo = get(pData, stride, rezIdx - dstArrayElement); - MVKImageView* imageView = (MVKImageView*)imageInfo.imageView; - tb.mtlTexture = imageView->getMTLTexture(); - if (imageView) { - tb.swizzle = imageView->getPackedSwizzle(); - } else { - tb.swizzle = 0; - } - MVKSampler* sampler; - if (_immutableSamplers.empty()) { - sampler = (MVKSampler*)imageInfo.sampler; - validate(sampler); - } else { - sampler = _immutableSamplers[rezIdx]; - } - sb.mtlSamplerState = sampler->getMTLSamplerState(); - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (_applyToStage[i]) { - tb.index = mtlIdxs.stages[i].textureIndex + rezIdx; - sb.index = mtlIdxs.stages[i].samplerIndex + rezIdx; - if (i == kMVKShaderStageCompute) { - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindTexture(tb); } - if (cmdEncoder) { cmdEncoder->_computeResourcesState.bindSamplerState(sb); } - } else { - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindTexture(MVKShaderStage(i), tb); } - if (cmdEncoder) { cmdEncoder->_graphicsResourcesState.bindSamplerState(MVKShaderStage(i), sb); } - } - } - } - break; - } - - default: - break; - } - } - - dstArrayElement = 0; - if (_info.descriptorCount > descriptorCount) - descriptorCount = 0; - else { - descriptorCount -= _info.descriptorCount; - descriptorsPushed = _info.descriptorCount; - } -} - -// If depth compare is required, but unavailable on the device, the sampler can only be used as an immutable sampler -bool MVKDescriptorSetLayoutBinding::validate(MVKSampler* mvkSampler) { - if (mvkSampler->getRequiresConstExprSampler()) { - _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkUpdateDescriptorSets(): Depth texture samplers using a compare operation can only be used as immutable samplers on this device.")); - return false; - } - return true; -} - -void MVKDescriptorSetLayoutBinding::populateShaderConverterContext(SPIRVToMSLConversionConfiguration& context, - MVKShaderResourceBinding& dslMTLRezIdxOffsets, - uint32_t dslIndex) { - - MVKSampler* mvkSamp = !_immutableSamplers.empty() ? _immutableSamplers.front() : nullptr; - - // Establish the resource indices to use, by combining the offsets of the DSL and this DSL binding. - MVKShaderResourceBinding mtlIdxs = _mtlResourceIndexOffsets + dslMTLRezIdxOffsets; - - static const spv::ExecutionModel models[] = { - spv::ExecutionModelVertex, - spv::ExecutionModelTessellationControl, - spv::ExecutionModelTessellationEvaluation, - spv::ExecutionModelFragment, - spv::ExecutionModelGLCompute - }; - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - if (_applyToStage[i]) { - mvkPopulateShaderConverterContext(context, - mtlIdxs.stages[i], - models[i], - dslIndex, - _info.binding, - mvkSamp); - } - } -} - -MVKDescriptorSetLayoutBinding::MVKDescriptorSetLayoutBinding(MVKDevice* device, - MVKDescriptorSetLayout* layout, - const VkDescriptorSetLayoutBinding* pBinding) : MVKBaseDeviceObject(device), _layout(layout) { - - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - // Determine if this binding is used by this shader stage - _applyToStage[i] = mvkAreAllFlagsEnabled(pBinding->stageFlags, mvkVkShaderStageFlagBitsFromMVKShaderStage(MVKShaderStage(i))); - // If this binding is used by the shader, set the Metal resource index - if (_applyToStage[i]) { - initMetalResourceIndexOffsets(&_mtlResourceIndexOffsets.stages[i], - &layout->_mtlResourceCounts.stages[i], pBinding); - } - } - - // If immutable samplers are defined, copy them in - if ( pBinding->pImmutableSamplers && - (pBinding->descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || - pBinding->descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) ) { - _immutableSamplers.reserve(pBinding->descriptorCount); - for (uint32_t i = 0; i < pBinding->descriptorCount; i++) { - _immutableSamplers.push_back((MVKSampler*)pBinding->pImmutableSamplers[i]); - _immutableSamplers.back()->retain(); - } - } - - _info = *pBinding; - _info.pImmutableSamplers = nullptr; // Remove dangling pointer -} - -MVKDescriptorSetLayoutBinding::MVKDescriptorSetLayoutBinding(const MVKDescriptorSetLayoutBinding& binding) : - MVKBaseDeviceObject(binding._device), _layout(binding._layout), - _info(binding._info), _immutableSamplers(binding._immutableSamplers), - _mtlResourceIndexOffsets(binding._mtlResourceIndexOffsets) { - - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) { - _applyToStage[i] = binding._applyToStage[i]; - } - for (MVKSampler* sampler : _immutableSamplers) { - sampler->retain(); - } -} +#pragma mark MVKDescriptorSetLayout -MVKDescriptorSetLayoutBinding::~MVKDescriptorSetLayoutBinding() { - for (MVKSampler* sampler : _immutableSamplers) { - sampler->release(); +// Look through the layout bindings looking for the binding number, accumulating the number +// of descriptors in each layout binding as we go, then add the element index. +uint32_t MVKDescriptorSetLayout::getDescriptorIndex(uint32_t binding, uint32_t elementIndex) { + uint32_t descIdx = 0; + for (auto& dslBind : _bindings) { + if (dslBind.getBinding() == binding) { break; } + descIdx += dslBind.getDescriptorCount(); } + return descIdx + elementIndex; } -// Sets the appropriate Metal resource indexes within this binding from the -// specified descriptor set binding counts, and updates those counts accordingly. -void MVKDescriptorSetLayoutBinding::initMetalResourceIndexOffsets(MVKShaderStageResourceBinding* pBindingIndexes, - MVKShaderStageResourceBinding* pDescSetCounts, - const VkDescriptorSetLayoutBinding* pBinding) { - switch (pBinding->descriptorType) { - case VK_DESCRIPTOR_TYPE_SAMPLER: - pBindingIndexes->samplerIndex = pDescSetCounts->samplerIndex; - pDescSetCounts->samplerIndex += pBinding->descriptorCount; - - if (pBinding->descriptorCount > 1 && !_device->_pMetalFeatures->arrayOfSamplers) { - _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of samplers.", _device->getName())); - } - break; - - case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - pBindingIndexes->textureIndex = pDescSetCounts->textureIndex; - pDescSetCounts->textureIndex += pBinding->descriptorCount; - pBindingIndexes->samplerIndex = pDescSetCounts->samplerIndex; - pDescSetCounts->samplerIndex += pBinding->descriptorCount; - - if (pBinding->descriptorCount > 1) { - if ( !_device->_pMetalFeatures->arrayOfTextures ) { - _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of textures.", _device->getName())); - } - if ( !_device->_pMetalFeatures->arrayOfSamplers ) { - _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of samplers.", _device->getName())); - } - } - break; - - case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: - case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: - case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: - case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: - pBindingIndexes->textureIndex = pDescSetCounts->textureIndex; - pDescSetCounts->textureIndex += pBinding->descriptorCount; - - if (pBinding->descriptorCount > 1 && !_device->_pMetalFeatures->arrayOfTextures) { - _layout->setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "Device %s does not support arrays of textures.", _device->getName())); - } - break; - - 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: - case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: - pBindingIndexes->bufferIndex = pDescSetCounts->bufferIndex; - pDescSetCounts->bufferIndex += pBinding->descriptorCount; - break; - - default: - break; - } -} - - -#pragma mark - -#pragma mark MVKDescriptorSetLayout - // A null cmdEncoder can be passed to perform a validation pass void MVKDescriptorSetLayout::bindDescriptorSet(MVKCommandEncoder* cmdEncoder, MVKDescriptorSet* descSet, MVKShaderResourceBinding& dslMTLRezIdxOffsets, MVKVector& dynamicOffsets, uint32_t* pDynamicOffsetIndex) { - if (_isPushDescriptorLayout) return; clearConfigurationResult(); uint32_t bindCnt = (uint32_t)_bindings.size(); - for (uint32_t bindIdx = 0; bindIdx < bindCnt; bindIdx++) { - _bindings[bindIdx].bind(cmdEncoder, descSet->_bindings[bindIdx], - dslMTLRezIdxOffsets, dynamicOffsets, - pDynamicOffsetIndex); + for (uint32_t descIdx = 0, bindIdx = 0; bindIdx < bindCnt; bindIdx++) { + descIdx += _bindings[bindIdx].bind(cmdEncoder, descSet, descIdx, + dslMTLRezIdxOffsets, dynamicOffsets, + pDynamicOffsetIndex); } } @@ -688,7 +173,7 @@ } } -void MVKDescriptorSetLayout::populateShaderConverterContext(SPIRVToMSLConversionConfiguration& context, +void MVKDescriptorSetLayout::populateShaderConverterContext(mvk::SPIRVToMSLConversionConfiguration& context, MVKShaderResourceBinding& dslMTLRezIdxOffsets, uint32_t dslIndex) { uint32_t bindCnt = (uint32_t)_bindings.size(); @@ -700,401 +185,302 @@ MVKDescriptorSetLayout::MVKDescriptorSetLayout(MVKDevice* device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device) { _isPushDescriptorLayout = (pCreateInfo->flags & VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR) != 0; - // Create the descriptor bindings + + // Create the descriptor layout bindings + _descriptorCount = 0; _bindings.reserve(pCreateInfo->bindingCount); for (uint32_t i = 0; i < pCreateInfo->bindingCount; i++) { _bindings.emplace_back(_device, this, &pCreateInfo->pBindings[i]); + _descriptorCount += _bindings.back().getDescriptorCount(); _bindingToIndex[pCreateInfo->pBindings[i].binding] = i; } } -MVKDescriptorSetLayout::~MVKDescriptorSetLayout() { - for (auto& dsPool : _descriptorPools) { dsPool->removeDescriptorSetPool(this); } -} - #pragma mark - -#pragma mark MVKDescriptorBinding +#pragma mark MVKDescriptorSet -MVKVulkanAPIObject* MVKDescriptorBinding::getVulkanAPIObject() { return _pDescSet->getVulkanAPIObject(); }; +VkDescriptorType MVKDescriptorSet::getDescriptorType(uint32_t binding) { + return _layout->getBinding(binding)->getDescriptorType(); +} -uint32_t MVKDescriptorBinding::writeBindings(uint32_t srcStartIndex, - uint32_t dstStartIndex, - uint32_t count, - size_t stride, - const void* pData) { +template +void MVKDescriptorSet::write(const DescriptorAction* pDescriptorAction, + size_t stride, + const void* pData) { + + VkDescriptorType descType = getDescriptorType(pDescriptorAction->dstBinding); + uint32_t dstStartIdx = _layout->getDescriptorIndex(pDescriptorAction->dstBinding, + pDescriptorAction->dstArrayElement); + uint32_t descCnt = pDescriptorAction->descriptorCount; + for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) { + _descriptors[dstStartIdx + descIdx]->write(this, descType, descIdx, stride, pData); + } +} - uint32_t dstCnt = MIN(count, _pBindingLayout->_info.descriptorCount - dstStartIndex); +// Create concrete implementations of the three variations of the write() function. +template void MVKDescriptorSet::write(const VkWriteDescriptorSet* pDescriptorAction, + size_t stride, const void *pData); +template void MVKDescriptorSet::write(const VkCopyDescriptorSet* pDescriptorAction, + size_t stride, const void *pData); +template void MVKDescriptorSet::write(const VkDescriptorUpdateTemplateEntryKHR* pDescriptorAction, + size_t stride, const void *pData); - switch (_pBindingLayout->_info.descriptorType) { - case VK_DESCRIPTOR_TYPE_SAMPLER: - for (uint32_t i = 0; i < dstCnt; i++) { - uint32_t dstIdx = dstStartIndex + i; - const auto* pImgInfo = &get(pData, stride, srcStartIndex + i); - auto* oldSampler = (MVKSampler*)_imageBindings[dstIdx].sampler; - _imageBindings[dstIdx] = *pImgInfo; - _imageBindings[dstIdx].imageView = nullptr; // Sampler only. Guard against app not explicitly clearing ImageView. - if (_hasDynamicSamplers) { - auto* mvkSampler = (MVKSampler*)pImgInfo->sampler; - validate(mvkSampler); - mvkSampler->retain(); - _mtlSamplers[dstIdx] = mvkSampler ? mvkSampler->getMTLSamplerState() : nil; - } else { - _imageBindings[dstIdx].sampler = nullptr; // Guard against app not explicitly clearing Sampler. - } - if (oldSampler) { - oldSampler->release(); - } - } - break; +void MVKDescriptorSet::read(const VkCopyDescriptorSet* pDescriptorCopy, + VkDescriptorImageInfo* pImageInfo, + VkDescriptorBufferInfo* pBufferInfo, + VkBufferView* pTexelBufferView, + VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock) { + + VkDescriptorType descType = getDescriptorType(pDescriptorCopy->srcBinding); + uint32_t srcStartIdx = _layout->getDescriptorIndex(pDescriptorCopy->srcBinding, + pDescriptorCopy->srcArrayElement); + uint32_t descCnt = pDescriptorCopy->descriptorCount; + for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) { + _descriptors[srcStartIdx + descIdx]->read(this, descType, descIdx, pImageInfo, pBufferInfo, + pTexelBufferView, pInlineUniformBlock); + } +} - case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - for (uint32_t i = 0; i < dstCnt; i++) { - uint32_t dstIdx = dstStartIndex + i; - const auto* pImgInfo = &get(pData, stride, srcStartIndex + i); - auto* mvkImageView = (MVKImageView*)pImgInfo->imageView; - auto* oldImageView = (MVKImageView*)_imageBindings[dstIdx].imageView; - auto* oldSampler = (MVKSampler*)_imageBindings[dstIdx].sampler; - mvkImageView->retain(); - _imageBindings[dstIdx] = *pImgInfo; - _mtlTextures[dstIdx] = mvkImageView ? mvkImageView->getMTLTexture() : nil; - if (_hasDynamicSamplers) { - auto* mvkSampler = (MVKSampler*)pImgInfo->sampler; - validate(mvkSampler); - mvkSampler->retain(); - _mtlSamplers[dstIdx] = mvkSampler ? mvkSampler->getMTLSamplerState() : nil; - } else { - _imageBindings[dstIdx].sampler = nullptr; // Guard against app not explicitly clearing Sampler. - } - if (oldImageView) { - oldImageView->release(); - } - if (oldSampler) { - oldSampler->release(); - } - } - break; +// If the descriptor pool fails to allocate a descriptor, record a configuration error +MVKDescriptorSet::MVKDescriptorSet(MVKDescriptorSetLayout* layout, MVKDescriptorPool* pool) : + MVKVulkanAPIDeviceObject(pool->_device), _layout(layout), _pool(pool) { - case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: - case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: - case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: - for (uint32_t i = 0; i < dstCnt; i++) { - uint32_t dstIdx = dstStartIndex + i; - const auto* pImgInfo = &get(pData, stride, srcStartIndex + i); - auto* mvkImageView = (MVKImageView*)pImgInfo->imageView; - auto* oldImageView = (MVKImageView*)_imageBindings[dstIdx].imageView; - if (mvkImageView) { - mvkImageView->retain(); - } - _imageBindings[dstIdx] = *pImgInfo; - _imageBindings[dstIdx].sampler = nullptr; // ImageView only. Guard against app not explicitly clearing Sampler. - _mtlTextures[dstIdx] = mvkImageView ? mvkImageView->getMTLTexture() : nil; - if (oldImageView) { - oldImageView->release(); - } - } - break; + _descriptors.reserve(layout->getDescriptorCount()); + uint32_t bindCnt = (uint32_t)layout->_bindings.size(); + for (uint32_t bindIdx = 0; bindIdx < bindCnt; bindIdx++) { + MVKDescriptorSetLayoutBinding* mvkDSLBind = &layout->_bindings[bindIdx]; + uint32_t descCnt = mvkDSLBind->getDescriptorCount(); + for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) { + MVKDescriptor* mvkDesc = nullptr; + setConfigurationResult(_pool->allocateDescriptor(mvkDSLBind->getDescriptorType(), &mvkDesc)); + if ( !wasConfigurationSuccessful() ) { break; } + + mvkDesc->setLayout(mvkDSLBind, descIdx); + _descriptors.push_back(mvkDesc); + } + if ( !wasConfigurationSuccessful() ) { break; } + } +} - 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: - for (uint32_t i = 0; i < dstCnt; i++) { - uint32_t dstIdx = dstStartIndex + i; - const auto* pBuffInfo = &get(pData, stride, srcStartIndex + i); - auto* oldBuff = (MVKBuffer*)_bufferBindings[dstIdx].buffer; - _bufferBindings[dstIdx] = *pBuffInfo; - auto* mtlBuff = (MVKBuffer*)pBuffInfo->buffer; - if (mtlBuff) { - mtlBuff->retain(); - } - _mtlBuffers[dstIdx] = mtlBuff ? mtlBuff->getMTLBuffer() : nil; - _mtlBufferOffsets[dstIdx] = mtlBuff ? (mtlBuff->getMTLBufferOffset() + pBuffInfo->offset) : 0; - if (oldBuff) { - oldBuff->release(); - } - } - break; +MVKDescriptorSet::~MVKDescriptorSet() { + for (auto mvkDesc : _descriptors) { _pool->freeDescriptor(mvkDesc); } +} - case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: - for (uint32_t i = 0; i < dstCnt; i++) { - uint32_t dstIdx = dstStartIndex + i; - const auto* pBuffView = &get(pData, stride, srcStartIndex + i); - auto* mvkBuffView = (MVKBufferView*)*pBuffView; - auto* oldBuffView = (MVKBufferView*)_texelBufferBindings[dstIdx]; - if (mvkBuffView) { - mvkBuffView->retain(); - } - _texelBufferBindings[dstIdx] = *pBuffView; - _mtlTextures[dstIdx] = mvkBuffView ? mvkBuffView->getMTLTexture() : nil; - if (oldBuffView) { - oldBuffView->release(); - } - } - break; - case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: - for (uint32_t i = 0; i < dstCnt; i++) { - uint32_t dstIdx = dstStartIndex + i; - const auto& srcInlineUniformBlock = get(pData, stride, srcStartIndex + i); - auto& dstInlineUniformBlock = _inlineBindings[dstIdx]; - if (srcInlineUniformBlock.dataSize != 0) { - MTLResourceOptions mtlBuffOpts = MTLResourceStorageModeShared | MTLResourceCPUCacheModeDefaultCache; - _mtlBuffers[dstIdx] = [_pDescSet->getMTLDevice() newBufferWithBytes:srcInlineUniformBlock.pData length:srcInlineUniformBlock.dataSize options:mtlBuffOpts]; - } else { - _mtlBuffers[dstIdx] = nil; - } - dstInlineUniformBlock.dataSize = srcInlineUniformBlock.dataSize; - dstInlineUniformBlock.pData = nullptr; - } - break; +#pragma mark - +#pragma mark MVKDescriptorTypePreallocation + +#ifndef MVK_CONFIG_PREALLOCATE_DESCRIPTORS +# define MVK_CONFIG_PREALLOCATE_DESCRIPTORS 0 +#endif + +static bool _mvkPreallocateDescriptors = MVK_CONFIG_PREALLOCATE_DESCRIPTORS; +static bool _mvkPreallocateDescriptorsInitialized = false; + +// Returns whether descriptors should be preallocated in the descriptor pools +// We do this once lazily instead of in a library constructor function to +// ensure the NSProcessInfo environment is available when called upon. +static inline bool getMVKPreallocateDescriptors() { + if ( !_mvkPreallocateDescriptorsInitialized ) { + _mvkPreallocateDescriptorsInitialized = true; + MVK_SET_FROM_ENV_OR_BUILD_BOOL(_mvkPreallocateDescriptors, MVK_CONFIG_PREALLOCATE_DESCRIPTORS); + } + return _mvkPreallocateDescriptors; +} - default: - break; +template +VkResult MVKDescriptorTypePreallocation::allocateDescriptor(MVKDescriptor** pMVKDesc) { + + uint32_t descCnt = (uint32_t)_descriptors.size(); + + // Preallocated descriptors that CANNOT be freed. + // Next available index can only monotonically increase towards the limit. + if ( !_supportAvailability ) { + if (_nextAvailableIndex < descCnt) { + *pMVKDesc = &_descriptors[_nextAvailableIndex++]; + return VK_SUCCESS; + } else { + return VK_ERROR_OUT_OF_POOL_MEMORY; + } } - return count - dstCnt; -} + // Descriptors that CAN be freed. + // An available index might exist anywhere in the pool of descriptors. + uint32_t origNextAvailPoolIdx = _nextAvailableIndex; -uint32_t MVKDescriptorBinding::readBindings(uint32_t srcStartIndex, - uint32_t dstStartIndex, - uint32_t count, - VkDescriptorType& descType, - VkDescriptorImageInfo* pImageInfo, - VkDescriptorBufferInfo* pBufferInfo, - VkBufferView* pTexelBufferView, - VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock) { + // First start looking from most recently found available slot + if (findDescriptor(descCnt, pMVKDesc)) { return VK_SUCCESS; } - uint32_t srcCnt = MIN(count, _pBindingLayout->_info.descriptorCount - srcStartIndex); + // Then look from beginning of the collection, in case any previous descriptors were freed + _nextAvailableIndex = 0; + if (findDescriptor(origNextAvailPoolIdx, pMVKDesc)) { return VK_SUCCESS; } - descType = _pBindingLayout->_info.descriptorType; - switch (_pBindingLayout->_info.descriptorType) { - case VK_DESCRIPTOR_TYPE_SAMPLER: - case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: - case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: - case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: - for (uint32_t i = 0; i < srcCnt; i++) { - pImageInfo[dstStartIndex + i] = _imageBindings[srcStartIndex + i]; - } - break; + return VK_ERROR_OUT_OF_POOL_MEMORY; +} - 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: - for (uint32_t i = 0; i < srcCnt; i++) { - pBufferInfo[dstStartIndex + i] = _bufferBindings[srcStartIndex + i]; - } - break; +// Find a descriptor within a range in a preallocated collection based on availability, +// and return true if found, false if not +template +bool MVKDescriptorTypePreallocation::findDescriptor(uint32_t endIndex, + MVKDescriptor** pMVKDesc) { + while (_nextAvailableIndex < endIndex) { + if (_availability[_nextAvailableIndex]) { + _availability[_nextAvailableIndex] = false; + *pMVKDesc = &_descriptors[_nextAvailableIndex]; + _nextAvailableIndex++; + return true; + } + _nextAvailableIndex++; + } + return false; +} - case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: - case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: - for (uint32_t i = 0; i < srcCnt; i++) { - pTexelBufferView[dstStartIndex + i] = _texelBufferBindings[srcStartIndex + i]; - } - break; +// Reset a descriptor and mark it available, if applicable +template +void MVKDescriptorTypePreallocation::freeDescriptor(MVKDescriptor* mvkDesc) { - case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: - for (uint32_t i = 0; i < srcCnt; i++) { - const auto& srcInlineUniformBlock = _inlineBindings[srcStartIndex + i]; - auto& dstInlineUniformBlock = pInlineUniformBlock[dstStartIndex + i]; - if (dstInlineUniformBlock.pData && dstInlineUniformBlock.pData != srcInlineUniformBlock.pData) - delete [] reinterpret_cast(dstInlineUniformBlock.pData); - if (srcInlineUniformBlock.dataSize != 0) { - dstInlineUniformBlock.pData = reinterpret_cast(new uint8_t*[srcInlineUniformBlock.dataSize]); - if (srcInlineUniformBlock.pData) { - memcpy(const_cast(dstInlineUniformBlock.pData), srcInlineUniformBlock.pData, srcInlineUniformBlock.dataSize); - } - } else { - dstInlineUniformBlock.pData = nullptr; - } - dstInlineUniformBlock.dataSize = srcInlineUniformBlock.dataSize; + mvkDesc->reset(); + + if (_supportAvailability) { + bool found = false; + size_t descCnt = _descriptors.size(); + for (uint32_t descIdx = 0; !found && descIdx < descCnt; descIdx++) { + if (&_descriptors[descIdx] == mvkDesc) { + found = true; + _availability[descIdx] = true; } - break; + } + } +} - default: - break; +template +MVKDescriptorTypePreallocation::MVKDescriptorTypePreallocation(const VkDescriptorPoolCreateInfo* pCreateInfo, + VkDescriptorType descriptorType) { + // There may be more than one poolSizeCount instance for the desired VkDescriptorType. + // Accumulate the descriptor count for the desired VkDescriptorType, and size the collections accordingly. + uint32_t descriptorCount = 0; + uint32_t poolCnt = pCreateInfo->poolSizeCount; + for (uint32_t poolIdx = 0; poolIdx < poolCnt; poolIdx++) { + auto& poolSize = pCreateInfo->pPoolSizes[poolIdx]; + if (poolSize.type == descriptorType) { descriptorCount += poolSize.descriptorCount; } } + _descriptors.resize(descriptorCount); - return count - srcCnt; + // Determine whether we need to track the availability of previously freed descriptors. + _supportAvailability = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT); + if (_supportAvailability) { _availability.resize(descriptorCount, true); } + _nextAvailableIndex = 0; } -bool MVKDescriptorBinding::hasBinding(uint32_t binding) { - return _pBindingLayout->_info.binding == binding; -} -MVKDescriptorBinding::MVKDescriptorBinding(MVKDescriptorSet* pDescSet, MVKDescriptorSetLayoutBinding* pBindingLayout) : _pDescSet(pDescSet) { +#pragma mark - +#pragma mark MVKPreallocatedDescriptors - uint32_t descCnt = pBindingLayout->_info.descriptorCount; +// Allocate a descriptor of the specified type +VkResult MVKPreallocatedDescriptors::allocateDescriptor(VkDescriptorType descriptorType, + MVKDescriptor** pMVKDesc) { + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + return _uniformBufferDescriptors.allocateDescriptor(pMVKDesc); - // Create space for the binding and Metal resources and populate with NULL and zero values - switch (pBindingLayout->_info.descriptorType) { - case VK_DESCRIPTOR_TYPE_SAMPLER: - _imageBindings.resize(descCnt, VkDescriptorImageInfo()); - initMTLSamplers(pBindingLayout); - break; + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + return _storageBufferDescriptors.allocateDescriptor(pMVKDesc); - case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - _imageBindings.resize(descCnt, VkDescriptorImageInfo()); - _mtlTextures.resize(descCnt, nil); - initMTLSamplers(pBindingLayout); - break; + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + return _uniformBufferDynamicDescriptors.allocateDescriptor(pMVKDesc); + + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + return _storageBufferDynamicDescriptors.allocateDescriptor(pMVKDesc); + + case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: + return _inlineUniformBlockDescriptors.allocateDescriptor(pMVKDesc); case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + return _sampledImageDescriptors.allocateDescriptor(pMVKDesc); + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + return _storageImageDescriptors.allocateDescriptor(pMVKDesc); + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: - _imageBindings.resize(descCnt, VkDescriptorImageInfo()); - _mtlTextures.resize(descCnt, nil); - break; + return _inputAttachmentDescriptors.allocateDescriptor(pMVKDesc); - 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: - _bufferBindings.resize(descCnt, VkDescriptorBufferInfo()); - _mtlBuffers.resize(descCnt, nil); - _mtlBufferOffsets.resize(descCnt, 0); - break; + case VK_DESCRIPTOR_TYPE_SAMPLER: + return _samplerDescriptors.allocateDescriptor(pMVKDesc); - case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: { - static const VkWriteDescriptorSetInlineUniformBlockEXT inlineUniformBlock {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT, nullptr, 0, nullptr}; - _inlineBindings.resize(descCnt, inlineUniformBlock); - _mtlBuffers.resize(descCnt, nil); - _mtlBufferOffsets.resize(descCnt, 0); - break; - } + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + return _combinedImageSamplerDescriptors.allocateDescriptor(pMVKDesc); case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + return _uniformTexelBufferDescriptors.allocateDescriptor(pMVKDesc); + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: - _texelBufferBindings.resize(descCnt, nil); - _mtlTextures.resize(descCnt, nil); - break; + return _storageTexelBufferDescriptors.allocateDescriptor(pMVKDesc); default: - break; + return reportError(VK_ERROR_INITIALIZATION_FAILED, "Unrecognized VkDescriptorType %d.", descriptorType); } - - // Okay to hold layout as a pointer. From the Vulkan spec... - // "VkDescriptorSetLayout objects may be accessed by commands that operate on descriptor - // sets allocated using that layout, and those descriptor sets must not be updated with - // vkUpdateDescriptorSets after the descriptor set layout has been destroyed. - _pBindingLayout = pBindingLayout; } -MVKDescriptorBinding::~MVKDescriptorBinding() { - for (const VkDescriptorImageInfo& imgInfo : _imageBindings) { - if (imgInfo.imageView) { - ((MVKImageView*)imgInfo.imageView)->release(); - } - if (imgInfo.sampler) { - ((MVKSampler*)imgInfo.sampler)->release(); - } - } - for (const VkDescriptorBufferInfo& buffInfo : _bufferBindings) { - if (buffInfo.buffer) { - ((MVKBuffer*)buffInfo.buffer)->release(); - } - } - for (VkBufferView buffView : _texelBufferBindings) { - if (buffView) { - ((MVKBufferView*)buffView)->release(); - } - } -} +void MVKPreallocatedDescriptors::freeDescriptor(MVKDescriptor* mvkDesc) { + VkDescriptorType descriptorType = mvkDesc->getDescriptorType(); + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + return _uniformBufferDescriptors.freeDescriptor(mvkDesc); -/** - * If the descriptor set layout binding contains immutable samplers, immediately populate - * the corresponding Metal sampler in this descriptor binding from it. Otherwise add a null - * placeholder that will be populated dynamically at a later time. - */ -void MVKDescriptorBinding::initMTLSamplers(MVKDescriptorSetLayoutBinding* pBindingLayout) { - uint32_t descCnt = pBindingLayout->_info.descriptorCount; - auto imtblSamps = pBindingLayout->_immutableSamplers; - _hasDynamicSamplers = imtblSamps.empty(); - - _mtlSamplers.reserve(descCnt); - for (uint32_t i = 0; i < descCnt; i++) { - _mtlSamplers.push_back(_hasDynamicSamplers ? nil : imtblSamps[i]->getMTLSamplerState()); - } -} + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + return _storageBufferDescriptors.freeDescriptor(mvkDesc); + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + return _uniformBufferDynamicDescriptors.freeDescriptor(mvkDesc); -#pragma mark - -#pragma mark MVKDescriptorSet + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + return _storageBufferDynamicDescriptors.freeDescriptor(mvkDesc); -template -void MVKDescriptorSet::writeDescriptorSets(const DescriptorAction* pDescriptorAction, - size_t stride, const void* pData) { - uint32_t dstStartIdx = pDescriptorAction->dstArrayElement; - uint32_t binding = pDescriptorAction->dstBinding; - uint32_t origCnt = pDescriptorAction->descriptorCount; - uint32_t remainCnt = origCnt; - - MVKDescriptorBinding* mvkDescBind = getBinding(binding); - while (mvkDescBind && remainCnt > 0) { - uint32_t srcStartIdx = origCnt - remainCnt; - remainCnt = mvkDescBind->writeBindings(srcStartIdx, dstStartIdx, remainCnt, - stride, pData); - binding++; // If not consumed, move to next consecutive binding point - mvkDescBind = getBinding(binding); - dstStartIdx = 0; // Subsequent bindings start reading at first element - } -} + case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: + return _inlineUniformBlockDescriptors.freeDescriptor(mvkDesc); -// Create concrete implementations of the three variations of the writeDescriptorSets() function. -template void MVKDescriptorSet::writeDescriptorSets(const VkWriteDescriptorSet* pDescriptorAction, - size_t stride, const void *pData); -template void MVKDescriptorSet::writeDescriptorSets(const VkCopyDescriptorSet* pDescriptorAction, - size_t stride, const void *pData); -template void MVKDescriptorSet::writeDescriptorSets( - const VkDescriptorUpdateTemplateEntryKHR* pDescriptorAction, - size_t stride, const void *pData); - -void MVKDescriptorSet::readDescriptorSets(const VkCopyDescriptorSet* pDescriptorCopy, - VkDescriptorType& descType, - VkDescriptorImageInfo* pImageInfo, - VkDescriptorBufferInfo* pBufferInfo, - VkBufferView* pTexelBufferView, - VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock) { - uint32_t srcStartIdx = pDescriptorCopy->srcArrayElement; - uint32_t binding = pDescriptorCopy->srcBinding; - uint32_t origCnt = pDescriptorCopy->descriptorCount; - uint32_t remainCnt = origCnt; - - MVKDescriptorBinding* mvkDescBind = getBinding(binding); - while (mvkDescBind && remainCnt > 0) { - uint32_t dstStartIdx = origCnt - remainCnt; - remainCnt = mvkDescBind->readBindings(srcStartIdx, dstStartIdx, remainCnt, descType, - pImageInfo, pBufferInfo, pTexelBufferView, pInlineUniformBlock); - binding++; // If not consumed, move to next consecutive binding point - mvkDescBind = getBinding(binding); - srcStartIdx = 0; // Subsequent bindings start reading at first element - } -} + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + return _sampledImageDescriptors.freeDescriptor(mvkDesc); -// Returns the binding instance that is assigned the specified -// binding number, or returns null if no such binding exists. -MVKDescriptorBinding* MVKDescriptorSet::getBinding(uint32_t binding) { - for (auto& mvkDB : _bindings) { if (mvkDB.hasBinding(binding)) { return &mvkDB; } } - return nullptr; -} + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + return _storageImageDescriptors.freeDescriptor(mvkDesc); -// If the layout has changed, create the binding slots, each referencing a corresponding binding layout -void MVKDescriptorSet::setLayout(MVKDescriptorSetLayout* layout) { - if (layout != _pLayout) { - _pLayout = layout; - uint32_t bindCnt = (uint32_t)layout->_bindings.size(); + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + return _inputAttachmentDescriptors.freeDescriptor(mvkDesc); - _bindings.clear(); - _bindings.reserve(bindCnt); - for (uint32_t i = 0; i < bindCnt; i++) { - _bindings.emplace_back(this, &layout->_bindings[i]); - } + case VK_DESCRIPTOR_TYPE_SAMPLER: + return _samplerDescriptors.freeDescriptor(mvkDesc); + + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + return _combinedImageSamplerDescriptors.freeDescriptor(mvkDesc); + + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + return _uniformTexelBufferDescriptors.freeDescriptor(mvkDesc); + + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: + return _storageTexelBufferDescriptors.freeDescriptor(mvkDesc); + + default: + reportError(VK_ERROR_INITIALIZATION_FAILED, "Unrecognized VkDescriptorType %d.", descriptorType); } } +MVKPreallocatedDescriptors::MVKPreallocatedDescriptors(const VkDescriptorPoolCreateInfo* pCreateInfo) : + _uniformBufferDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER), + _storageBufferDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER), + _uniformBufferDynamicDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC), + _storageBufferDynamicDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC), + _inlineUniformBlockDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT), + _sampledImageDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE), + _storageImageDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE), + _inputAttachmentDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT), + _samplerDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_SAMPLER), + _combinedImageSamplerDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER), + _uniformTexelBufferDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER), + _storageTexelBufferDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) { +} + #pragma mark - #pragma mark MVKDescriptorPool @@ -1110,82 +496,134 @@ } } + VkResult rslt = VK_SUCCESS; for (uint32_t dsIdx = 0; dsIdx < count; dsIdx++) { MVKDescriptorSetLayout* mvkDSL = (MVKDescriptorSetLayout*)pSetLayouts[dsIdx]; if ( !mvkDSL->isPushDescriptorLayout() ) { - MVKDescriptorSet* mvkDS = getDescriptorSetPool(mvkDSL)->acquireObject(); - mvkDS->setLayout(mvkDSL); - _allocatedSets.insert(mvkDS); - pDescriptorSets[dsIdx] = (VkDescriptorSet)mvkDS; + rslt = allocateDescriptorSet(mvkDSL, &pDescriptorSets[dsIdx]); + if (rslt) { break; } } } - return VK_SUCCESS; + return rslt; } // Ensure descriptor set was actually allocated, then return to pool VkResult MVKDescriptorPool::freeDescriptorSets(uint32_t count, const VkDescriptorSet* pDescriptorSets) { for (uint32_t dsIdx = 0; dsIdx < count; dsIdx++) { MVKDescriptorSet* mvkDS = (MVKDescriptorSet*)pDescriptorSets[dsIdx]; - if (_allocatedSets.erase(mvkDS)) { returnDescriptorSet(mvkDS); } + freeDescriptorSet(mvkDS); + _allocatedSets.erase(mvkDS); } return VK_SUCCESS; } -// Return any allocated descriptor sets to their pools +// Destroy all allocated descriptor sets VkResult MVKDescriptorPool::reset(VkDescriptorPoolResetFlags flags) { - for (auto& mvkDS : _allocatedSets) { returnDescriptorSet(mvkDS); } + for (auto& mvkDS : _allocatedSets) { freeDescriptorSet(mvkDS); } _allocatedSets.clear(); return VK_SUCCESS; } -// Returns the descriptor set to its pool, or if that pool doesn't exist, the descriptor set is destroyed -void MVKDescriptorPool::returnDescriptorSet(MVKDescriptorSet* mvkDescSet) { - MVKDescriptorSetLayout* dsLayout = mvkDescSet->_pLayout; - MVKDescriptorSetPool* dsPool = dsLayout ? _descriptorSetPools[dsLayout] : nullptr; - if (dsPool) { - dsPool->returnObject(mvkDescSet); +VkResult MVKDescriptorPool::allocateDescriptorSet(MVKDescriptorSetLayout* mvkDSL, + VkDescriptorSet* pVKDS) { + MVKDescriptorSet* mvkDS = new MVKDescriptorSet(mvkDSL, this); + VkResult rslt = mvkDS->getConfigurationResult(); + + if (mvkDS->wasConfigurationSuccessful()) { + _allocatedSets.insert(mvkDS); + *pVKDS = (VkDescriptorSet)mvkDS; } else { - mvkDescSet->destroy(); - _descriptorSetPools.erase(dsLayout); + freeDescriptorSet(mvkDS); } + return rslt; } -// Returns the pool of descriptor sets that use a specific layout, lazily creating it if necessary -MVKDescriptorSetPool* MVKDescriptorPool::getDescriptorSetPool(MVKDescriptorSetLayout* mvkDescSetLayout) { - MVKDescriptorSetPool* dsp = _descriptorSetPools[mvkDescSetLayout]; - if ( !dsp ) { - dsp = new MVKDescriptorSetPool(_device); - _descriptorSetPools[mvkDescSetLayout] = dsp; - mvkDescSetLayout->addDescriptorPool(this); // tell layout to track me +void MVKDescriptorPool::freeDescriptorSet(MVKDescriptorSet* mvkDS) { mvkDS->destroy(); } + +// Allocate a descriptor of the specified type +VkResult MVKDescriptorPool::allocateDescriptor(VkDescriptorType descriptorType, + MVKDescriptor** pMVKDesc) { + + // If descriptors are preallocated allocate from the preallocated pools + if (_preallocatedDescriptors) { + return _preallocatedDescriptors->allocateDescriptor(descriptorType, pMVKDesc); } - return dsp; -} -// Remove the descriptor set pool associated with the descriptor set layout, -// and make sure any allocated sets don't try to return back to their pools. -void MVKDescriptorPool::removeDescriptorSetPool(MVKDescriptorSetLayout* mvkDescSetLayout) { - MVKDescriptorSetPool* dsp = _descriptorSetPools[mvkDescSetLayout]; - if (dsp) { dsp->destroy(); } - _descriptorSetPools.erase(mvkDescSetLayout); + // Otherwise instantiate one of the apporpriate type now + switch (descriptorType) { + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + *pMVKDesc = new MVKUniformBufferDescriptor(); + break; - for (auto& mvkDS : _allocatedSets) { - if (mvkDS->_pLayout == mvkDescSetLayout) { mvkDS->_pLayout = nullptr; } + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + *pMVKDesc = new MVKStorageBufferDescriptor(); + break; + + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + *pMVKDesc = new MVKUniformBufferDynamicDescriptor(); + break; + + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + *pMVKDesc = new MVKStorageBufferDynamicDescriptor(); + break; + + case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT: + *pMVKDesc = new MVKInlineUniformBlockDescriptor(); + break; + + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + *pMVKDesc = new MVKSampledImageDescriptor(); + break; + + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + *pMVKDesc = new MVKStorageImageDescriptor(); + break; + + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + *pMVKDesc = new MVKInputAttachmentDescriptor(); + break; + + case VK_DESCRIPTOR_TYPE_SAMPLER: + *pMVKDesc = new MVKSamplerDescriptor(); + break; + + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + *pMVKDesc = new MVKCombinedImageSamplerDescriptor(); + break; + + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + *pMVKDesc = new MVKUniformTexelBufferDescriptor(); + break; + + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: + *pMVKDesc = new MVKStorageTexelBufferDescriptor(); + break; + + default: + return reportError(VK_ERROR_INITIALIZATION_FAILED, "Unrecognized VkDescriptorType %d.", descriptorType); + } + return VK_SUCCESS; +} + +// Free a descriptor, either through the preallocated pool, or directly destroy it +void MVKDescriptorPool::freeDescriptor(MVKDescriptor* mvkDesc) { + if (_preallocatedDescriptors) { + _preallocatedDescriptors->freeDescriptor(mvkDesc); + } else { + mvkDesc->destroy(); } } MVKDescriptorPool::MVKDescriptorPool(MVKDevice* device, const VkDescriptorPoolCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device) { _maxSets = pCreateInfo->maxSets; + _preallocatedDescriptors = getMVKPreallocateDescriptors() ? new MVKPreallocatedDescriptors(pCreateInfo) : nullptr; } -// Return any allocated sets to their pools and then destroy all the pools, -// and ensure any descriptor set layouts used as keys are notified. +// Destroy all allocated descriptor sets and preallocated descriptors MVKDescriptorPool::~MVKDescriptorPool() { reset(0); - for (auto& pair : _descriptorSetPools) { - pair.first->removeDescriptorPool(this); - if (pair.second) { pair.second->destroy(); } - } + if (_preallocatedDescriptors) { _preallocatedDescriptors->destroy(); } } @@ -1246,7 +684,7 @@ void mvkUpdateDescriptorSets(uint32_t writeCount, const void* pData = getWriteParameters(pDescWrite->descriptorType, pDescWrite->pImageInfo, pDescWrite->pBufferInfo, pDescWrite->pTexelBufferView, pInlineUniformBlock, stride); - dstSet->writeDescriptorSets(pDescWrite, stride, pData); + dstSet->write(pDescWrite, stride, pData); } // Perform the copy updates by reading bindings from one set and writing to other set. @@ -1254,19 +692,19 @@ void mvkUpdateDescriptorSets(uint32_t writeCount, const VkCopyDescriptorSet* pDescCopy = &pDescriptorCopies[i]; uint32_t descCnt = pDescCopy->descriptorCount; - VkDescriptorType descType; VkDescriptorImageInfo imgInfos[descCnt]; VkDescriptorBufferInfo buffInfos[descCnt]; VkBufferView texelBuffInfos[descCnt]; VkWriteDescriptorSetInlineUniformBlockEXT inlineUniformBlocks[descCnt]; MVKDescriptorSet* srcSet = (MVKDescriptorSet*)pDescCopy->srcSet; - srcSet->readDescriptorSets(pDescCopy, descType, imgInfos, buffInfos, texelBuffInfos, inlineUniformBlocks); + srcSet->read(pDescCopy, imgInfos, buffInfos, texelBuffInfos, inlineUniformBlocks); MVKDescriptorSet* dstSet = (MVKDescriptorSet*)pDescCopy->dstSet; + VkDescriptorType descType = dstSet->getDescriptorType(pDescCopy->dstBinding); size_t stride; const void* pData = getWriteParameters(descType, imgInfos, buffInfos, texelBuffInfos, inlineUniformBlocks, stride); - dstSet->writeDescriptorSets(pDescCopy, stride, pData); + dstSet->write(pDescCopy, stride, pData); } } @@ -1285,17 +723,17 @@ void mvkUpdateDescriptorSetWithTemplate(VkDescriptorSet descriptorSet, for (uint32_t i = 0; i < pTemplate->getNumberOfEntries(); i++) { const VkDescriptorUpdateTemplateEntryKHR* pEntry = pTemplate->getEntry(i); const void* pCurData = (const char*)pData + pEntry->offset; - dstSet->writeDescriptorSets(pEntry, pEntry->stride, pCurData); + dstSet->write(pEntry, pEntry->stride, pCurData); } } -void mvkPopulateShaderConverterContext(SPIRVToMSLConversionConfiguration& context, +void mvkPopulateShaderConverterContext(mvk::SPIRVToMSLConversionConfiguration& context, MVKShaderStageResourceBinding& ssRB, spv::ExecutionModel stage, uint32_t descriptorSetIndex, uint32_t bindingIndex, MVKSampler* immutableSampler) { - MSLResourceBinding rb; + mvk::MSLResourceBinding rb; auto& rbb = rb.resourceBinding; rbb.stage = stage; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index 9dd15384e..55621e484 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -1143,7 +1143,7 @@ // If compareEnable is true, but dynamic samplers with depth compare are not available // on this device, this sampler must only be used as an immutable sampler, and will // be automatically hardcoded into the shader MSL. An error will be triggered if this - // sampler is used to update or push a descriptor binding. + // sampler is used to update or push a descriptor. if (pCreateInfo->compareEnable && !_requiresConstExprSampler) { mtlSampDesc.compareFunctionMVK = mvkMTLCompareFunctionFromVkCompareOp(pCreateInfo->compareOp); } diff --git a/MoltenVK/MoltenVK/Utility/MVKBaseObject.h b/MoltenVK/MoltenVK/Utility/MVKBaseObject.h index 946fe31f6..5d106f9cb 100644 --- a/MoltenVK/MoltenVK/Utility/MVKBaseObject.h +++ b/MoltenVK/MoltenVK/Utility/MVKBaseObject.h @@ -117,6 +117,9 @@ class MVKConfigurableObject : public MVKBaseObject { if (_configurationResult == VK_SUCCESS) { _configurationResult = vkResult; } } + /** Returns whether the configuration was successful. */ + inline bool wasConfigurationSuccessful() { return _configurationResult == VK_SUCCESS; } + /** Resets the indication of the success of the configuration of this instance back to VK_SUCCESS. */ inline void clearConfigurationResult() { _configurationResult = VK_SUCCESS; }