diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md index 434bb858..80c235a8 100644 --- a/Docs/Whats_New.md +++ b/Docs/Whats_New.md @@ -18,6 +18,7 @@ MoltenVK 1.1.9 Released TBD +- Fixes to pipeline layout compatibility. - Reinstate memory barriers on non-Apple GPUs, which were inadvertently disabled in an earlier update. - Support base vertex instance support in shader conversion. - Fix alignment between outputs and inputs between shader stages when using nested structures. diff --git a/MoltenVK/MoltenVK/API/mvk_datatypes.h b/MoltenVK/MoltenVK/API/mvk_datatypes.h index 0a1ddb08..6fb71f5a 100644 --- a/MoltenVK/MoltenVK/API/mvk_datatypes.h +++ b/MoltenVK/MoltenVK/API/mvk_datatypes.h @@ -321,7 +321,7 @@ typedef enum { kMVKShaderStageFragment, kMVKShaderStageCompute, kMVKShaderStageCount, - kMVKShaderStageMax = kMVKShaderStageCount // Pubic API legacy value + kMVKShaderStageMax = kMVKShaderStageCount // Public API legacy value } MVKShaderStage; /** Returns the Metal MTLColorWriteMask corresponding to the specified Vulkan VkColorComponentFlags. */ diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h index 4bbda08b..00d32a4a 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h @@ -363,15 +363,3 @@ void mvkUpdateDescriptorSets(uint32_t writeCount, void mvkUpdateDescriptorSetWithTemplate(VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplateKHR updateTemplate, const void* pData); - -/** - * If the shader stage binding has a binding defined for the specified stage, populates - * the context at the descriptor set binding from the shader stage resource binding. - */ -void mvkPopulateShaderConversionConfig(mvk::SPIRVToMSLConversionConfiguration& shaderConfig, - MVKShaderStageResourceBinding& ssRB, - spv::ExecutionModel stage, - uint32_t descriptorSetIndex, - uint32_t bindingIndex, - uint32_t count, - MVKSampler* immutableSampler); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h index de407fb6..a7f271d3 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h @@ -75,35 +75,11 @@ public: /** Populates the specified shader conversion config. */ void populateShaderConversionConfig(SPIRVToMSLConversionConfiguration& shaderConfig); - /** Returns the current swizzle buffer bindings. */ - const MVKShaderImplicitRezBinding& getSwizzleBufferIndex() { return _swizzleBufferIndex; } - - /** Returns the current buffer size buffer bindings. */ - const MVKShaderImplicitRezBinding& getBufferSizeBufferIndex() { return _bufferSizeBufferIndex; } - - /** Returns the current dynamic buffer offset buffer bindings. */ - const MVKShaderImplicitRezBinding& getDynamicOffsetBufferIndex() { return _dynamicOffsetBufferIndex; } - - /** Returns the current view range buffer binding for multiview draws. */ - const MVKShaderImplicitRezBinding& getViewRangeBufferIndex() { return _viewRangeBufferIndex; } - - /** Returns the current indirect parameter buffer bindings. */ - const MVKShaderImplicitRezBinding& getIndirectParamsIndex() { return _indirectParamsIndex; } - - /** Returns the current captured output buffer bindings. */ - const MVKShaderImplicitRezBinding& getOutputBufferIndex() { return _outputBufferIndex; } - - /** Returns the current captured per-patch output buffer binding for the tess. control shader. */ - uint32_t getTessCtlPatchOutputBufferIndex() { return _tessCtlPatchOutputBufferIndex; } - - /** Returns the current tessellation level buffer binding for the tess. control shader. */ - uint32_t getTessCtlLevelBufferIndex() { return _tessCtlLevelBufferIndex; } - /** Returns the number of textures in this layout. This is used to calculate the size of the swizzle buffer. */ - uint32_t getTextureCount() { return _pushConstantsMTLResourceIndexes.getMaxTextureIndex(); } + uint32_t getTextureCount() { return _mtlResourceCounts.getMaxTextureIndex(); } /** Returns the number of buffers in this layout. This is used to calculate the size of the buffer size buffer. */ - uint32_t getBufferCount() { return _pushConstantsMTLResourceIndexes.getMaxBufferIndex(); } + uint32_t getBufferCount() { return _mtlResourceCounts.getMaxBufferIndex(); } /** Returns the number of descriptor sets in this pipeline layout. */ uint32_t getDescriptorSetCount() { return (uint32_t)_descriptorSetLayouts.size(); } @@ -114,9 +90,6 @@ public: /** Returns the descriptor set layout. */ MVKDescriptorSetLayout* getDescriptorSetLayout(uint32_t descSetIndex) { return _descriptorSetLayouts[descSetIndex]; } - /** Returns the push constant binding info. */ - const MVKShaderResourceBinding& getPushConstantBindings() { return _pushConstantsMTLResourceIndexes; } - /** Constructs an instance for the specified device. */ MVKPipelineLayout(MVKDevice* device, const VkPipelineLayoutCreateInfo* pCreateInfo); @@ -126,19 +99,13 @@ protected: friend class MVKPipeline; void propagateDebugName() override {} + bool stageUsesPushConstants(MVKShaderStage mvkStage); MVKSmallVector _descriptorSetLayouts; MVKSmallVector _dslMTLResourceIndexOffsets; MVKSmallVector _pushConstants; + MVKShaderResourceBinding _mtlResourceCounts; MVKShaderResourceBinding _pushConstantsMTLResourceIndexes; - MVKShaderImplicitRezBinding _swizzleBufferIndex; - MVKShaderImplicitRezBinding _bufferSizeBufferIndex; - MVKShaderImplicitRezBinding _dynamicOffsetBufferIndex; - MVKShaderImplicitRezBinding _viewRangeBufferIndex; - MVKShaderImplicitRezBinding _indirectParamsIndex; - MVKShaderImplicitRezBinding _outputBufferIndex; - uint32_t _tessCtlPatchOutputBufferIndex = 0; - uint32_t _tessCtlLevelBufferIndex = 0; }; @@ -199,12 +166,14 @@ protected: MVKShaderStage stage); MVKPipelineCache* _pipelineCache; + MVKShaderImplicitRezBinding _descriptorBufferCounts; MVKShaderImplicitRezBinding _swizzleBufferIndex; MVKShaderImplicitRezBinding _bufferSizeBufferIndex; MVKShaderImplicitRezBinding _dynamicOffsetBufferIndex; MVKShaderImplicitRezBinding _indirectParamsIndex; - MVKShaderResourceBinding _pushConstantsMTLResourceIndexes; + MVKShaderImplicitRezBinding _pushConstantsBufferIndex; uint32_t _descriptorSetCount; + bool _stageUsesPushConstants[kMVKShaderStageCount]; bool _fullImageViewSwizzle; bool _hasValidMTLPipelineStates = true; @@ -338,8 +307,10 @@ protected: void addFragmentOutputToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo); bool isRenderingPoints(const VkGraphicsPipelineCreateInfo* pCreateInfo); bool isRasterizationDisabled(const VkGraphicsPipelineCreateInfo* pCreateInfo); - bool verifyImplicitBuffer(bool needsBuffer, MVKShaderImplicitRezBinding& index, MVKShaderStage stage, const char* name, uint32_t reservedBuffers); + bool verifyImplicitBuffer(bool needsBuffer, MVKShaderImplicitRezBinding& index, MVKShaderStage stage, const char* name); uint32_t getTranslatedVertexBinding(uint32_t binding, uint32_t translationOffset, uint32_t maxBinding); + uint32_t getImplicitBufferIndex(const VkGraphicsPipelineCreateInfo* pCreateInfo, MVKShaderStage stage, uint32_t bufferIndexOffset); + uint32_t getReservedBufferCount(const VkGraphicsPipelineCreateInfo* pCreateInfo, MVKShaderStage stage); const VkPipelineShaderStageCreateInfo* _pVertexSS = nullptr; const VkPipelineShaderStageCreateInfo* _pTessCtlSS = nullptr; @@ -434,6 +405,7 @@ public: protected: MVKMTLFunction getMTLFunction(const VkComputePipelineCreateInfo* pCreateInfo); + uint32_t getImplicitBufferIndex(uint32_t bufferIndexOffset); id _mtlPipelineState; MVKSmallVector _mtlArgumentEncoders; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm index dc4bf84f..bf545e39 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm @@ -82,6 +82,21 @@ void MVKPipelineLayout::populateShaderConversionConfig(SPIRVToMSLConversionConfi shaderConfig.discreteDescriptorSets.clear(); shaderConfig.dynamicBufferDescriptors.clear(); + // Add any resource bindings used by push-constants. + // Use VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT descriptor type as compatible with push constants in Metal. + for (uint32_t stage = kMVKShaderStageVertex; stage < kMVKShaderStageCount; stage++) { + if (stageUsesPushConstants((MVKShaderStage)stage)) { + mvkPopulateShaderConversionConfig(shaderConfig, + _pushConstantsMTLResourceIndexes.stages[stage], + MVKShaderStage(stage), + kPushConstDescSet, + kPushConstBinding, + 1, + VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT, + nullptr); + } + } + // Add resource bindings defined in the descriptor set layouts uint32_t dslCnt = getDescriptorSetCount(); for (uint32_t dslIdx = 0; dslIdx < dslCnt; dslIdx++) { @@ -89,81 +104,65 @@ void MVKPipelineLayout::populateShaderConversionConfig(SPIRVToMSLConversionConfi _dslMTLResourceIndexOffsets[dslIdx], dslIdx); } +} - // Add any resource bindings used by push-constants. - // Use VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT descriptor type as compatible with push constants in Metal. - for (uint32_t stage = kMVKShaderStageVertex; stage < kMVKShaderStageCount; stage++) { - mvkPopulateShaderConversionConfig(shaderConfig, - _pushConstantsMTLResourceIndexes.stages[stage], - MVKShaderStage(stage), - kPushConstDescSet, - kPushConstBinding, - 1, - VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT, - nullptr); +bool MVKPipelineLayout::stageUsesPushConstants(MVKShaderStage mvkStage) { + VkShaderStageFlagBits vkStage = mvkVkShaderStageFlagBitsFromMVKShaderStage(mvkStage); + for (auto pushConst : _pushConstants) { + if (mvkIsAnyFlagEnabled(pushConst.stageFlags, vkStage)) { + return true; + } } + return false; } MVKPipelineLayout::MVKPipelineLayout(MVKDevice* device, const VkPipelineLayoutCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device) { - // Add descriptor set layouts, accumulating the resource index offsets used by the - // corresponding DSL, and associating the current accumulated resource index offsets - // with each DSL as it is added. The final accumulation of resource index offsets - // becomes the resource index offsets that will be used for push contants. - // If we are using Metal argument buffers, reserve space for the Metal argument - // buffers themselves, and clear indexes of offsets used in Metal argument buffers, - // but still accumulate dynamic offset buffer indexes across descriptor sets. + // For pipeline layout compatibility (“compatible for set N”), + // consume the Metal resource indexes in this order: + // - Fixed count of argument buffers for descriptor sets (if using Metal argument buffers). + // - Push constants + // - Descriptor set content - // According to the Vulkan spec, VkDescriptorSetLayout is intended to be consumed when passed - // to any Vulkan function, and may be safely destroyed by app immediately after. In order for - // this pipeline layout to retain the VkDescriptorSetLayout, the MVKDescriptorSetLayout - // instance is retained, so that it will live on here after it has been destroyed by the API. + // If we are using Metal argument buffers, consume a fixed number + // of buffer indexes for the Metal argument buffers themselves. + if (isUsingMetalArgumentBuffers()) { + _mtlResourceCounts.addArgumentBuffers(kMVKMaxDescriptorSetCount); + } + // Add push constants from config + _pushConstants.reserve(pCreateInfo->pushConstantRangeCount); + for (uint32_t i = 0; i < pCreateInfo->pushConstantRangeCount; i++) { + _pushConstants.push_back(pCreateInfo->pPushConstantRanges[i]); + } + + // Set push constant resource indexes, and consume a buffer index for any stage that uses a push constant buffer. + _pushConstantsMTLResourceIndexes = _mtlResourceCounts; + for (uint32_t stage = kMVKShaderStageVertex; stage < kMVKShaderStageCount; stage++) { + if (stageUsesPushConstants((MVKShaderStage)stage)) { + _mtlResourceCounts.stages[stage].bufferIndex++; + } + } + + // Add descriptor set layouts, accumulating the resource index offsets used by the corresponding DSL, + // and associating the current accumulated resource index offsets with each DSL as it is added. uint32_t dslCnt = pCreateInfo->setLayoutCount; - _pushConstantsMTLResourceIndexes.addArgumentBuffers(dslCnt); - _descriptorSetLayouts.reserve(dslCnt); for (uint32_t i = 0; i < dslCnt; i++) { MVKDescriptorSetLayout* pDescSetLayout = (MVKDescriptorSetLayout*)pCreateInfo->pSetLayouts[i]; pDescSetLayout->retain(); _descriptorSetLayouts.push_back(pDescSetLayout); - MVKShaderResourceBinding adjstdDSLRezOfsts = _pushConstantsMTLResourceIndexes; + MVKShaderResourceBinding adjstdDSLRezOfsts = _mtlResourceCounts; MVKShaderResourceBinding adjstdDSLRezCnts = pDescSetLayout->_mtlResourceCounts; if (pDescSetLayout->isUsingMetalArgumentBuffer()) { adjstdDSLRezOfsts.clearArgumentBufferResources(); adjstdDSLRezCnts.clearArgumentBufferResources(); } _dslMTLResourceIndexOffsets.push_back(adjstdDSLRezOfsts); - _pushConstantsMTLResourceIndexes += adjstdDSLRezCnts; + _mtlResourceCounts += adjstdDSLRezCnts; } - - // Add push constants - _pushConstants.reserve(pCreateInfo->pushConstantRangeCount); - for (uint32_t i = 0; i < pCreateInfo->pushConstantRangeCount; i++) { - _pushConstants.push_back(pCreateInfo->pPushConstantRanges[i]); - } - - // Set implicit buffer indices - // FIXME: Many of these are optional. We shouldn't set the ones that aren't - // present--or at least, we should move the ones that are down to avoid running over - // the limit of available buffers. But we can't know that until we compile the shaders. - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageCount; i++) { - _dynamicOffsetBufferIndex.stages[i] = _pushConstantsMTLResourceIndexes.stages[i].bufferIndex + 1; - _bufferSizeBufferIndex.stages[i] = _dynamicOffsetBufferIndex.stages[i] + 1; - _swizzleBufferIndex.stages[i] = _bufferSizeBufferIndex.stages[i] + 1; - _indirectParamsIndex.stages[i] = _swizzleBufferIndex.stages[i] + 1; - _outputBufferIndex.stages[i] = _indirectParamsIndex.stages[i] + 1; - if (i == kMVKShaderStageTessCtl) { - _tessCtlPatchOutputBufferIndex = _outputBufferIndex.stages[i] + 1; - _tessCtlLevelBufferIndex = _tessCtlPatchOutputBufferIndex + 1; - } - } - // Since we currently can't use multiview with tessellation or geometry shaders, - // to conserve the number of buffer bindings, use the same bindings for the - // view range buffer as for the indirect paramters buffer. - _viewRangeBufferIndex = _indirectParamsIndex; } MVKPipelineLayout::~MVKPipelineLayout() { @@ -175,9 +174,9 @@ MVKPipelineLayout::~MVKPipelineLayout() { #pragma mark MVKPipeline void MVKPipeline::bindPushConstants(MVKCommandEncoder* cmdEncoder) { - if (cmdEncoder) { - for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageCount; i++) { - cmdEncoder->getPushConstants(mvkVkShaderStageFlagBitsFromMVKShaderStage(MVKShaderStage(i)))->setMTLBufferIndex(_pushConstantsMTLResourceIndexes.stages[i].bufferIndex); + for (uint32_t stage = kMVKShaderStageVertex; stage < kMVKShaderStageCount; stage++) { + if (cmdEncoder && _stageUsesPushConstants[stage]) { + cmdEncoder->getPushConstants(mvkVkShaderStageFlagBitsFromMVKShaderStage(MVKShaderStage(stage)))->setMTLBufferIndex(_pushConstantsBufferIndex.stages[stage]); } } } @@ -205,9 +204,16 @@ void MVKPipeline::addMTLArgumentEncoders(MVKMTLFunction& mvkMTLFunc, MVKPipeline::MVKPipeline(MVKDevice* device, MVKPipelineCache* pipelineCache, MVKPipelineLayout* layout, MVKPipeline* parent) : MVKVulkanAPIDeviceObject(device), _pipelineCache(pipelineCache), - _pushConstantsMTLResourceIndexes(layout->getPushConstantBindings()), _fullImageViewSwizzle(mvkConfig().fullImageViewSwizzle), - _descriptorSetCount(layout->getDescriptorSetCount()) {} + _descriptorSetCount(layout->getDescriptorSetCount()) { + + // Establish descriptor counts and push constants use. + for (uint32_t stage = kMVKShaderStageVertex; stage < kMVKShaderStageCount; stage++) { + _descriptorBufferCounts.stages[stage] = layout->_mtlResourceCounts.stages[stage].bufferIndex; + _pushConstantsBufferIndex.stages[stage] = layout->_pushConstantsMTLResourceIndexes.stages[stage].bufferIndex; + _stageUsesPushConstants[stage] = layout->stageUsesPushConstants((MVKShaderStage)stage); + } + } #pragma mark - @@ -879,14 +885,14 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::newMTLTessRasterStageDescripto return plDesc; } -bool MVKGraphicsPipeline::verifyImplicitBuffer(bool needsBuffer, MVKShaderImplicitRezBinding& index, MVKShaderStage stage, const char* name, uint32_t reservedBuffers) { +bool MVKGraphicsPipeline::verifyImplicitBuffer(bool needsBuffer, MVKShaderImplicitRezBinding& index, MVKShaderStage stage, const char* name) { const char* stageNames[] = { "Vertex", "Tessellation control", "Tessellation evaluation", "Fragment" }; - if (needsBuffer && index.stages[stage] >= _device->_pMetalFeatures->maxPerStageBufferCount - reservedBuffers) { + if (needsBuffer && index.stages[stage] < _descriptorBufferCounts.stages[stage]) { setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "%s shader requires %s buffer, but there is no free slot to pass it.", stageNames[stage], name)); return false; } @@ -897,7 +903,6 @@ bool MVKGraphicsPipeline::verifyImplicitBuffer(bool needsBuffer, MVKShaderImplic bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig) { - uint32_t vbCnt = pCreateInfo->pVertexInputState->vertexBindingDescriptionCount; shaderConfig.options.entryPointStage = spv::ExecutionModelVertex; shaderConfig.options.entryPointName = _pVertexSS->pName; shaderConfig.options.mslOptions.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageVertex]; @@ -933,25 +938,25 @@ bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLRenderPipelineDescriptor* } // If we need the swizzle buffer and there's no place to put it, we're in serious trouble. - if (!verifyImplicitBuffer(_needsVertexSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageVertex, "swizzle", vbCnt)) { + if (!verifyImplicitBuffer(_needsVertexSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageVertex, "swizzle")) { return false; } // Ditto buffer size buffer. - if (!verifyImplicitBuffer(_needsVertexBufferSizeBuffer, _bufferSizeBufferIndex, kMVKShaderStageVertex, "buffer size", vbCnt)) { + if (!verifyImplicitBuffer(_needsVertexBufferSizeBuffer, _bufferSizeBufferIndex, kMVKShaderStageVertex, "buffer size")) { return false; } // Ditto dynamic offset buffer. - if (!verifyImplicitBuffer(_needsVertexDynamicOffsetBuffer, _dynamicOffsetBufferIndex, kMVKShaderStageVertex, "dynamic offset", vbCnt)) { + if (!verifyImplicitBuffer(_needsVertexDynamicOffsetBuffer, _dynamicOffsetBufferIndex, kMVKShaderStageVertex, "dynamic offset")) { return false; } // Ditto captured output buffer. - if (!verifyImplicitBuffer(_needsVertexOutputBuffer, _outputBufferIndex, kMVKShaderStageVertex, "output", vbCnt)) { + if (!verifyImplicitBuffer(_needsVertexOutputBuffer, _outputBufferIndex, kMVKShaderStageVertex, "output")) { return false; } - if (!verifyImplicitBuffer(_needsVertexOutputBuffer, _indirectParamsIndex, kMVKShaderStageVertex, "indirect parameters", vbCnt)) { + if (!verifyImplicitBuffer(_needsVertexOutputBuffer, _indirectParamsIndex, kMVKShaderStageVertex, "indirect parameters")) { return false; } - if (!verifyImplicitBuffer(_needsVertexViewRangeBuffer, _viewRangeBufferIndex, kMVKShaderStageVertex, "view range", vbCnt)) { + if (!verifyImplicitBuffer(_needsVertexViewRangeBuffer, _viewRangeBufferIndex, kMVKShaderStageVertex, "view range")) { return false; } return true; @@ -961,7 +966,6 @@ bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLRenderPipelineDescriptor* bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLComputePipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig) { - uint32_t vbCnt = pCreateInfo->pVertexInputState->vertexBindingDescriptionCount; shaderConfig.options.entryPointStage = spv::ExecutionModelVertex; shaderConfig.options.entryPointName = _pVertexSS->pName; shaderConfig.options.mslOptions.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageVertex]; @@ -1001,22 +1005,22 @@ bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLComputePipelineDescriptor addMTLArgumentEncoders(func, pCreateInfo, shaderConfig, kMVKShaderStageVertex); // If we need the swizzle buffer and there's no place to put it, we're in serious trouble. - if (!verifyImplicitBuffer(_needsVertexSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageVertex, "swizzle", vbCnt)) { + if (!verifyImplicitBuffer(_needsVertexSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageVertex, "swizzle")) { return false; } // Ditto buffer size buffer. - if (!verifyImplicitBuffer(_needsVertexBufferSizeBuffer, _bufferSizeBufferIndex, kMVKShaderStageVertex, "buffer size", vbCnt)) { + if (!verifyImplicitBuffer(_needsVertexBufferSizeBuffer, _bufferSizeBufferIndex, kMVKShaderStageVertex, "buffer size")) { return false; } // Ditto dynamic offset buffer. - if (!verifyImplicitBuffer(_needsVertexDynamicOffsetBuffer, _dynamicOffsetBufferIndex, kMVKShaderStageVertex, "dynamic offset", vbCnt)) { + if (!verifyImplicitBuffer(_needsVertexDynamicOffsetBuffer, _dynamicOffsetBufferIndex, kMVKShaderStageVertex, "dynamic offset")) { return false; } // Ditto captured output buffer. - if (!verifyImplicitBuffer(_needsVertexOutputBuffer, _outputBufferIndex, kMVKShaderStageVertex, "output", vbCnt)) { + if (!verifyImplicitBuffer(_needsVertexOutputBuffer, _outputBufferIndex, kMVKShaderStageVertex, "output")) { return false; } - if (!verifyImplicitBuffer(!shaderConfig.shaderInputs.empty(), _indirectParamsIndex, kMVKShaderStageVertex, "index", vbCnt)) { + if (!verifyImplicitBuffer(!shaderConfig.shaderInputs.empty(), _indirectParamsIndex, kMVKShaderStageVertex, "index")) { return false; } return true; @@ -1059,26 +1063,26 @@ bool MVKGraphicsPipeline::addTessCtlShaderToPipeline(MTLComputePipelineDescripto addMTLArgumentEncoders(func, pCreateInfo, shaderConfig, kMVKShaderStageTessCtl); - if (!verifyImplicitBuffer(_needsTessCtlSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageTessCtl, "swizzle", kMVKTessCtlNumReservedBuffers)) { + if (!verifyImplicitBuffer(_needsTessCtlSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageTessCtl, "swizzle")) { return false; } - if (!verifyImplicitBuffer(_needsTessCtlBufferSizeBuffer, _bufferSizeBufferIndex, kMVKShaderStageTessCtl, "buffer size", kMVKTessCtlNumReservedBuffers)) { + if (!verifyImplicitBuffer(_needsTessCtlBufferSizeBuffer, _bufferSizeBufferIndex, kMVKShaderStageTessCtl, "buffer size")) { return false; } - if (!verifyImplicitBuffer(_needsTessCtlDynamicOffsetBuffer, _dynamicOffsetBufferIndex, kMVKShaderStageTessCtl, "dynamic offset", kMVKTessCtlNumReservedBuffers)) { + if (!verifyImplicitBuffer(_needsTessCtlDynamicOffsetBuffer, _dynamicOffsetBufferIndex, kMVKShaderStageTessCtl, "dynamic offset")) { return false; } - if (!verifyImplicitBuffer(true, _indirectParamsIndex, kMVKShaderStageTessCtl, "indirect parameters", kMVKTessCtlNumReservedBuffers)) { + if (!verifyImplicitBuffer(true, _indirectParamsIndex, kMVKShaderStageTessCtl, "indirect parameters")) { return false; } - if (!verifyImplicitBuffer(_needsTessCtlOutputBuffer, _outputBufferIndex, kMVKShaderStageTessCtl, "per-vertex output", kMVKTessCtlNumReservedBuffers)) { + if (!verifyImplicitBuffer(_needsTessCtlOutputBuffer, _outputBufferIndex, kMVKShaderStageTessCtl, "per-vertex output")) { return false; } - if (_needsTessCtlPatchOutputBuffer && _tessCtlPatchOutputBufferIndex >= _device->_pMetalFeatures->maxPerStageBufferCount - kMVKTessCtlNumReservedBuffers) { + if (_needsTessCtlPatchOutputBuffer && _tessCtlPatchOutputBufferIndex < _descriptorBufferCounts.stages[kMVKShaderStageTessCtl]) { setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation control shader requires per-patch output buffer, but there is no free slot to pass it.")); return false; } - if (_tessCtlLevelBufferIndex >= _device->_pMetalFeatures->maxPerStageBufferCount - kMVKTessCtlNumReservedBuffers) { + if (_tessCtlLevelBufferIndex < _descriptorBufferCounts.stages[kMVKShaderStageTessCtl]) { setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation control shader requires tessellation level output buffer, but there is no free slot to pass it.")); return false; } @@ -1119,13 +1123,13 @@ bool MVKGraphicsPipeline::addTessEvalShaderToPipeline(MTLRenderPipelineDescripto _pFragmentSS = nullptr; } - if (!verifyImplicitBuffer(_needsTessEvalSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageTessEval, "swizzle", kMVKTessEvalNumReservedBuffers)) { + if (!verifyImplicitBuffer(_needsTessEvalSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageTessEval, "swizzle")) { return false; } - if (!verifyImplicitBuffer(_needsTessEvalBufferSizeBuffer, _bufferSizeBufferIndex, kMVKShaderStageTessEval, "buffer size", kMVKTessEvalNumReservedBuffers)) { + if (!verifyImplicitBuffer(_needsTessEvalBufferSizeBuffer, _bufferSizeBufferIndex, kMVKShaderStageTessEval, "buffer size")) { return false; } - if (!verifyImplicitBuffer(_needsTessEvalDynamicOffsetBuffer, _dynamicOffsetBufferIndex, kMVKShaderStageTessEval, "dynamic offset", kMVKTessEvalNumReservedBuffers)) { + if (!verifyImplicitBuffer(_needsTessEvalDynamicOffsetBuffer, _dynamicOffsetBufferIndex, kMVKShaderStageTessEval, "dynamic offset")) { return false; } return true; @@ -1171,16 +1175,16 @@ bool MVKGraphicsPipeline::addFragmentShaderToPipeline(MTLRenderPipelineDescripto addMTLArgumentEncoders(func, pCreateInfo, shaderConfig, kMVKShaderStageFragment); - if (!verifyImplicitBuffer(_needsFragmentSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageFragment, "swizzle", 0)) { + if (!verifyImplicitBuffer(_needsFragmentSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageFragment, "swizzle")) { return false; } - if (!verifyImplicitBuffer(_needsFragmentBufferSizeBuffer, _bufferSizeBufferIndex, kMVKShaderStageFragment, "buffer size", 0)) { + if (!verifyImplicitBuffer(_needsFragmentBufferSizeBuffer, _bufferSizeBufferIndex, kMVKShaderStageFragment, "buffer size")) { return false; } - if (!verifyImplicitBuffer(_needsFragmentDynamicOffsetBuffer, _dynamicOffsetBufferIndex, kMVKShaderStageFragment, "dynamic offset", 0)) { + if (!verifyImplicitBuffer(_needsFragmentDynamicOffsetBuffer, _dynamicOffsetBufferIndex, kMVKShaderStageFragment, "dynamic offset")) { return false; } - if (!verifyImplicitBuffer(_needsFragmentViewRangeBuffer, _viewRangeBufferIndex, kMVKShaderStageFragment, "view range", 0)) { + if (!verifyImplicitBuffer(_needsFragmentViewRangeBuffer, _viewRangeBufferIndex, kMVKShaderStageFragment, "view range")) { return false; } } @@ -1520,14 +1524,27 @@ void MVKGraphicsPipeline::initShaderConversionConfig(SPIRVToMSLConversionConfigu MVKPipelineLayout* layout = (MVKPipelineLayout*)pCreateInfo->layout; layout->populateShaderConversionConfig(shaderConfig); - _swizzleBufferIndex = layout->getSwizzleBufferIndex(); - _bufferSizeBufferIndex = layout->getBufferSizeBufferIndex(); - _dynamicOffsetBufferIndex = layout->getDynamicOffsetBufferIndex(); - _indirectParamsIndex = layout->getIndirectParamsIndex(); - _outputBufferIndex = layout->getOutputBufferIndex(); - _tessCtlPatchOutputBufferIndex = layout->getTessCtlPatchOutputBufferIndex(); - _tessCtlLevelBufferIndex = layout->getTessCtlLevelBufferIndex(); - _viewRangeBufferIndex = layout->getViewRangeBufferIndex(); + + // Set implicit buffer indices + // FIXME: Many of these are optional. We shouldn't set the ones that aren't + // present--or at least, we should move the ones that are down to avoid running over + // the limit of available buffers. But we can't know that until we compile the shaders. + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageCount; i++) { + MVKShaderStage stage = (MVKShaderStage)i; + _dynamicOffsetBufferIndex.stages[stage] = getImplicitBufferIndex(pCreateInfo, stage, 0); + _bufferSizeBufferIndex.stages[stage] = getImplicitBufferIndex(pCreateInfo, stage, 1); + _swizzleBufferIndex.stages[stage] = getImplicitBufferIndex(pCreateInfo, stage, 2); + _indirectParamsIndex.stages[stage] = getImplicitBufferIndex(pCreateInfo, stage, 3); + _outputBufferIndex.stages[stage] = getImplicitBufferIndex(pCreateInfo, stage, 4); + if (stage == kMVKShaderStageTessCtl) { + _tessCtlPatchOutputBufferIndex = getImplicitBufferIndex(pCreateInfo, stage, 5); + _tessCtlLevelBufferIndex = getImplicitBufferIndex(pCreateInfo, stage, 6); + } + } + // Since we currently can't use multiview with tessellation or geometry shaders, + // to conserve the number of buffer bindings, use the same bindings for the + // view range buffer as for the indirect paramters buffer. + _viewRangeBufferIndex = _indirectParamsIndex; MVKRenderPass* mvkRendPass = (MVKRenderPass*)pCreateInfo->renderPass; MVKRenderSubpass* mvkRenderSubpass = mvkRendPass->getSubpass(pCreateInfo->subpass); @@ -1572,6 +1589,19 @@ void MVKGraphicsPipeline::initShaderConversionConfig(SPIRVToMSLConversionConfigu shaderConfig.options.numTessControlPoints = reflectData.numControlPoints; } +uint32_t MVKGraphicsPipeline::getImplicitBufferIndex(const VkGraphicsPipelineCreateInfo* pCreateInfo, MVKShaderStage stage, uint32_t bufferIndexOffset) { + return _device->_pMetalFeatures->maxPerStageBufferCount - (getReservedBufferCount(pCreateInfo, stage) + bufferIndexOffset + 1); +} + +uint32_t MVKGraphicsPipeline::getReservedBufferCount(const VkGraphicsPipelineCreateInfo* pCreateInfo, MVKShaderStage stage) { + switch (stage) { + case kMVKShaderStageVertex: return pCreateInfo->pVertexInputState->vertexBindingDescriptionCount; + case kMVKShaderStageTessCtl: return kMVKTessCtlNumReservedBuffers; + case kMVKShaderStageTessEval: return kMVKTessEvalNumReservedBuffers; + default: return 0; + } +} + // Initializes the vertex attributes in a shader conversion configuration. void MVKGraphicsPipeline::addVertexInputToShaderConversionConfig(SPIRVToMSLConversionConfiguration& shaderConfig, const VkGraphicsPipelineCreateInfo* pCreateInfo) { @@ -1793,9 +1823,19 @@ MVKMTLFunction MVKComputePipeline::getMTLFunction(const VkComputePipelineCreateI MVKPipelineLayout* layout = (MVKPipelineLayout*)pCreateInfo->layout; layout->populateShaderConversionConfig(shaderConfig); - _swizzleBufferIndex = layout->getSwizzleBufferIndex(); - _bufferSizeBufferIndex = layout->getBufferSizeBufferIndex(); - _dynamicOffsetBufferIndex = layout->getDynamicOffsetBufferIndex(); + + // Set implicit buffer indices + // FIXME: Many of these are optional. We shouldn't set the ones that aren't + // present--or at least, we should move the ones that are down to avoid running over + // the limit of available buffers. But we can't know that until we compile the shaders. + for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageCount; i++) { + MVKShaderStage stage = (MVKShaderStage)i; + _dynamicOffsetBufferIndex.stages[stage] = getImplicitBufferIndex(0); + _bufferSizeBufferIndex.stages[stage] = getImplicitBufferIndex(1); + _swizzleBufferIndex.stages[stage] = getImplicitBufferIndex(2); + _indirectParamsIndex.stages[stage] = getImplicitBufferIndex(3); + } + shaderConfig.options.mslOptions.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageCompute]; shaderConfig.options.mslOptions.buffer_size_buffer_index = _bufferSizeBufferIndex.stages[kMVKShaderStageCompute]; shaderConfig.options.mslOptions.dynamic_offsets_buffer_index = _dynamicOffsetBufferIndex.stages[kMVKShaderStageCompute]; @@ -1814,6 +1854,10 @@ MVKMTLFunction MVKComputePipeline::getMTLFunction(const VkComputePipelineCreateI return func; } +uint32_t MVKComputePipeline::getImplicitBufferIndex(uint32_t bufferIndexOffset) { + return _device->_pMetalFeatures->maxPerStageBufferCount - (bufferIndexOffset + 1); +} + MVKComputePipeline::~MVKComputePipeline() { @synchronized (getMTLDevice()) { [_mtlPipelineState release];