diff --git a/Common/MVKOSExtensions.h b/Common/MVKOSExtensions.h index ff96991c..576eb403 100644 --- a/Common/MVKOSExtensions.h +++ b/Common/MVKOSExtensions.h @@ -76,6 +76,14 @@ uint64_t mvkGetTimestamp(); /** Returns the number of nanoseconds between each increment of the value returned by mvkGetTimestamp(). */ double mvkGetTimestampPeriod(); +/** + * Returns the number of nanoseconds elapsed between startTimestamp and endTimestamp, + * each of which should be a value returned by mvkGetTimestamp(). + * If endTimestamp is zero or not supplied, it is taken to be the current time. + * If startTimestamp is zero or not supplied, it is taken to be the time the app was initialized. + */ +uint64_t mvkGetElapsedNanoseconds(uint64_t startTimestamp = 0, uint64_t endTimestamp = 0); + /** * Returns the number of milliseconds elapsed between startTimestamp and endTimestamp, * each of which should be a value returned by mvkGetTimestamp(). diff --git a/Common/MVKOSExtensions.mm b/Common/MVKOSExtensions.mm index 672335d7..0f9643e1 100644 --- a/Common/MVKOSExtensions.mm +++ b/Common/MVKOSExtensions.mm @@ -46,9 +46,13 @@ uint64_t mvkGetTimestamp() { return mach_absolute_time() - _mvkTimestampBase; } double mvkGetTimestampPeriod() { return _mvkTimestampPeriod; } -double mvkGetElapsedMilliseconds(uint64_t startTimestamp, uint64_t endTimestamp) { +uint64_t mvkGetElapsedNanoseconds(uint64_t startTimestamp, uint64_t endTimestamp) { if (endTimestamp == 0) { endTimestamp = mvkGetTimestamp(); } - return (double)(endTimestamp - startTimestamp) * _mvkTimestampPeriod / 1e6; + return (endTimestamp - startTimestamp) * _mvkTimestampPeriod; +} + +double mvkGetElapsedMilliseconds(uint64_t startTimestamp, uint64_t endTimestamp) { + return mvkGetElapsedNanoseconds(startTimestamp, endTimestamp) / 1e6; } uint64_t mvkGetAbsoluteTime() { return mach_continuous_time() * _mvkMachTimebase.numer / _mvkMachTimebase.denom; } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h index e81f93df..825ae6b8 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h @@ -233,7 +233,7 @@ public: bool supportsDynamicState(VkDynamicState state); /** Returns whether this pipeline has tessellation shaders. */ - bool isTessellationPipeline() { return _pTessCtlSS && _pTessEvalSS && _tessInfo.patchControlPoints > 0; } + bool isTessellationPipeline() { return _tessInfo.patchControlPoints > 0; } /** Returns the number of input tessellation patch control points. */ uint32_t getInputControlPointCount() { return _tessInfo.patchControlPoints; } @@ -251,13 +251,13 @@ public: uint32_t getTessCtlLevelBufferIndex() { return _tessCtlLevelBufferIndex; } /** Returns the MTLComputePipelineState object for the vertex stage of a tessellated draw with no indices. */ - id getTessVertexStageState(); + id getTessVertexStageState() { return _mtlTessVertexStageState; } /** Returns the MTLComputePipelineState object for the vertex stage of a tessellated draw with 16-bit indices. */ - id getTessVertexStageIndex16State(); + id getTessVertexStageIndex16State() { return _mtlTessVertexStageIndex16State; } /** Returns the MTLComputePipelineState object for the vertex stage of a tessellated draw with 32-bit indices. */ - id getTessVertexStageIndex32State(); + id getTessVertexStageIndex32State() { return _mtlTessVertexStageIndex32State; } /** Returns the MTLComputePipelineState object for the tessellation control stage of a tessellated draw. */ id getTessControlStageState() { return _mtlTessControlStageState; } @@ -318,22 +318,24 @@ protected: id getOrCompilePipeline(MTLRenderPipelineDescriptor* plDesc, id& plState); id getOrCompilePipeline(MTLComputePipelineDescriptor* plDesc, id& plState, const char* compilerType); + bool compileTessVertexStageState(MTLComputePipelineDescriptor* vtxPLDesc, id* vtxFunctions, VkPipelineCreationFeedback* pVertexFB); + bool compileTessControlStageState(MTLComputePipelineDescriptor* tcPLDesc, VkPipelineCreationFeedback* pTessCtlFB); void initCustomSamplePositions(const VkGraphicsPipelineCreateInfo* pCreateInfo); - void initMTLRenderPipelineState(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData); + void initMTLRenderPipelineState(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, VkPipelineCreationFeedback* pPipelineFB, const VkPipelineShaderStageCreateInfo* pVertexSS, VkPipelineCreationFeedback* pVertexFB, const VkPipelineShaderStageCreateInfo* pTessCtlSS, VkPipelineCreationFeedback* pTessCtlFB, const VkPipelineShaderStageCreateInfo* pTessEvalSS, VkPipelineCreationFeedback* pTessEvalFB, const VkPipelineShaderStageCreateInfo* pFragmentSS, VkPipelineCreationFeedback* pFragmentFB); void initShaderConversionConfig(SPIRVToMSLConversionConfiguration& shaderConfig, const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData); void initReservedVertexAttributeBufferCount(const VkGraphicsPipelineCreateInfo* pCreateInfo); void addVertexInputToShaderConversionConfig(SPIRVToMSLConversionConfiguration& shaderConfig, const VkGraphicsPipelineCreateInfo* pCreateInfo); void addNextStageInputToShaderConversionConfig(SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderInputs& inputs); void addPrevStageOutputToShaderConversionConfig(SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderOutputs& outputs); - MTLRenderPipelineDescriptor* newMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData); - MTLComputePipelineDescriptor* newMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderConfig); - MTLComputePipelineDescriptor* newMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderConfig); - MTLRenderPipelineDescriptor* newMTLTessRasterStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderConfig); - bool addVertexShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig); - bool addVertexShaderToPipeline(MTLComputePipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderInputs& nextInputs); - bool addTessCtlShaderToPipeline(MTLComputePipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderOutputs& prevOutput, SPIRVShaderInputs& nextInputs); - bool addTessEvalShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderOutputs& prevOutput); - bool addFragmentShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderOutputs& prevOutput); + MTLRenderPipelineDescriptor* newMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, const VkPipelineShaderStageCreateInfo* pVertexSS, VkPipelineCreationFeedback* pVertexFB, const VkPipelineShaderStageCreateInfo* pFragmentSS, VkPipelineCreationFeedback* pFragmentFB); + MTLComputePipelineDescriptor* newMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderConfig, const VkPipelineShaderStageCreateInfo* pVertexSS, VkPipelineCreationFeedback* pVertexFB, const VkPipelineShaderStageCreateInfo* pTessCtlSS, id* vtxFunctions); + MTLComputePipelineDescriptor* newMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderConfig, const VkPipelineShaderStageCreateInfo* pTessCtlSS, VkPipelineCreationFeedback* pTessCtlFB, const VkPipelineShaderStageCreateInfo* pVertexSS, const VkPipelineShaderStageCreateInfo* pTessEvalSS); + MTLRenderPipelineDescriptor* newMTLTessRasterStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderConfig, const VkPipelineShaderStageCreateInfo* pTessEvalSS, VkPipelineCreationFeedback* pTessEvalFB, const VkPipelineShaderStageCreateInfo* pFragmentSS, VkPipelineCreationFeedback* pFragmentFB, const VkPipelineShaderStageCreateInfo* pTessCtlSS); + bool addVertexShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, const VkPipelineShaderStageCreateInfo* pVertexSS, VkPipelineCreationFeedback* pVertexFB, const VkPipelineShaderStageCreateInfo*& pFragmentSS); + bool addVertexShaderToPipeline(MTLComputePipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderInputs& nextInputs, const VkPipelineShaderStageCreateInfo* pVertexSS, VkPipelineCreationFeedback* pVertexFB, id* vtxFunctions); + bool addTessCtlShaderToPipeline(MTLComputePipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderOutputs& prevOutput, SPIRVShaderInputs& nextInputs, const VkPipelineShaderStageCreateInfo* pTessCtlSS, VkPipelineCreationFeedback* pTessCtlFB); + bool addTessEvalShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderOutputs& prevOutput, const VkPipelineShaderStageCreateInfo* pTessEvalSS, VkPipelineCreationFeedback* pTessEvalFB, const VkPipelineShaderStageCreateInfo*& pFragmentSS); + bool addFragmentShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderOutputs& prevOutput, const VkPipelineShaderStageCreateInfo* pFragmentSS, VkPipelineCreationFeedback* pFragmentFB); template bool addVertexInputToPipeline(T* inputDesc, const VkPipelineVertexInputStateCreateInfo* pVI, const SPIRVToMSLConversionConfiguration& shaderConfig); void adjustVertexInputForMultiview(MTLVertexDescriptor* inputDesc, const VkPipelineVertexInputStateCreateInfo* pVI, uint32_t viewCount, uint32_t oldViewCount = 1); @@ -346,6 +348,7 @@ protected: uint32_t getImplicitBufferIndex(MVKShaderStage stage, uint32_t bufferIndexOffset); MVKMTLFunction getMTLFunction(SPIRVToMSLConversionConfiguration& shaderConfig, const VkPipelineShaderStageCreateInfo* pShaderStage, + VkPipelineCreationFeedback* pStageFB, const char* pStageName); void markIfUsingPhysicalStorageBufferAddressesCapability(SPIRVToMSLConversionResultInfo& resultsInfo, MVKShaderStage stage); @@ -365,13 +368,6 @@ protected: MVKSmallVector _stagesUsingPhysicalStorageBufferAddressesCapability; std::unordered_map> _multiviewMTLPipelineStates; - const VkPipelineShaderStageCreateInfo* _pVertexSS = nullptr; - const VkPipelineShaderStageCreateInfo* _pTessCtlSS = nullptr; - const VkPipelineShaderStageCreateInfo* _pTessEvalSS = nullptr; - const VkPipelineShaderStageCreateInfo* _pFragmentSS = nullptr; - MTLComputePipelineDescriptor* _mtlTessVertexStageDesc = nil; - id _mtlTessVertexFunctions[3] = {nil, nil, nil}; - id _mtlTessVertexStageState = nil; id _mtlTessVertexStageIndex16State = nil; id _mtlTessVertexStageIndex32State = nil; @@ -446,7 +442,8 @@ public: ~MVKComputePipeline() override; protected: - MVKMTLFunction getMTLFunction(const VkComputePipelineCreateInfo* pCreateInfo); + MVKMTLFunction getMTLFunction(const VkComputePipelineCreateInfo* pCreateInfo, + VkPipelineCreationFeedback* pStageFB); uint32_t getImplicitBufferIndex(uint32_t bufferIndexOffset); id _mtlPipelineState; @@ -490,6 +487,7 @@ public: MVKShaderLibrary* getShaderLibrary(SPIRVToMSLConversionConfiguration* pContext, MVKShaderModule* shaderModule, MVKPipeline* pipeline, + VkPipelineCreationFeedback* pShaderFeedback = nullptr, uint64_t startTime = 0); /** Merges the contents of the specified number of pipeline caches into this cache. */ @@ -510,6 +508,7 @@ protected: MVKShaderLibrary* getShaderLibraryImpl(SPIRVToMSLConversionConfiguration* pContext, MVKShaderModule* shaderModule, MVKPipeline* pipeline, + VkPipelineCreationFeedback* pShaderFeedback, uint64_t startTime); VkResult writeDataImpl(size_t* pDataSize, void* pData); VkResult mergePipelineCachesImpl(uint32_t srcCacheCount, const VkPipelineCache* pSrcCaches); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm index a9fc1ec1..75ebe872 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm @@ -337,42 +337,54 @@ bool MVKGraphicsPipeline::supportsDynamicState(VkDynamicState state) { static const char vtxCompilerType[] = "Vertex stage pipeline for tessellation"; -id MVKGraphicsPipeline::getTessVertexStageState() { - MTLComputePipelineDescriptor* plDesc = [_mtlTessVertexStageDesc copy]; // temp retain a copy to be thread-safe. - plDesc.computeFunction = _mtlTessVertexFunctions[0]; - id plState = getOrCompilePipeline(plDesc, _mtlTessVertexStageState, vtxCompilerType); - [plDesc release]; // temp release - return plState; -} +bool MVKGraphicsPipeline::compileTessVertexStageState(MTLComputePipelineDescriptor* vtxPLDesc, + id* vtxFunctions, + VkPipelineCreationFeedback* pVertexFB) { + uint64_t startTime = 0; + if (pVertexFB) { + startTime = mvkGetTimestamp(); + } + vtxPLDesc.computeFunction = vtxFunctions[0]; + bool res = !!getOrCompilePipeline(vtxPLDesc, _mtlTessVertexStageState, vtxCompilerType); -id MVKGraphicsPipeline::getTessVertexStageIndex16State() { - MTLComputePipelineDescriptor* plDesc = [_mtlTessVertexStageDesc copy]; // temp retain a copy to be thread-safe. - plDesc.computeFunction = _mtlTessVertexFunctions[1]; - plDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16; + vtxPLDesc.computeFunction = vtxFunctions[1]; + vtxPLDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16; for (uint32_t i = 0; i < 31; i++) { - MTLBufferLayoutDescriptor* blDesc = plDesc.stageInputDescriptor.layouts[i]; + MTLBufferLayoutDescriptor* blDesc = vtxPLDesc.stageInputDescriptor.layouts[i]; if (blDesc.stepFunction == MTLStepFunctionThreadPositionInGridX) { blDesc.stepFunction = MTLStepFunctionThreadPositionInGridXIndexed; } } - id plState = getOrCompilePipeline(plDesc, _mtlTessVertexStageIndex16State, vtxCompilerType); - [plDesc release]; // temp release - return plState; + res |= !!getOrCompilePipeline(vtxPLDesc, _mtlTessVertexStageIndex16State, vtxCompilerType); + + vtxPLDesc.computeFunction = vtxFunctions[2]; + vtxPLDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32; + res |= !!getOrCompilePipeline(vtxPLDesc, _mtlTessVertexStageIndex32State, vtxCompilerType); + + if (pVertexFB) { + if (!res) { + // Compilation of the shader will have enabled the flag, so I need to turn it off. + mvkDisableFlags(pVertexFB->flags, VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT); + } + pVertexFB->duration += mvkGetElapsedNanoseconds(startTime); + } + return res; } -id MVKGraphicsPipeline::getTessVertexStageIndex32State() { - MTLComputePipelineDescriptor* plDesc = [_mtlTessVertexStageDesc copy]; // temp retain a copy to be thread-safe. - plDesc.computeFunction = _mtlTessVertexFunctions[2]; - plDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32; - for (uint32_t i = 0; i < 31; i++) { - MTLBufferLayoutDescriptor* blDesc = plDesc.stageInputDescriptor.layouts[i]; - if (blDesc.stepFunction == MTLStepFunctionThreadPositionInGridX) { - blDesc.stepFunction = MTLStepFunctionThreadPositionInGridXIndexed; - } - } - id plState = getOrCompilePipeline(plDesc, _mtlTessVertexStageIndex32State, vtxCompilerType); - [plDesc release]; // temp release - return plState; +bool MVKGraphicsPipeline::compileTessControlStageState(MTLComputePipelineDescriptor* tcPLDesc, + VkPipelineCreationFeedback* pTessCtlFB) { + uint64_t startTime = 0; + if (pTessCtlFB) { + startTime = mvkGetTimestamp(); + } + bool res = !!getOrCompilePipeline(tcPLDesc, _mtlTessControlStageState, "Tessellation control"); + if (pTessCtlFB) { + if (!res) { + mvkDisableFlags(pTessCtlFB->flags, VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT); + } + pTessCtlFB->duration += mvkGetElapsedNanoseconds(startTime); + } + return res; } @@ -407,26 +419,78 @@ MVKGraphicsPipeline::MVKGraphicsPipeline(MVKDevice* device, _isRasterizing = !isRasterizationDisabled(pCreateInfo); _isRasterizingColor = _isRasterizing && mvkHasColorAttachments(pRendInfo); - // Get the tessellation shaders, if present. Do this now, because we need to extract + const VkPipelineCreationFeedbackCreateInfo* pFeedbackInfo = nullptr; + for (const auto* next = (VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) { + switch (next->sType) { + case VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO: + pFeedbackInfo = (VkPipelineCreationFeedbackCreateInfo*)next; + break; + default: + break; + } + } + + // Initialize feedback. The VALID bit must be initialized, either set or cleared. + // We'll set the VALID bits later, after successful compilation. + VkPipelineCreationFeedback* pPipelineFB = nullptr; + if (pFeedbackInfo) { + pPipelineFB = pFeedbackInfo->pPipelineCreationFeedback; + // n.b. Do *NOT* use mvkClear(). That would also clear the sType and pNext fields. + pPipelineFB->flags = 0; + pPipelineFB->duration = 0; + for (uint32_t i = 0; i < pFeedbackInfo->pipelineStageCreationFeedbackCount; ++i) { + pFeedbackInfo->pPipelineStageCreationFeedbacks[i].flags = 0; + pFeedbackInfo->pPipelineStageCreationFeedbacks[i].duration = 0; + } + } + + // Get the shader stages. Do this now, because we need to extract // reflection data from them that informs everything else. + const VkPipelineShaderStageCreateInfo* pVertexSS = nullptr; + const VkPipelineShaderStageCreateInfo* pTessCtlSS = nullptr; + const VkPipelineShaderStageCreateInfo* pTessEvalSS = nullptr; + const VkPipelineShaderStageCreateInfo* pFragmentSS = nullptr; + VkPipelineCreationFeedback* pVertexFB = nullptr; + VkPipelineCreationFeedback* pTessCtlFB = nullptr; + VkPipelineCreationFeedback* pTessEvalFB = nullptr; + VkPipelineCreationFeedback* pFragmentFB = nullptr; for (uint32_t i = 0; i < pCreateInfo->stageCount; i++) { const auto* pSS = &pCreateInfo->pStages[i]; - if (pSS->stage == VK_SHADER_STAGE_VERTEX_BIT) { - _pVertexSS = pSS; - } else if (pSS->stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) { - _pTessCtlSS = pSS; - } else if (pSS->stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) { - _pTessEvalSS = pSS; - } else if (pSS->stage == VK_SHADER_STAGE_FRAGMENT_BIT) { - _pFragmentSS = pSS; + switch (pSS->stage) { + case VK_SHADER_STAGE_VERTEX_BIT: + pVertexSS = pSS; + if (pFeedbackInfo && pFeedbackInfo->pPipelineStageCreationFeedbacks) { + pVertexFB = &pFeedbackInfo->pPipelineStageCreationFeedbacks[i]; + } + break; + case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: + pTessCtlSS = pSS; + if (pFeedbackInfo && pFeedbackInfo->pPipelineStageCreationFeedbacks) { + pTessCtlFB = &pFeedbackInfo->pPipelineStageCreationFeedbacks[i]; + } + break; + case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: + pTessEvalSS = pSS; + if (pFeedbackInfo && pFeedbackInfo->pPipelineStageCreationFeedbacks) { + pTessEvalFB = &pFeedbackInfo->pPipelineStageCreationFeedbacks[i]; + } + break; + case VK_SHADER_STAGE_FRAGMENT_BIT: + pFragmentSS = pSS; + if (pFeedbackInfo && pFeedbackInfo->pPipelineStageCreationFeedbacks) { + pFragmentFB = &pFeedbackInfo->pPipelineStageCreationFeedbacks[i]; + } + break; + default: + break; } } // Get the tessellation parameters from the shaders. SPIRVTessReflectionData reflectData; std::string reflectErrorLog; - if (_pTessCtlSS && _pTessEvalSS) { - if (!getTessReflectionData(((MVKShaderModule*)_pTessCtlSS->module)->getSPIRV(), _pTessCtlSS->pName, ((MVKShaderModule*)_pTessEvalSS->module)->getSPIRV(), _pTessEvalSS->pName, reflectData, reflectErrorLog) ) { + if (pTessCtlSS && pTessEvalSS) { + if (!getTessReflectionData(((MVKShaderModule*)pTessCtlSS->module)->getSPIRV(), pTessCtlSS->pName, ((MVKShaderModule*)pTessEvalSS->module)->getSPIRV(), pTessEvalSS->pName, reflectData, reflectErrorLog) ) { setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to reflect tessellation shaders: %s", reflectErrorLog.c_str())); return; } @@ -439,10 +503,10 @@ MVKGraphicsPipeline::MVKGraphicsPipeline(MVKDevice* device, // Tessellation - must ignore allowed bad pTessellationState pointer if not tess pipeline _outputControlPointCount = reflectData.numControlPoints; - mvkSetOrClear(&_tessInfo, (_pTessCtlSS && _pTessEvalSS) ? pCreateInfo->pTessellationState : nullptr); + mvkSetOrClear(&_tessInfo, (pTessCtlSS && pTessEvalSS) ? pCreateInfo->pTessellationState : nullptr); // Render pipeline state. Do this as early as possible, to fail fast if pipeline requires a fail on cache-miss. - initMTLRenderPipelineState(pCreateInfo, reflectData); + initMTLRenderPipelineState(pCreateInfo, reflectData, pPipelineFB, pVertexSS, pVertexFB, pTessCtlSS, pTessCtlFB, pTessEvalSS, pTessEvalFB, pFragmentSS, pFragmentFB); if ( !_hasValidMTLPipelineStates ) { return; } // Track dynamic state @@ -564,20 +628,33 @@ void MVKGraphicsPipeline::initCustomSamplePositions(const VkGraphicsPipelineCrea } // Constructs the underlying Metal render pipeline. -void MVKGraphicsPipeline::initMTLRenderPipelineState(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData) { +void MVKGraphicsPipeline::initMTLRenderPipelineState(const VkGraphicsPipelineCreateInfo* pCreateInfo, + const SPIRVTessReflectionData& reflectData, + VkPipelineCreationFeedback* pPipelineFB, + const VkPipelineShaderStageCreateInfo* pVertexSS, + VkPipelineCreationFeedback* pVertexFB, + const VkPipelineShaderStageCreateInfo* pTessCtlSS, + VkPipelineCreationFeedback* pTessCtlFB, + const VkPipelineShaderStageCreateInfo* pTessEvalSS, + VkPipelineCreationFeedback* pTessEvalFB, + const VkPipelineShaderStageCreateInfo* pFragmentSS, + VkPipelineCreationFeedback* pFragmentFB) { _mtlTessVertexStageState = nil; _mtlTessVertexStageIndex16State = nil; _mtlTessVertexStageIndex32State = nil; _mtlTessControlStageState = nil; _mtlPipelineState = nil; - _mtlTessVertexStageDesc = nil; - for (uint32_t i = 0; i < 3; i++) { _mtlTessVertexFunctions[i] = nil; } + + uint64_t pipelineStart = 0; + if (pPipelineFB) { + pipelineStart = mvkGetTimestamp(); + } if (isUsingMetalArgumentBuffers()) { _descriptorBindingUse.resize(_descriptorSetCount); } if (isUsingPipelineStageMetalArgumentBuffers()) { _mtlArgumentEncoders.resize(_descriptorSetCount); } if (!isTessellationPipeline()) { - MTLRenderPipelineDescriptor* plDesc = newMTLRenderPipelineDescriptor(pCreateInfo, reflectData); // temp retain + MTLRenderPipelineDescriptor* plDesc = newMTLRenderPipelineDescriptor(pCreateInfo, reflectData, pVertexSS, pVertexFB, pFragmentSS, pFragmentFB); // temp retain if (plDesc) { const VkPipelineRenderingCreateInfo* pRendInfo = getRenderingCreateInfo(pCreateInfo); if (pRendInfo && mvkIsMultiview(pRendInfo->viewMask)) { @@ -613,30 +690,45 @@ void MVKGraphicsPipeline::initMTLRenderPipelineState(const VkGraphicsPipelineCre } } else { // In this case, we need to create three render pipelines. But, the way Metal handles - // index buffers for compute stage-in means we have to defer creation of stage 1 until - // draw time. In the meantime, we'll create and retain a descriptor for it. + // index buffers for compute stage-in means we have to create three pipelines for + // stage 1 (five pipelines in total). SPIRVToMSLConversionConfiguration shaderConfig; initShaderConversionConfig(shaderConfig, pCreateInfo, reflectData); - _mtlTessVertexStageDesc = newMTLTessVertexStageDescriptor(pCreateInfo, reflectData, shaderConfig); // retained - MTLComputePipelineDescriptor* tcPLDesc = newMTLTessControlStageDescriptor(pCreateInfo, reflectData, shaderConfig); // temp retained - MTLRenderPipelineDescriptor* rastPLDesc = newMTLTessRasterStageDescriptor(pCreateInfo, reflectData, shaderConfig); // temp retained - if (_mtlTessVertexStageDesc && tcPLDesc && rastPLDesc) { - if (getOrCompilePipeline(tcPLDesc, _mtlTessControlStageState, "Tessellation control")) { - getOrCompilePipeline(rastPLDesc, _mtlPipelineState); + id vtxFunctions[3] = { nil }; + MTLComputePipelineDescriptor* vtxPLDesc = newMTLTessVertexStageDescriptor(pCreateInfo, reflectData, shaderConfig, pVertexSS, pVertexFB, pTessCtlSS, vtxFunctions); // temp retained + MTLComputePipelineDescriptor* tcPLDesc = newMTLTessControlStageDescriptor(pCreateInfo, reflectData, shaderConfig, pTessCtlSS, pTessCtlFB, pVertexSS, pTessEvalSS); // temp retained + MTLRenderPipelineDescriptor* rastPLDesc = newMTLTessRasterStageDescriptor(pCreateInfo, reflectData, shaderConfig, pTessEvalSS, pTessEvalFB, pFragmentSS, pFragmentFB, pTessCtlSS); // temp retained + if (vtxPLDesc && tcPLDesc && rastPLDesc) { + if (compileTessVertexStageState(vtxPLDesc, vtxFunctions, pVertexFB)) { + if (compileTessControlStageState(tcPLDesc, pTessCtlFB)) { + getOrCompilePipeline(rastPLDesc, _mtlPipelineState); + } } } else { _hasValidMTLPipelineStates = false; } + [vtxPLDesc release]; // temp release [tcPLDesc release]; // temp release [rastPLDesc release]; // temp release } + + if (pPipelineFB) { + if ( _hasValidMTLPipelineStates ) { + mvkEnableFlags(pPipelineFB->flags, VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT); + } + pPipelineFB->duration = mvkGetElapsedNanoseconds(pipelineStart); + } } // Returns a retained MTLRenderPipelineDescriptor constructed from this instance, or nil if an error occurs. // It is the responsibility of the caller to release the returned descriptor. MTLRenderPipelineDescriptor* MVKGraphicsPipeline::newMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, - const SPIRVTessReflectionData& reflectData) { + const SPIRVTessReflectionData& reflectData, + const VkPipelineShaderStageCreateInfo* pVertexSS, + VkPipelineCreationFeedback* pVertexFB, + const VkPipelineShaderStageCreateInfo* pFragmentSS, + VkPipelineCreationFeedback* pFragmentFB) { SPIRVToMSLConversionConfiguration shaderConfig; initShaderConversionConfig(shaderConfig, pCreateInfo, reflectData); @@ -644,20 +736,20 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::newMTLRenderPipelineDescriptor SPIRVShaderOutputs vtxOutputs; std::string errorLog; - if (!getShaderOutputs(((MVKShaderModule*)_pVertexSS->module)->getSPIRV(), spv::ExecutionModelVertex, _pVertexSS->pName, vtxOutputs, errorLog) ) { + if (!getShaderOutputs(((MVKShaderModule*)pVertexSS->module)->getSPIRV(), spv::ExecutionModelVertex, pVertexSS->pName, vtxOutputs, errorLog) ) { setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to get vertex outputs: %s", errorLog.c_str())); return nil; } // Add shader stages. Compile vertex shader before others just in case conversion changes anything...like rasterizaion disable. - if (!addVertexShaderToPipeline(plDesc, pCreateInfo, shaderConfig)) { return nil; } + if (!addVertexShaderToPipeline(plDesc, pCreateInfo, shaderConfig, pVertexSS, pVertexFB, pFragmentSS)) { return nil; } // Vertex input // This needs to happen before compiling the fragment shader, or we'll lose information on vertex attributes. if (!addVertexInputToPipeline(plDesc.vertexDescriptor, pCreateInfo->pVertexInputState, shaderConfig)) { return nil; } // Fragment shader - only add if rasterization is enabled - if (!addFragmentShaderToPipeline(plDesc, pCreateInfo, shaderConfig, vtxOutputs)) { return nil; } + if (!addFragmentShaderToPipeline(plDesc, pCreateInfo, shaderConfig, vtxOutputs, pFragmentSS, pFragmentFB)) { return nil; } // Output addFragmentOutputToPipeline(plDesc, pCreateInfo); @@ -673,13 +765,17 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::newMTLRenderPipelineDescriptor // Returns a retained MTLComputePipelineDescriptor for the vertex stage of a tessellated draw constructed from this instance, or nil if an error occurs. // It is the responsibility of the caller to release the returned descriptor. MTLComputePipelineDescriptor* MVKGraphicsPipeline::newMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, - const SPIRVTessReflectionData& reflectData, - SPIRVToMSLConversionConfiguration& shaderConfig) { + const SPIRVTessReflectionData& reflectData, + SPIRVToMSLConversionConfiguration& shaderConfig, + const VkPipelineShaderStageCreateInfo* pVertexSS, + VkPipelineCreationFeedback* pVertexFB, + const VkPipelineShaderStageCreateInfo* pTessCtlSS, + id* vtxFunctions) { MTLComputePipelineDescriptor* plDesc = [MTLComputePipelineDescriptor new]; // retained SPIRVShaderInputs tcInputs; std::string errorLog; - if (!getShaderInputs(((MVKShaderModule*)_pTessCtlSS->module)->getSPIRV(), spv::ExecutionModelTessellationControl, _pTessCtlSS->pName, tcInputs, errorLog) ) { + if (!getShaderInputs(((MVKShaderModule*)pTessCtlSS->module)->getSPIRV(), spv::ExecutionModelTessellationControl, pTessCtlSS->pName, tcInputs, errorLog) ) { setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to get tessellation control inputs: %s", errorLog.c_str())); return nil; } @@ -691,7 +787,7 @@ MTLComputePipelineDescriptor* MVKGraphicsPipeline::newMTLTessVertexStageDescript }), tcInputs.end()); // Add shader stages. - if (!addVertexShaderToPipeline(plDesc, pCreateInfo, shaderConfig, tcInputs)) { return nil; } + if (!addVertexShaderToPipeline(plDesc, pCreateInfo, shaderConfig, tcInputs, pVertexSS, pVertexFB, vtxFunctions)) { return nil; } // Vertex input plDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor]; @@ -811,17 +907,21 @@ static MTLVertexFormat mvkAdjustFormatVectorToSize(MTLVertexFormat format, uint3 // It is the responsibility of the caller to release the returned descriptor. MTLComputePipelineDescriptor* MVKGraphicsPipeline::newMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, - SPIRVToMSLConversionConfiguration& shaderConfig) { + SPIRVToMSLConversionConfiguration& shaderConfig, + const VkPipelineShaderStageCreateInfo* pTessCtlSS, + VkPipelineCreationFeedback* pTessCtlFB, + const VkPipelineShaderStageCreateInfo* pVertexSS, + const VkPipelineShaderStageCreateInfo* pTessEvalSS) { MTLComputePipelineDescriptor* plDesc = [MTLComputePipelineDescriptor new]; // retained SPIRVShaderOutputs vtxOutputs; SPIRVShaderInputs teInputs; std::string errorLog; - if (!getShaderOutputs(((MVKShaderModule*)_pVertexSS->module)->getSPIRV(), spv::ExecutionModelVertex, _pVertexSS->pName, vtxOutputs, errorLog) ) { + if (!getShaderOutputs(((MVKShaderModule*)pVertexSS->module)->getSPIRV(), spv::ExecutionModelVertex, pVertexSS->pName, vtxOutputs, errorLog) ) { setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to get vertex outputs: %s", errorLog.c_str())); return nil; } - if (!getShaderInputs(((MVKShaderModule*)_pTessEvalSS->module)->getSPIRV(), spv::ExecutionModelTessellationEvaluation, _pTessEvalSS->pName, teInputs, errorLog) ) { + if (!getShaderInputs(((MVKShaderModule*)pTessEvalSS->module)->getSPIRV(), spv::ExecutionModelTessellationEvaluation, pTessEvalSS->pName, teInputs, errorLog) ) { setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to get tessellation evaluation inputs: %s", errorLog.c_str())); return nil; } @@ -833,7 +933,7 @@ MTLComputePipelineDescriptor* MVKGraphicsPipeline::newMTLTessControlStageDescrip }), teInputs.end()); // Add shader stages. - if (!addTessCtlShaderToPipeline(plDesc, pCreateInfo, shaderConfig, vtxOutputs, teInputs)) { + if (!addTessCtlShaderToPipeline(plDesc, pCreateInfo, shaderConfig, vtxOutputs, teInputs, pTessCtlSS, pTessCtlFB)) { [plDesc release]; return nil; } @@ -850,29 +950,34 @@ MTLComputePipelineDescriptor* MVKGraphicsPipeline::newMTLTessControlStageDescrip // It is the responsibility of the caller to release the returned descriptor. MTLRenderPipelineDescriptor* MVKGraphicsPipeline::newMTLTessRasterStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, - SPIRVToMSLConversionConfiguration& shaderConfig) { + SPIRVToMSLConversionConfiguration& shaderConfig, + const VkPipelineShaderStageCreateInfo* pTessEvalSS, + VkPipelineCreationFeedback* pTessEvalFB, + const VkPipelineShaderStageCreateInfo* pFragmentSS, + VkPipelineCreationFeedback* pFragmentFB, + const VkPipelineShaderStageCreateInfo* pTessCtlSS) { MTLRenderPipelineDescriptor* plDesc = [MTLRenderPipelineDescriptor new]; // retained SPIRVShaderOutputs tcOutputs, teOutputs; SPIRVShaderInputs teInputs; std::string errorLog; - if (!getShaderOutputs(((MVKShaderModule*)_pTessCtlSS->module)->getSPIRV(), spv::ExecutionModelTessellationControl, _pTessCtlSS->pName, tcOutputs, errorLog) ) { + if (!getShaderOutputs(((MVKShaderModule*)pTessCtlSS->module)->getSPIRV(), spv::ExecutionModelTessellationControl, pTessCtlSS->pName, tcOutputs, errorLog) ) { setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to get tessellation control outputs: %s", errorLog.c_str())); return nil; } - if (!getShaderOutputs(((MVKShaderModule*)_pTessEvalSS->module)->getSPIRV(), spv::ExecutionModelTessellationEvaluation, _pTessEvalSS->pName, teOutputs, errorLog) ) { + if (!getShaderOutputs(((MVKShaderModule*)pTessEvalSS->module)->getSPIRV(), spv::ExecutionModelTessellationEvaluation, pTessEvalSS->pName, teOutputs, errorLog) ) { setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to get tessellation evaluation outputs: %s", errorLog.c_str())); return nil; } // Add shader stages. Compile tessellation evaluation shader before others just in case conversion changes anything...like rasterizaion disable. - if (!addTessEvalShaderToPipeline(plDesc, pCreateInfo, shaderConfig, tcOutputs)) { + if (!addTessEvalShaderToPipeline(plDesc, pCreateInfo, shaderConfig, tcOutputs, pTessEvalSS, pTessEvalFB, pFragmentSS)) { [plDesc release]; return nil; } // Fragment shader - only add if rasterization is enabled - if (!addFragmentShaderToPipeline(plDesc, pCreateInfo, shaderConfig, teOutputs)) { + if (!addFragmentShaderToPipeline(plDesc, pCreateInfo, shaderConfig, teOutputs, pFragmentSS, pFragmentFB)) { [plDesc release]; return nil; } @@ -903,9 +1008,12 @@ bool MVKGraphicsPipeline::verifyImplicitBuffer(bool needsBuffer, MVKShaderImplic // Adds a vertex shader to the pipeline description. bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, - SPIRVToMSLConversionConfiguration& shaderConfig) { + SPIRVToMSLConversionConfiguration& shaderConfig, + const VkPipelineShaderStageCreateInfo* pVertexSS, + VkPipelineCreationFeedback* pVertexFB, + const VkPipelineShaderStageCreateInfo*& pFragmentSS) { shaderConfig.options.entryPointStage = spv::ExecutionModelVertex; - shaderConfig.options.entryPointName = _pVertexSS->pName; + shaderConfig.options.entryPointName = pVertexSS->pName; shaderConfig.options.mslOptions.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageVertex]; shaderConfig.options.mslOptions.indirect_params_buffer_index = _indirectParamsIndex.stages[kMVKShaderStageVertex]; shaderConfig.options.mslOptions.shader_output_buffer_index = _outputBufferIndex.stages[kMVKShaderStageVertex]; @@ -916,7 +1024,7 @@ bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLRenderPipelineDescriptor* shaderConfig.options.mslOptions.disable_rasterization = !_isRasterizing; addVertexInputToShaderConversionConfig(shaderConfig, pCreateInfo); - MVKMTLFunction func = getMTLFunction(shaderConfig, _pVertexSS, "Vertex"); + MVKMTLFunction func = getMTLFunction(shaderConfig, pVertexSS, pVertexFB, "Vertex"); id mtlFunc = func.getMTLFunction(); plDesc.vertexFunction = mtlFunc; if ( !mtlFunc ) { return false; } @@ -933,7 +1041,7 @@ bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLRenderPipelineDescriptor* addMTLArgumentEncoders(func, pCreateInfo, shaderConfig, kMVKShaderStageVertex); if (funcRslts.isRasterizationDisabled) { - _pFragmentSS = nullptr; + pFragmentSS = nullptr; } // If we need the swizzle buffer and there's no place to put it, we're in serious trouble. @@ -965,9 +1073,12 @@ bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLRenderPipelineDescriptor* bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLComputePipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, - SPIRVShaderInputs& tcInputs) { + SPIRVShaderInputs& tcInputs, + const VkPipelineShaderStageCreateInfo* pVertexSS, + VkPipelineCreationFeedback* pVertexFB, + id* vtxFunctions) { shaderConfig.options.entryPointStage = spv::ExecutionModelVertex; - shaderConfig.options.entryPointName = _pVertexSS->pName; + shaderConfig.options.entryPointName = pVertexSS->pName; shaderConfig.options.mslOptions.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageVertex]; shaderConfig.options.mslOptions.shader_index_buffer_index = _indirectParamsIndex.stages[kMVKShaderStageVertex]; shaderConfig.options.mslOptions.shader_output_buffer_index = _outputBufferIndex.stages[kMVKShaderStageVertex]; @@ -988,9 +1099,9 @@ bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLComputePipelineDescriptor MVKMTLFunction func; for (uint32_t i = 0; i < sizeof(indexTypes)/sizeof(indexTypes[0]); i++) { shaderConfig.options.mslOptions.vertex_index_type = indexTypes[i]; - func = getMTLFunction(shaderConfig, _pVertexSS, "Vertex"); + func = getMTLFunction(shaderConfig, pVertexSS, pVertexFB, "Vertex"); id mtlFunc = func.getMTLFunction(); - _mtlTessVertexFunctions[i] = [mtlFunc retain]; + vtxFunctions[i] = mtlFunc; // not retained if ( !mtlFunc ) { return false; } auto& funcRslts = func.shaderConversionResults; @@ -1029,9 +1140,11 @@ bool MVKGraphicsPipeline::addTessCtlShaderToPipeline(MTLComputePipelineDescripto const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderOutputs& vtxOutputs, - SPIRVShaderInputs& teInputs) { + SPIRVShaderInputs& teInputs, + const VkPipelineShaderStageCreateInfo* pTessCtlSS, + VkPipelineCreationFeedback* pTessCtlFB) { shaderConfig.options.entryPointStage = spv::ExecutionModelTessellationControl; - shaderConfig.options.entryPointName = _pTessCtlSS->pName; + shaderConfig.options.entryPointName = pTessCtlSS->pName; shaderConfig.options.mslOptions.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageTessCtl]; shaderConfig.options.mslOptions.indirect_params_buffer_index = _indirectParamsIndex.stages[kMVKShaderStageTessCtl]; shaderConfig.options.mslOptions.shader_input_buffer_index = getMetalBufferIndexForVertexAttributeBinding(kMVKTessCtlInputBufferBinding); @@ -1042,11 +1155,11 @@ bool MVKGraphicsPipeline::addTessCtlShaderToPipeline(MTLComputePipelineDescripto shaderConfig.options.mslOptions.dynamic_offsets_buffer_index = _dynamicOffsetBufferIndex.stages[kMVKShaderStageTessCtl]; shaderConfig.options.mslOptions.capture_output_to_buffer = true; shaderConfig.options.mslOptions.multi_patch_workgroup = true; - shaderConfig.options.mslOptions.fixed_subgroup_size = mvkIsAnyFlagEnabled(_pTessCtlSS->flags, VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT) ? 0 : _device->_pMetalFeatures->maxSubgroupSize; + shaderConfig.options.mslOptions.fixed_subgroup_size = mvkIsAnyFlagEnabled(pTessCtlSS->flags, VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT) ? 0 : _device->_pMetalFeatures->maxSubgroupSize; addPrevStageOutputToShaderConversionConfig(shaderConfig, vtxOutputs); addNextStageInputToShaderConversionConfig(shaderConfig, teInputs); - MVKMTLFunction func = getMTLFunction(shaderConfig, _pTessCtlSS, "Tessellation control"); + MVKMTLFunction func = getMTLFunction(shaderConfig, pTessCtlSS, pTessCtlFB, "Tessellation control"); id mtlFunc = func.getMTLFunction(); if ( !mtlFunc ) { return false; } plDesc.computeFunction = mtlFunc; @@ -1091,9 +1204,12 @@ bool MVKGraphicsPipeline::addTessCtlShaderToPipeline(MTLComputePipelineDescripto bool MVKGraphicsPipeline::addTessEvalShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, - SPIRVShaderOutputs& tcOutputs) { + SPIRVShaderOutputs& tcOutputs, + const VkPipelineShaderStageCreateInfo* pTessEvalSS, + VkPipelineCreationFeedback* pTessEvalFB, + const VkPipelineShaderStageCreateInfo*& pFragmentSS) { shaderConfig.options.entryPointStage = spv::ExecutionModelTessellationEvaluation; - shaderConfig.options.entryPointName = _pTessEvalSS->pName; + shaderConfig.options.entryPointName = pTessEvalSS->pName; shaderConfig.options.mslOptions.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageTessEval]; shaderConfig.options.mslOptions.shader_input_buffer_index = getMetalBufferIndexForVertexAttributeBinding(kMVKTessEvalInputBufferBinding); shaderConfig.options.mslOptions.shader_patch_input_buffer_index = getMetalBufferIndexForVertexAttributeBinding(kMVKTessEvalPatchInputBufferBinding); @@ -1105,7 +1221,7 @@ bool MVKGraphicsPipeline::addTessEvalShaderToPipeline(MTLRenderPipelineDescripto shaderConfig.options.mslOptions.disable_rasterization = !_isRasterizing; addPrevStageOutputToShaderConversionConfig(shaderConfig, tcOutputs); - MVKMTLFunction func = getMTLFunction(shaderConfig, _pTessEvalSS, "Tessellation evaluation"); + MVKMTLFunction func = getMTLFunction(shaderConfig, pTessEvalSS, pTessEvalFB, "Tessellation evaluation"); id mtlFunc = func.getMTLFunction(); plDesc.vertexFunction = mtlFunc; // Yeah, you read that right. Tess. eval functions are a kind of vertex function in Metal. if ( !mtlFunc ) { return false; } @@ -1120,7 +1236,7 @@ bool MVKGraphicsPipeline::addTessEvalShaderToPipeline(MTLRenderPipelineDescripto addMTLArgumentEncoders(func, pCreateInfo, shaderConfig, kMVKShaderStageTessEval); if (funcRslts.isRasterizationDisabled) { - _pFragmentSS = nullptr; + pFragmentSS = nullptr; } if (!verifyImplicitBuffer(_needsTessEvalSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageTessEval, "swizzle")) { @@ -1138,16 +1254,18 @@ bool MVKGraphicsPipeline::addTessEvalShaderToPipeline(MTLRenderPipelineDescripto bool MVKGraphicsPipeline::addFragmentShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, - SPIRVShaderOutputs& shaderOutputs) { - if (_pFragmentSS) { + SPIRVShaderOutputs& shaderOutputs, + const VkPipelineShaderStageCreateInfo* pFragmentSS, + VkPipelineCreationFeedback* pFragmentFB) { + if (pFragmentSS) { shaderConfig.options.entryPointStage = spv::ExecutionModelFragment; shaderConfig.options.mslOptions.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageFragment]; shaderConfig.options.mslOptions.buffer_size_buffer_index = _bufferSizeBufferIndex.stages[kMVKShaderStageFragment]; shaderConfig.options.mslOptions.dynamic_offsets_buffer_index = _dynamicOffsetBufferIndex.stages[kMVKShaderStageFragment]; shaderConfig.options.mslOptions.view_mask_buffer_index = _viewRangeBufferIndex.stages[kMVKShaderStageFragment]; - shaderConfig.options.entryPointName = _pFragmentSS->pName; + shaderConfig.options.entryPointName = pFragmentSS->pName; shaderConfig.options.mslOptions.capture_output_to_buffer = false; - shaderConfig.options.mslOptions.fixed_subgroup_size = mvkIsAnyFlagEnabled(_pFragmentSS->flags, VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT) ? 0 : _device->_pMetalFeatures->maxSubgroupSize; + shaderConfig.options.mslOptions.fixed_subgroup_size = mvkIsAnyFlagEnabled(pFragmentSS->flags, VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT) ? 0 : _device->_pMetalFeatures->maxSubgroupSize; shaderConfig.options.mslOptions.check_discarded_frag_stores = true; if (_device->_pMetalFeatures->needsSampleDrefLodArrayWorkaround) { shaderConfig.options.mslOptions.sample_dref_lod_array_as_grad = true; @@ -1163,7 +1281,7 @@ bool MVKGraphicsPipeline::addFragmentShaderToPipeline(MTLRenderPipelineDescripto } addPrevStageOutputToShaderConversionConfig(shaderConfig, shaderOutputs); - MVKMTLFunction func = getMTLFunction(shaderConfig, _pFragmentSS, "Fragment"); + MVKMTLFunction func = getMTLFunction(shaderConfig, pFragmentSS, pFragmentFB, "Fragment"); id mtlFunc = func.getMTLFunction(); plDesc.fragmentFunction = mtlFunc; if ( !mtlFunc ) { return false; } @@ -1795,11 +1913,13 @@ bool MVKGraphicsPipeline::isRasterizationDisabled(const VkGraphicsPipelineCreate MVKMTLFunction MVKGraphicsPipeline::getMTLFunction(SPIRVToMSLConversionConfiguration& shaderConfig, const VkPipelineShaderStageCreateInfo* pShaderStage, + VkPipelineCreationFeedback* pStageFB, const char* pStageName) { MVKShaderModule* shaderModule = (MVKShaderModule*)pShaderStage->module; MVKMTLFunction func = shaderModule->getMTLFunction(&shaderConfig, pShaderStage->pSpecializationInfo, - this); + this, + pStageFB); if ( !func.getMTLFunction() ) { if (shouldFailOnPipelineCompileRequired()) { setConfigurationResult(VK_PIPELINE_COMPILE_REQUIRED); @@ -1823,15 +1943,11 @@ bool MVKGraphicsPipeline::usesPhysicalStorageBufferAddressesCapability(MVKShader MVKGraphicsPipeline::~MVKGraphicsPipeline() { @synchronized (getMTLDevice()) { - [_mtlTessVertexStageDesc release]; - [_mtlTessVertexStageState release]; [_mtlTessVertexStageIndex16State release]; [_mtlTessVertexStageIndex32State release]; [_mtlTessControlStageState release]; [_mtlPipelineState release]; - - for (id func : _mtlTessVertexFunctions) { [func release]; } } } @@ -1862,7 +1978,36 @@ MVKComputePipeline::MVKComputePipeline(MVKDevice* device, if (isUsingMetalArgumentBuffers()) { _descriptorBindingUse.resize(_descriptorSetCount); } if (isUsingPipelineStageMetalArgumentBuffers()) { _mtlArgumentEncoders.resize(_descriptorSetCount); } - MVKMTLFunction func = getMTLFunction(pCreateInfo); + const VkPipelineCreationFeedbackCreateInfo* pFeedbackInfo = nullptr; + for (const auto* next = (VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) { + switch (next->sType) { + case VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO: + pFeedbackInfo = (VkPipelineCreationFeedbackCreateInfo*)next; + break; + default: + break; + } + } + + // Initialize feedback. The VALID bit must be initialized, either set or cleared. + // We'll set the VALID bit on the stage feedback when we compile it. + VkPipelineCreationFeedback* pPipelineFB = nullptr; + VkPipelineCreationFeedback* pStageFB = nullptr; + uint64_t pipelineStart; + if (pFeedbackInfo) { + pPipelineFB = pFeedbackInfo->pPipelineCreationFeedback; + // n.b. Do *NOT* use mvkClear(). + pPipelineFB->flags = 0; + pPipelineFB->duration = 0; + for (uint32_t i = 0; i < pFeedbackInfo->pipelineStageCreationFeedbackCount; ++i) { + pFeedbackInfo->pPipelineStageCreationFeedbacks[i].flags = 0; + pFeedbackInfo->pPipelineStageCreationFeedbacks[i].duration = 0; + } + pStageFB = &pFeedbackInfo->pPipelineStageCreationFeedbacks[0]; + pipelineStart = mvkGetTimestamp(); + } + + MVKMTLFunction func = getMTLFunction(pCreateInfo, pStageFB); _mtlThreadgroupSize = func.threadGroupSize; _mtlPipelineState = nil; @@ -1890,6 +2035,10 @@ MVKComputePipeline::MVKComputePipeline(MVKDevice* device, } else { _hasValidMTLPipelineStates = false; } + if (pPipelineFB) { + if (_hasValidMTLPipelineStates) { mvkEnableFlags(pPipelineFB->flags, VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT); } + pPipelineFB->duration = mvkGetElapsedNanoseconds(pipelineStart); + } if (_needsSwizzleBuffer && _swizzleBufferIndex.stages[kMVKShaderStageCompute] > _device->_pMetalFeatures->maxPerStageBufferCount) { setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Compute shader requires swizzle buffer, but there is no free slot to pass it.")); @@ -1906,7 +2055,8 @@ MVKComputePipeline::MVKComputePipeline(MVKDevice* device, } // Returns a MTLFunction to use when creating the MTLComputePipelineState. -MVKMTLFunction MVKComputePipeline::getMTLFunction(const VkComputePipelineCreateInfo* pCreateInfo) { +MVKMTLFunction MVKComputePipeline::getMTLFunction(const VkComputePipelineCreateInfo* pCreateInfo, + VkPipelineCreationFeedback* pStageFB) { const VkPipelineShaderStageCreateInfo* pSS = &pCreateInfo->stage; if ( !mvkAreAllFlagsEnabled(pSS->stage, VK_SHADER_STAGE_COMPUTE_BIT) ) { return MVKMTLFunctionNull; } @@ -1956,7 +2106,7 @@ MVKMTLFunction MVKComputePipeline::getMTLFunction(const VkComputePipelineCreateI shaderConfig.options.mslOptions.dynamic_offsets_buffer_index = _dynamicOffsetBufferIndex.stages[kMVKShaderStageCompute]; shaderConfig.options.mslOptions.indirect_params_buffer_index = _indirectParamsIndex.stages[kMVKShaderStageCompute]; - MVKMTLFunction func = ((MVKShaderModule*)pSS->module)->getMTLFunction(&shaderConfig, pSS->pSpecializationInfo, this); + MVKMTLFunction func = ((MVKShaderModule*)pSS->module)->getMTLFunction(&shaderConfig, pSS->pSpecializationInfo, this, pStageFB); if ( !func.getMTLFunction() ) { if (shouldFailOnPipelineCompileRequired()) { setConfigurationResult(VK_PIPELINE_COMPILE_REQUIRED); @@ -1998,23 +2148,26 @@ MVKComputePipeline::~MVKComputePipeline() { MVKShaderLibrary* MVKPipelineCache::getShaderLibrary(SPIRVToMSLConversionConfiguration* pContext, MVKShaderModule* shaderModule, MVKPipeline* pipeline, + VkPipelineCreationFeedback* pShaderFeedback, uint64_t startTime) { if (_isExternallySynchronized) { - return getShaderLibraryImpl(pContext, shaderModule, pipeline, startTime); + return getShaderLibraryImpl(pContext, shaderModule, pipeline, pShaderFeedback, startTime); } else { lock_guard lock(_shaderCacheLock); - return getShaderLibraryImpl(pContext, shaderModule, pipeline, startTime); + return getShaderLibraryImpl(pContext, shaderModule, pipeline, pShaderFeedback, startTime); } } MVKShaderLibrary* MVKPipelineCache::getShaderLibraryImpl(SPIRVToMSLConversionConfiguration* pContext, MVKShaderModule* shaderModule, MVKPipeline* pipeline, + VkPipelineCreationFeedback* pShaderFeedback, uint64_t startTime) { bool wasAdded = false; MVKShaderLibraryCache* slCache = getShaderLibraryCache(shaderModule->getKey()); - MVKShaderLibrary* shLib = slCache->getShaderLibrary(pContext, shaderModule, pipeline, &wasAdded, startTime); + MVKShaderLibrary* shLib = slCache->getShaderLibrary(pContext, shaderModule, pipeline, &wasAdded, pShaderFeedback, startTime); if (wasAdded) { markDirty(); } + else if (pShaderFeedback) { mvkEnableFlags(pShaderFeedback->flags, VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT); } return shLib; } diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h index a7e3417f..87418edd 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h @@ -107,7 +107,9 @@ protected: friend MVKShaderLibraryCache; friend MVKShaderModule; - MVKMTLFunction getMTLFunction(const VkSpecializationInfo* pSpecializationInfo, MVKShaderModule* shaderModule); + MVKMTLFunction getMTLFunction(const VkSpecializationInfo* pSpecializationInfo, + VkPipelineCreationFeedback* pShaderFeedback, + MVKShaderModule* shaderModule); void handleCompilationError(NSError* err, const char* opDesc); MTLFunctionConstant* getFunctionConstant(NSArray* mtlFCs, NSUInteger mtlFCID); void compileLibrary(const std::string& msl); @@ -144,7 +146,8 @@ public: */ MVKShaderLibrary* getShaderLibrary(SPIRVToMSLConversionConfiguration* pShaderConfig, MVKShaderModule* shaderModule, MVKPipeline* pipeline, - bool* pWasAdded, uint64_t startTime = 0); + bool* pWasAdded, VkPipelineCreationFeedback* pShaderFeedback, + uint64_t startTime = 0); MVKShaderLibraryCache(MVKVulkanAPIDeviceObject* owner) : _owner(owner) {}; @@ -155,7 +158,9 @@ protected: friend MVKPipelineCache; friend MVKShaderModule; - MVKShaderLibrary* findShaderLibrary(SPIRVToMSLConversionConfiguration* pShaderConfig, uint64_t startTime = 0); + MVKShaderLibrary* findShaderLibrary(SPIRVToMSLConversionConfiguration* pShaderConfig, + VkPipelineCreationFeedback* pShaderFeedback = nullptr, + uint64_t startTime = 0); MVKShaderLibrary* addShaderLibrary(const SPIRVToMSLConversionConfiguration* pShaderConfig, const SPIRVToMSLConversionResult& conversionResult); MVKShaderLibrary* addShaderLibrary(const SPIRVToMSLConversionConfiguration* pShaderConfig, @@ -207,7 +212,8 @@ public: /** Returns the Metal shader function, possibly specialized. */ MVKMTLFunction getMTLFunction(SPIRVToMSLConversionConfiguration* pShaderConfig, const VkSpecializationInfo* pSpecializationInfo, - MVKPipeline* pipeline); + MVKPipeline* pipeline, + VkPipelineCreationFeedback* pShaderFeedback); /** Convert the SPIR-V to MSL, using the specified shader conversion configuration. */ bool convert(SPIRVToMSLConversionConfiguration* pShaderConfig, diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm index 12ddbf4c..a47a65b7 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm @@ -67,7 +67,9 @@ static uint32_t getWorkgroupDimensionSize(const SPIRVWorkgroupSizeDimension& wgD return wgDim.size; } -MVKMTLFunction MVKShaderLibrary::getMTLFunction(const VkSpecializationInfo* pSpecializationInfo, MVKShaderModule* shaderModule) { +MVKMTLFunction MVKShaderLibrary::getMTLFunction(const VkSpecializationInfo* pSpecializationInfo, + VkPipelineCreationFeedback* pShaderFeedback, + MVKShaderModule* shaderModule) { if ( !_mtlLibrary ) { return MVKMTLFunctionNull; } @@ -76,9 +78,15 @@ MVKMTLFunction MVKShaderLibrary::getMTLFunction(const VkSpecializationInfo* pSpe NSString* mtlFuncName = @(_shaderConversionResultInfo.entryPoint.mtlFunctionName.c_str()); MVKDevice* mvkDev = _owner->getDevice(); - uint64_t startTime = mvkDev->getPerformanceTimestamp(); + uint64_t startTime = pShaderFeedback ? mvkGetTimestamp() : mvkDev->getPerformanceTimestamp(); id mtlFunc = [[_mtlLibrary newFunctionWithName: mtlFuncName] autorelease]; mvkDev->addActivityPerformance(mvkDev->_performanceStatistics.shaderCompilation.functionRetrieval, startTime); + if (pShaderFeedback) { + if (mtlFunc) { + mvkEnableFlags(pShaderFeedback->flags, VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT); + } + pShaderFeedback->duration += mvkGetElapsedNanoseconds(startTime); + } if (mtlFunc) { // If the Metal device supports shader specialization, and the Metal function expects to be specialized, @@ -108,7 +116,13 @@ MVKMTLFunction MVKShaderLibrary::getMTLFunction(const VkSpecializationInfo* pSpe // Compile the specialized Metal function, and use it instead of the unspecialized Metal function. MVKFunctionSpecializer fs(_owner); + if (pShaderFeedback) { + startTime = mvkGetTimestamp(); + } mtlFunc = [fs.newMTLFunction(_mtlLibrary, mtlFuncName, mtlFCVals) autorelease]; + if (pShaderFeedback) { + pShaderFeedback->duration += mvkGetElapsedNanoseconds(startTime); + } } } } @@ -240,13 +254,17 @@ MVKShaderLibrary::~MVKShaderLibrary() { MVKShaderLibrary* MVKShaderLibraryCache::getShaderLibrary(SPIRVToMSLConversionConfiguration* pShaderConfig, MVKShaderModule* shaderModule, MVKPipeline* pipeline, - bool* pWasAdded, uint64_t startTime) { + bool* pWasAdded, VkPipelineCreationFeedback* pShaderFeedback, + uint64_t startTime) { bool wasAdded = false; - MVKShaderLibrary* shLib = findShaderLibrary(pShaderConfig, startTime); + MVKShaderLibrary* shLib = findShaderLibrary(pShaderConfig, pShaderFeedback, startTime); if ( !shLib && !pipeline->shouldFailOnPipelineCompileRequired() ) { SPIRVToMSLConversionResult conversionResult; if (shaderModule->convert(pShaderConfig, conversionResult)) { shLib = addShaderLibrary(pShaderConfig, conversionResult); + if (pShaderFeedback) { + pShaderFeedback->duration += mvkGetElapsedNanoseconds(startTime); + } wasAdded = true; } } @@ -259,12 +277,16 @@ MVKShaderLibrary* MVKShaderLibraryCache::getShaderLibrary(SPIRVToMSLConversionCo // Finds and returns a shader library matching the shader config, or returns nullptr if it doesn't exist. // If a match is found, the shader config is aligned with the shader config of the matching library. MVKShaderLibrary* MVKShaderLibraryCache::findShaderLibrary(SPIRVToMSLConversionConfiguration* pShaderConfig, + VkPipelineCreationFeedback* pShaderFeedback, uint64_t startTime) { for (auto& slPair : _shaderLibraries) { if (slPair.first.matches(*pShaderConfig)) { pShaderConfig->alignWith(slPair.first); MVKDevice* mvkDev = _owner->getDevice(); mvkDev->addActivityPerformance(mvkDev->_performanceStatistics.shaderCompilation.shaderLibraryFromCache, startTime); + if (pShaderFeedback) { + pShaderFeedback->duration += mvkGetElapsedNanoseconds(startTime); + } return slPair.second; } } @@ -309,23 +331,24 @@ MVKShaderLibraryCache::~MVKShaderLibraryCache() { MVKMTLFunction MVKShaderModule::getMTLFunction(SPIRVToMSLConversionConfiguration* pShaderConfig, const VkSpecializationInfo* pSpecializationInfo, - MVKPipeline* pipeline) { + MVKPipeline* pipeline, + VkPipelineCreationFeedback* pShaderFeedback) { MVKShaderLibrary* mvkLib = _directMSLLibrary; if ( !mvkLib ) { - uint64_t startTime = _device->getPerformanceTimestamp(); + uint64_t startTime = pShaderFeedback ? mvkGetTimestamp() : _device->getPerformanceTimestamp(); MVKPipelineCache* pipelineCache = pipeline->getPipelineCache(); if (pipelineCache) { - mvkLib = pipelineCache->getShaderLibrary(pShaderConfig, this, pipeline, startTime); + mvkLib = pipelineCache->getShaderLibrary(pShaderConfig, this, pipeline, pShaderFeedback, startTime); } else { lock_guard lock(_accessLock); - mvkLib = _shaderLibraryCache.getShaderLibrary(pShaderConfig, this, pipeline, nullptr, startTime); + mvkLib = _shaderLibraryCache.getShaderLibrary(pShaderConfig, this, pipeline, nullptr, pShaderFeedback, startTime); } } else { mvkLib->setEntryPointName(pShaderConfig->options.entryPointName); pShaderConfig->markAllInterfaceVarsAndResourcesUsed(); } - return mvkLib ? mvkLib->getMTLFunction(pSpecializationInfo, this) : MVKMTLFunctionNull; + return mvkLib ? mvkLib->getMTLFunction(pSpecializationInfo, pShaderFeedback, this) : MVKMTLFunctionNull; } bool MVKShaderModule::convert(SPIRVToMSLConversionConfiguration* pShaderConfig, diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def index 4c6fe6f5..efa84e0f 100644 --- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def +++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def @@ -111,6 +111,7 @@ MVK_EXTENSION(EXT_memory_budget, EXT_MEMORY_BUDGET, MVK_EXTENSION(EXT_metal_objects, EXT_METAL_OBJECTS, DEVICE, 10.11, 8.0) MVK_EXTENSION(EXT_metal_surface, EXT_METAL_SURFACE, INSTANCE, 10.11, 8.0) MVK_EXTENSION(EXT_pipeline_creation_cache_control, EXT_PIPELINE_CREATION_CACHE_CONTROL, DEVICE, 10.11, 8.0) +MVK_EXTENSION(EXT_pipeline_creation_feedback, EXT_PIPELINE_CREATION_FEEDBACK, DEVICE, 10.11, 8.0) MVK_EXTENSION(EXT_post_depth_coverage, EXT_POST_DEPTH_COVERAGE, DEVICE, 11.0, 11.0) MVK_EXTENSION(EXT_private_data, EXT_PRIVATE_DATA, DEVICE, 10.11, 8.0) MVK_EXTENSION(EXT_robustness2, EXT_ROBUSTNESS_2, DEVICE, 10.11, 8.0)