Support the VK_EXT_pipeline_creation_feedback extension.

This provides feedback that indicates:
* how long it took to compile each shader stage and the pipeline as a
  whole;
* whether or not the pipeline or any shader stage were found in any
  supplied pipeline cache; and
* whether or not any supplied base pipeline were used to accelerate
  pipeline creation.

This is similar to the performance statistics that MoltenVK already
collects.

Since we don't use any supplied base pipeline at all, this
implementation never sets
`VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT`. However,
I've identified several places where we could probably use the base
pipeline to accelerate pipeline creation. One day, I should probably
implement that.

Likewise, because we don't yet support using `MTLBinaryArchive`s,
`VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT` is
never set on the whole pipeline, though it *is* set for individual
stages, on the assumption that any shader found in a cache is likely to
be found in Metal's own implicit cache.

In this implementation, shader stage compilation time includes any time
needed to build the `MTLComputePipelineState`s needed for vertex and
tessellation control shaders in tessellated pipelines.

This patch also changes compilation of the vertex stage
`MTLComputePipelineState`s in tessellated pipelines to be eager instead
of lazy. We really ought to have been doing this anyway, in order to
report pipeline failures at creation time instead of draw time. I'm not
happy, though, that we now pay the cost of all three pipeline states all
the time, instead of just the ones that are used.

This also gets rid of some fields of `MVKGraphicsPipeline` that were
only used during pipeline construction, which should save some memory,
particularly for apps that create lots of pipelines.
This commit is contained in:
Chip Davis 2023-07-07 00:25:37 -07:00
parent 2db85ea060
commit 561e14ba62
7 changed files with 333 additions and 139 deletions

View File

@ -76,6 +76,14 @@ uint64_t mvkGetTimestamp();
/** Returns the number of nanoseconds between each increment of the value returned by mvkGetTimestamp(). */ /** Returns the number of nanoseconds between each increment of the value returned by mvkGetTimestamp(). */
double mvkGetTimestampPeriod(); 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, * Returns the number of milliseconds elapsed between startTimestamp and endTimestamp,
* each of which should be a value returned by mvkGetTimestamp(). * each of which should be a value returned by mvkGetTimestamp().

View File

@ -46,9 +46,13 @@ uint64_t mvkGetTimestamp() { return mach_absolute_time() - _mvkTimestampBase; }
double mvkGetTimestampPeriod() { return _mvkTimestampPeriod; } 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(); } 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; } uint64_t mvkGetAbsoluteTime() { return mach_continuous_time() * _mvkMachTimebase.numer / _mvkMachTimebase.denom; }

View File

@ -233,7 +233,7 @@ public:
bool supportsDynamicState(VkDynamicState state); bool supportsDynamicState(VkDynamicState state);
/** Returns whether this pipeline has tessellation shaders. */ /** 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. */ /** Returns the number of input tessellation patch control points. */
uint32_t getInputControlPointCount() { return _tessInfo.patchControlPoints; } uint32_t getInputControlPointCount() { return _tessInfo.patchControlPoints; }
@ -251,13 +251,13 @@ public:
uint32_t getTessCtlLevelBufferIndex() { return _tessCtlLevelBufferIndex; } uint32_t getTessCtlLevelBufferIndex() { return _tessCtlLevelBufferIndex; }
/** Returns the MTLComputePipelineState object for the vertex stage of a tessellated draw with no indices. */ /** Returns the MTLComputePipelineState object for the vertex stage of a tessellated draw with no indices. */
id<MTLComputePipelineState> getTessVertexStageState(); id<MTLComputePipelineState> getTessVertexStageState() { return _mtlTessVertexStageState; }
/** Returns the MTLComputePipelineState object for the vertex stage of a tessellated draw with 16-bit indices. */ /** Returns the MTLComputePipelineState object for the vertex stage of a tessellated draw with 16-bit indices. */
id<MTLComputePipelineState> getTessVertexStageIndex16State(); id<MTLComputePipelineState> getTessVertexStageIndex16State() { return _mtlTessVertexStageIndex16State; }
/** Returns the MTLComputePipelineState object for the vertex stage of a tessellated draw with 32-bit indices. */ /** Returns the MTLComputePipelineState object for the vertex stage of a tessellated draw with 32-bit indices. */
id<MTLComputePipelineState> getTessVertexStageIndex32State(); id<MTLComputePipelineState> getTessVertexStageIndex32State() { return _mtlTessVertexStageIndex32State; }
/** Returns the MTLComputePipelineState object for the tessellation control stage of a tessellated draw. */ /** Returns the MTLComputePipelineState object for the tessellation control stage of a tessellated draw. */
id<MTLComputePipelineState> getTessControlStageState() { return _mtlTessControlStageState; } id<MTLComputePipelineState> getTessControlStageState() { return _mtlTessControlStageState; }
@ -318,22 +318,24 @@ protected:
id<MTLRenderPipelineState> getOrCompilePipeline(MTLRenderPipelineDescriptor* plDesc, id<MTLRenderPipelineState>& plState); id<MTLRenderPipelineState> getOrCompilePipeline(MTLRenderPipelineDescriptor* plDesc, id<MTLRenderPipelineState>& plState);
id<MTLComputePipelineState> getOrCompilePipeline(MTLComputePipelineDescriptor* plDesc, id<MTLComputePipelineState>& plState, const char* compilerType); id<MTLComputePipelineState> getOrCompilePipeline(MTLComputePipelineDescriptor* plDesc, id<MTLComputePipelineState>& plState, const char* compilerType);
bool compileTessVertexStageState(MTLComputePipelineDescriptor* vtxPLDesc, id<MTLFunction>* vtxFunctions, VkPipelineCreationFeedback* pVertexFB);
bool compileTessControlStageState(MTLComputePipelineDescriptor* tcPLDesc, VkPipelineCreationFeedback* pTessCtlFB);
void initCustomSamplePositions(const VkGraphicsPipelineCreateInfo* pCreateInfo); 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 initShaderConversionConfig(SPIRVToMSLConversionConfiguration& shaderConfig, const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData);
void initReservedVertexAttributeBufferCount(const VkGraphicsPipelineCreateInfo* pCreateInfo); void initReservedVertexAttributeBufferCount(const VkGraphicsPipelineCreateInfo* pCreateInfo);
void addVertexInputToShaderConversionConfig(SPIRVToMSLConversionConfiguration& shaderConfig, const VkGraphicsPipelineCreateInfo* pCreateInfo); void addVertexInputToShaderConversionConfig(SPIRVToMSLConversionConfiguration& shaderConfig, const VkGraphicsPipelineCreateInfo* pCreateInfo);
void addNextStageInputToShaderConversionConfig(SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderInputs& inputs); void addNextStageInputToShaderConversionConfig(SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderInputs& inputs);
void addPrevStageOutputToShaderConversionConfig(SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderOutputs& outputs); void addPrevStageOutputToShaderConversionConfig(SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderOutputs& outputs);
MTLRenderPipelineDescriptor* newMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData); 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); MTLComputePipelineDescriptor* newMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderConfig, const VkPipelineShaderStageCreateInfo* pVertexSS, VkPipelineCreationFeedback* pVertexFB, const VkPipelineShaderStageCreateInfo* pTessCtlSS, id<MTLFunction>* vtxFunctions);
MTLComputePipelineDescriptor* newMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData, SPIRVToMSLConversionConfiguration& shaderConfig); 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); 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); 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); bool addVertexShaderToPipeline(MTLComputePipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderInputs& nextInputs, const VkPipelineShaderStageCreateInfo* pVertexSS, VkPipelineCreationFeedback* pVertexFB, id<MTLFunction>* vtxFunctions);
bool addTessCtlShaderToPipeline(MTLComputePipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderOutputs& prevOutput, SPIRVShaderInputs& nextInputs); 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); 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); bool addFragmentShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVShaderOutputs& prevOutput, const VkPipelineShaderStageCreateInfo* pFragmentSS, VkPipelineCreationFeedback* pFragmentFB);
template<class T> template<class T>
bool addVertexInputToPipeline(T* inputDesc, const VkPipelineVertexInputStateCreateInfo* pVI, const SPIRVToMSLConversionConfiguration& shaderConfig); bool addVertexInputToPipeline(T* inputDesc, const VkPipelineVertexInputStateCreateInfo* pVI, const SPIRVToMSLConversionConfiguration& shaderConfig);
void adjustVertexInputForMultiview(MTLVertexDescriptor* inputDesc, const VkPipelineVertexInputStateCreateInfo* pVI, uint32_t viewCount, uint32_t oldViewCount = 1); 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); uint32_t getImplicitBufferIndex(MVKShaderStage stage, uint32_t bufferIndexOffset);
MVKMTLFunction getMTLFunction(SPIRVToMSLConversionConfiguration& shaderConfig, MVKMTLFunction getMTLFunction(SPIRVToMSLConversionConfiguration& shaderConfig,
const VkPipelineShaderStageCreateInfo* pShaderStage, const VkPipelineShaderStageCreateInfo* pShaderStage,
VkPipelineCreationFeedback* pStageFB,
const char* pStageName); const char* pStageName);
void markIfUsingPhysicalStorageBufferAddressesCapability(SPIRVToMSLConversionResultInfo& resultsInfo, void markIfUsingPhysicalStorageBufferAddressesCapability(SPIRVToMSLConversionResultInfo& resultsInfo,
MVKShaderStage stage); MVKShaderStage stage);
@ -365,13 +368,6 @@ protected:
MVKSmallVector<MVKShaderStage> _stagesUsingPhysicalStorageBufferAddressesCapability; MVKSmallVector<MVKShaderStage> _stagesUsingPhysicalStorageBufferAddressesCapability;
std::unordered_map<uint32_t, id<MTLRenderPipelineState>> _multiviewMTLPipelineStates; std::unordered_map<uint32_t, id<MTLRenderPipelineState>> _multiviewMTLPipelineStates;
const VkPipelineShaderStageCreateInfo* _pVertexSS = nullptr;
const VkPipelineShaderStageCreateInfo* _pTessCtlSS = nullptr;
const VkPipelineShaderStageCreateInfo* _pTessEvalSS = nullptr;
const VkPipelineShaderStageCreateInfo* _pFragmentSS = nullptr;
MTLComputePipelineDescriptor* _mtlTessVertexStageDesc = nil;
id<MTLFunction> _mtlTessVertexFunctions[3] = {nil, nil, nil};
id<MTLComputePipelineState> _mtlTessVertexStageState = nil; id<MTLComputePipelineState> _mtlTessVertexStageState = nil;
id<MTLComputePipelineState> _mtlTessVertexStageIndex16State = nil; id<MTLComputePipelineState> _mtlTessVertexStageIndex16State = nil;
id<MTLComputePipelineState> _mtlTessVertexStageIndex32State = nil; id<MTLComputePipelineState> _mtlTessVertexStageIndex32State = nil;
@ -446,7 +442,8 @@ public:
~MVKComputePipeline() override; ~MVKComputePipeline() override;
protected: protected:
MVKMTLFunction getMTLFunction(const VkComputePipelineCreateInfo* pCreateInfo); MVKMTLFunction getMTLFunction(const VkComputePipelineCreateInfo* pCreateInfo,
VkPipelineCreationFeedback* pStageFB);
uint32_t getImplicitBufferIndex(uint32_t bufferIndexOffset); uint32_t getImplicitBufferIndex(uint32_t bufferIndexOffset);
id<MTLComputePipelineState> _mtlPipelineState; id<MTLComputePipelineState> _mtlPipelineState;
@ -490,6 +487,7 @@ public:
MVKShaderLibrary* getShaderLibrary(SPIRVToMSLConversionConfiguration* pContext, MVKShaderLibrary* getShaderLibrary(SPIRVToMSLConversionConfiguration* pContext,
MVKShaderModule* shaderModule, MVKShaderModule* shaderModule,
MVKPipeline* pipeline, MVKPipeline* pipeline,
VkPipelineCreationFeedback* pShaderFeedback = nullptr,
uint64_t startTime = 0); uint64_t startTime = 0);
/** Merges the contents of the specified number of pipeline caches into this cache. */ /** Merges the contents of the specified number of pipeline caches into this cache. */
@ -510,6 +508,7 @@ protected:
MVKShaderLibrary* getShaderLibraryImpl(SPIRVToMSLConversionConfiguration* pContext, MVKShaderLibrary* getShaderLibraryImpl(SPIRVToMSLConversionConfiguration* pContext,
MVKShaderModule* shaderModule, MVKShaderModule* shaderModule,
MVKPipeline* pipeline, MVKPipeline* pipeline,
VkPipelineCreationFeedback* pShaderFeedback,
uint64_t startTime); uint64_t startTime);
VkResult writeDataImpl(size_t* pDataSize, void* pData); VkResult writeDataImpl(size_t* pDataSize, void* pData);
VkResult mergePipelineCachesImpl(uint32_t srcCacheCount, const VkPipelineCache* pSrcCaches); VkResult mergePipelineCachesImpl(uint32_t srcCacheCount, const VkPipelineCache* pSrcCaches);

View File

@ -337,42 +337,54 @@ bool MVKGraphicsPipeline::supportsDynamicState(VkDynamicState state) {
static const char vtxCompilerType[] = "Vertex stage pipeline for tessellation"; static const char vtxCompilerType[] = "Vertex stage pipeline for tessellation";
id<MTLComputePipelineState> MVKGraphicsPipeline::getTessVertexStageState() { bool MVKGraphicsPipeline::compileTessVertexStageState(MTLComputePipelineDescriptor* vtxPLDesc,
MTLComputePipelineDescriptor* plDesc = [_mtlTessVertexStageDesc copy]; // temp retain a copy to be thread-safe. id<MTLFunction>* vtxFunctions,
plDesc.computeFunction = _mtlTessVertexFunctions[0]; VkPipelineCreationFeedback* pVertexFB) {
id<MTLComputePipelineState> plState = getOrCompilePipeline(plDesc, _mtlTessVertexStageState, vtxCompilerType); uint64_t startTime = 0;
[plDesc release]; // temp release if (pVertexFB) {
return plState; startTime = mvkGetTimestamp();
} }
vtxPLDesc.computeFunction = vtxFunctions[0];
bool res = !!getOrCompilePipeline(vtxPLDesc, _mtlTessVertexStageState, vtxCompilerType);
id<MTLComputePipelineState> MVKGraphicsPipeline::getTessVertexStageIndex16State() { vtxPLDesc.computeFunction = vtxFunctions[1];
MTLComputePipelineDescriptor* plDesc = [_mtlTessVertexStageDesc copy]; // temp retain a copy to be thread-safe. vtxPLDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
plDesc.computeFunction = _mtlTessVertexFunctions[1];
plDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
for (uint32_t i = 0; i < 31; i++) { for (uint32_t i = 0; i < 31; i++) {
MTLBufferLayoutDescriptor* blDesc = plDesc.stageInputDescriptor.layouts[i]; MTLBufferLayoutDescriptor* blDesc = vtxPLDesc.stageInputDescriptor.layouts[i];
if (blDesc.stepFunction == MTLStepFunctionThreadPositionInGridX) { if (blDesc.stepFunction == MTLStepFunctionThreadPositionInGridX) {
blDesc.stepFunction = MTLStepFunctionThreadPositionInGridXIndexed; blDesc.stepFunction = MTLStepFunctionThreadPositionInGridXIndexed;
} }
} }
id<MTLComputePipelineState> plState = getOrCompilePipeline(plDesc, _mtlTessVertexStageIndex16State, vtxCompilerType); res |= !!getOrCompilePipeline(vtxPLDesc, _mtlTessVertexStageIndex16State, vtxCompilerType);
[plDesc release]; // temp release
return plState; 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<MTLComputePipelineState> MVKGraphicsPipeline::getTessVertexStageIndex32State() { bool MVKGraphicsPipeline::compileTessControlStageState(MTLComputePipelineDescriptor* tcPLDesc,
MTLComputePipelineDescriptor* plDesc = [_mtlTessVertexStageDesc copy]; // temp retain a copy to be thread-safe. VkPipelineCreationFeedback* pTessCtlFB) {
plDesc.computeFunction = _mtlTessVertexFunctions[2]; uint64_t startTime = 0;
plDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32; if (pTessCtlFB) {
for (uint32_t i = 0; i < 31; i++) { startTime = mvkGetTimestamp();
MTLBufferLayoutDescriptor* blDesc = plDesc.stageInputDescriptor.layouts[i]; }
if (blDesc.stepFunction == MTLStepFunctionThreadPositionInGridX) { bool res = !!getOrCompilePipeline(tcPLDesc, _mtlTessControlStageState, "Tessellation control");
blDesc.stepFunction = MTLStepFunctionThreadPositionInGridXIndexed; if (pTessCtlFB) {
} if (!res) {
} mvkDisableFlags(pTessCtlFB->flags, VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT);
id<MTLComputePipelineState> plState = getOrCompilePipeline(plDesc, _mtlTessVertexStageIndex32State, vtxCompilerType); }
[plDesc release]; // temp release pTessCtlFB->duration += mvkGetElapsedNanoseconds(startTime);
return plState; }
return res;
} }
@ -407,26 +419,78 @@ MVKGraphicsPipeline::MVKGraphicsPipeline(MVKDevice* device,
_isRasterizing = !isRasterizationDisabled(pCreateInfo); _isRasterizing = !isRasterizationDisabled(pCreateInfo);
_isRasterizingColor = _isRasterizing && mvkHasColorAttachments(pRendInfo); _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. // 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++) { for (uint32_t i = 0; i < pCreateInfo->stageCount; i++) {
const auto* pSS = &pCreateInfo->pStages[i]; const auto* pSS = &pCreateInfo->pStages[i];
if (pSS->stage == VK_SHADER_STAGE_VERTEX_BIT) { switch (pSS->stage) {
_pVertexSS = pSS; case VK_SHADER_STAGE_VERTEX_BIT:
} else if (pSS->stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) { pVertexSS = pSS;
_pTessCtlSS = pSS; if (pFeedbackInfo && pFeedbackInfo->pPipelineStageCreationFeedbacks) {
} else if (pSS->stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) { pVertexFB = &pFeedbackInfo->pPipelineStageCreationFeedbacks[i];
_pTessEvalSS = pSS; }
} else if (pSS->stage == VK_SHADER_STAGE_FRAGMENT_BIT) { break;
_pFragmentSS = pSS; 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. // Get the tessellation parameters from the shaders.
SPIRVTessReflectionData reflectData; SPIRVTessReflectionData reflectData;
std::string reflectErrorLog; std::string reflectErrorLog;
if (_pTessCtlSS && _pTessEvalSS) { if (pTessCtlSS && pTessEvalSS) {
if (!getTessReflectionData(((MVKShaderModule*)_pTessCtlSS->module)->getSPIRV(), _pTessCtlSS->pName, ((MVKShaderModule*)_pTessEvalSS->module)->getSPIRV(), _pTessEvalSS->pName, reflectData, reflectErrorLog) ) { 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())); setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to reflect tessellation shaders: %s", reflectErrorLog.c_str()));
return; return;
} }
@ -439,10 +503,10 @@ MVKGraphicsPipeline::MVKGraphicsPipeline(MVKDevice* device,
// Tessellation - must ignore allowed bad pTessellationState pointer if not tess pipeline // Tessellation - must ignore allowed bad pTessellationState pointer if not tess pipeline
_outputControlPointCount = reflectData.numControlPoints; _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. // 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; } if ( !_hasValidMTLPipelineStates ) { return; }
// Track dynamic state // Track dynamic state
@ -564,20 +628,33 @@ void MVKGraphicsPipeline::initCustomSamplePositions(const VkGraphicsPipelineCrea
} }
// Constructs the underlying Metal render pipeline. // 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; _mtlTessVertexStageState = nil;
_mtlTessVertexStageIndex16State = nil; _mtlTessVertexStageIndex16State = nil;
_mtlTessVertexStageIndex32State = nil; _mtlTessVertexStageIndex32State = nil;
_mtlTessControlStageState = nil; _mtlTessControlStageState = nil;
_mtlPipelineState = 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 (isUsingMetalArgumentBuffers()) { _descriptorBindingUse.resize(_descriptorSetCount); }
if (isUsingPipelineStageMetalArgumentBuffers()) { _mtlArgumentEncoders.resize(_descriptorSetCount); } if (isUsingPipelineStageMetalArgumentBuffers()) { _mtlArgumentEncoders.resize(_descriptorSetCount); }
if (!isTessellationPipeline()) { if (!isTessellationPipeline()) {
MTLRenderPipelineDescriptor* plDesc = newMTLRenderPipelineDescriptor(pCreateInfo, reflectData); // temp retain MTLRenderPipelineDescriptor* plDesc = newMTLRenderPipelineDescriptor(pCreateInfo, reflectData, pVertexSS, pVertexFB, pFragmentSS, pFragmentFB); // temp retain
if (plDesc) { if (plDesc) {
const VkPipelineRenderingCreateInfo* pRendInfo = getRenderingCreateInfo(pCreateInfo); const VkPipelineRenderingCreateInfo* pRendInfo = getRenderingCreateInfo(pCreateInfo);
if (pRendInfo && mvkIsMultiview(pRendInfo->viewMask)) { if (pRendInfo && mvkIsMultiview(pRendInfo->viewMask)) {
@ -613,30 +690,45 @@ void MVKGraphicsPipeline::initMTLRenderPipelineState(const VkGraphicsPipelineCre
} }
} else { } else {
// In this case, we need to create three render pipelines. But, the way Metal handles // 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 // index buffers for compute stage-in means we have to create three pipelines for
// draw time. In the meantime, we'll create and retain a descriptor for it. // stage 1 (five pipelines in total).
SPIRVToMSLConversionConfiguration shaderConfig; SPIRVToMSLConversionConfiguration shaderConfig;
initShaderConversionConfig(shaderConfig, pCreateInfo, reflectData); initShaderConversionConfig(shaderConfig, pCreateInfo, reflectData);
_mtlTessVertexStageDesc = newMTLTessVertexStageDescriptor(pCreateInfo, reflectData, shaderConfig); // retained id<MTLFunction> vtxFunctions[3] = { nil };
MTLComputePipelineDescriptor* tcPLDesc = newMTLTessControlStageDescriptor(pCreateInfo, reflectData, shaderConfig); // temp retained MTLComputePipelineDescriptor* vtxPLDesc = newMTLTessVertexStageDescriptor(pCreateInfo, reflectData, shaderConfig, pVertexSS, pVertexFB, pTessCtlSS, vtxFunctions); // temp retained
MTLRenderPipelineDescriptor* rastPLDesc = newMTLTessRasterStageDescriptor(pCreateInfo, reflectData, shaderConfig); // temp retained MTLComputePipelineDescriptor* tcPLDesc = newMTLTessControlStageDescriptor(pCreateInfo, reflectData, shaderConfig, pTessCtlSS, pTessCtlFB, pVertexSS, pTessEvalSS); // temp retained
if (_mtlTessVertexStageDesc && tcPLDesc && rastPLDesc) { MTLRenderPipelineDescriptor* rastPLDesc = newMTLTessRasterStageDescriptor(pCreateInfo, reflectData, shaderConfig, pTessEvalSS, pTessEvalFB, pFragmentSS, pFragmentFB, pTessCtlSS); // temp retained
if (getOrCompilePipeline(tcPLDesc, _mtlTessControlStageState, "Tessellation control")) { if (vtxPLDesc && tcPLDesc && rastPLDesc) {
getOrCompilePipeline(rastPLDesc, _mtlPipelineState); if (compileTessVertexStageState(vtxPLDesc, vtxFunctions, pVertexFB)) {
if (compileTessControlStageState(tcPLDesc, pTessCtlFB)) {
getOrCompilePipeline(rastPLDesc, _mtlPipelineState);
}
} }
} else { } else {
_hasValidMTLPipelineStates = false; _hasValidMTLPipelineStates = false;
} }
[vtxPLDesc release]; // temp release
[tcPLDesc release]; // temp release [tcPLDesc release]; // temp release
[rastPLDesc 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. // 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. // It is the responsibility of the caller to release the returned descriptor.
MTLRenderPipelineDescriptor* MVKGraphicsPipeline::newMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, MTLRenderPipelineDescriptor* MVKGraphicsPipeline::newMTLRenderPipelineDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
const SPIRVTessReflectionData& reflectData) { const SPIRVTessReflectionData& reflectData,
const VkPipelineShaderStageCreateInfo* pVertexSS,
VkPipelineCreationFeedback* pVertexFB,
const VkPipelineShaderStageCreateInfo* pFragmentSS,
VkPipelineCreationFeedback* pFragmentFB) {
SPIRVToMSLConversionConfiguration shaderConfig; SPIRVToMSLConversionConfiguration shaderConfig;
initShaderConversionConfig(shaderConfig, pCreateInfo, reflectData); initShaderConversionConfig(shaderConfig, pCreateInfo, reflectData);
@ -644,20 +736,20 @@ MTLRenderPipelineDescriptor* MVKGraphicsPipeline::newMTLRenderPipelineDescriptor
SPIRVShaderOutputs vtxOutputs; SPIRVShaderOutputs vtxOutputs;
std::string errorLog; 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())); setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to get vertex outputs: %s", errorLog.c_str()));
return nil; return nil;
} }
// Add shader stages. Compile vertex shader before others just in case conversion changes anything...like rasterizaion disable. // 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 // Vertex input
// This needs to happen before compiling the fragment shader, or we'll lose information on vertex attributes. // 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; } if (!addVertexInputToPipeline(plDesc.vertexDescriptor, pCreateInfo->pVertexInputState, shaderConfig)) { return nil; }
// Fragment shader - only add if rasterization is enabled // 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 // Output
addFragmentOutputToPipeline(plDesc, pCreateInfo); 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. // 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. // It is the responsibility of the caller to release the returned descriptor.
MTLComputePipelineDescriptor* MVKGraphicsPipeline::newMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, MTLComputePipelineDescriptor* MVKGraphicsPipeline::newMTLTessVertexStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
const SPIRVTessReflectionData& reflectData, const SPIRVTessReflectionData& reflectData,
SPIRVToMSLConversionConfiguration& shaderConfig) { SPIRVToMSLConversionConfiguration& shaderConfig,
const VkPipelineShaderStageCreateInfo* pVertexSS,
VkPipelineCreationFeedback* pVertexFB,
const VkPipelineShaderStageCreateInfo* pTessCtlSS,
id<MTLFunction>* vtxFunctions) {
MTLComputePipelineDescriptor* plDesc = [MTLComputePipelineDescriptor new]; // retained MTLComputePipelineDescriptor* plDesc = [MTLComputePipelineDescriptor new]; // retained
SPIRVShaderInputs tcInputs; SPIRVShaderInputs tcInputs;
std::string errorLog; 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())); setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to get tessellation control inputs: %s", errorLog.c_str()));
return nil; return nil;
} }
@ -691,7 +787,7 @@ MTLComputePipelineDescriptor* MVKGraphicsPipeline::newMTLTessVertexStageDescript
}), tcInputs.end()); }), tcInputs.end());
// Add shader stages. // Add shader stages.
if (!addVertexShaderToPipeline(plDesc, pCreateInfo, shaderConfig, tcInputs)) { return nil; } if (!addVertexShaderToPipeline(plDesc, pCreateInfo, shaderConfig, tcInputs, pVertexSS, pVertexFB, vtxFunctions)) { return nil; }
// Vertex input // Vertex input
plDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor]; 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. // It is the responsibility of the caller to release the returned descriptor.
MTLComputePipelineDescriptor* MVKGraphicsPipeline::newMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, MTLComputePipelineDescriptor* MVKGraphicsPipeline::newMTLTessControlStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
const SPIRVTessReflectionData& reflectData, const SPIRVTessReflectionData& reflectData,
SPIRVToMSLConversionConfiguration& shaderConfig) { SPIRVToMSLConversionConfiguration& shaderConfig,
const VkPipelineShaderStageCreateInfo* pTessCtlSS,
VkPipelineCreationFeedback* pTessCtlFB,
const VkPipelineShaderStageCreateInfo* pVertexSS,
const VkPipelineShaderStageCreateInfo* pTessEvalSS) {
MTLComputePipelineDescriptor* plDesc = [MTLComputePipelineDescriptor new]; // retained MTLComputePipelineDescriptor* plDesc = [MTLComputePipelineDescriptor new]; // retained
SPIRVShaderOutputs vtxOutputs; SPIRVShaderOutputs vtxOutputs;
SPIRVShaderInputs teInputs; SPIRVShaderInputs teInputs;
std::string errorLog; 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())); setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to get vertex outputs: %s", errorLog.c_str()));
return nil; 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())); setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to get tessellation evaluation inputs: %s", errorLog.c_str()));
return nil; return nil;
} }
@ -833,7 +933,7 @@ MTLComputePipelineDescriptor* MVKGraphicsPipeline::newMTLTessControlStageDescrip
}), teInputs.end()); }), teInputs.end());
// Add shader stages. // Add shader stages.
if (!addTessCtlShaderToPipeline(plDesc, pCreateInfo, shaderConfig, vtxOutputs, teInputs)) { if (!addTessCtlShaderToPipeline(plDesc, pCreateInfo, shaderConfig, vtxOutputs, teInputs, pTessCtlSS, pTessCtlFB)) {
[plDesc release]; [plDesc release];
return nil; return nil;
} }
@ -850,29 +950,34 @@ MTLComputePipelineDescriptor* MVKGraphicsPipeline::newMTLTessControlStageDescrip
// It is the responsibility of the caller to release the returned descriptor. // It is the responsibility of the caller to release the returned descriptor.
MTLRenderPipelineDescriptor* MVKGraphicsPipeline::newMTLTessRasterStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo, MTLRenderPipelineDescriptor* MVKGraphicsPipeline::newMTLTessRasterStageDescriptor(const VkGraphicsPipelineCreateInfo* pCreateInfo,
const SPIRVTessReflectionData& reflectData, 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 MTLRenderPipelineDescriptor* plDesc = [MTLRenderPipelineDescriptor new]; // retained
SPIRVShaderOutputs tcOutputs, teOutputs; SPIRVShaderOutputs tcOutputs, teOutputs;
SPIRVShaderInputs teInputs; SPIRVShaderInputs teInputs;
std::string errorLog; 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())); setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to get tessellation control outputs: %s", errorLog.c_str()));
return nil; 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())); setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Failed to get tessellation evaluation outputs: %s", errorLog.c_str()));
return nil; return nil;
} }
// Add shader stages. Compile tessellation evaluation shader before others just in case conversion changes anything...like rasterizaion disable. // 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]; [plDesc release];
return nil; return nil;
} }
// Fragment shader - only add if rasterization is enabled // Fragment shader - only add if rasterization is enabled
if (!addFragmentShaderToPipeline(plDesc, pCreateInfo, shaderConfig, teOutputs)) { if (!addFragmentShaderToPipeline(plDesc, pCreateInfo, shaderConfig, teOutputs, pFragmentSS, pFragmentFB)) {
[plDesc release]; [plDesc release];
return nil; return nil;
} }
@ -903,9 +1008,12 @@ bool MVKGraphicsPipeline::verifyImplicitBuffer(bool needsBuffer, MVKShaderImplic
// Adds a vertex shader to the pipeline description. // Adds a vertex shader to the pipeline description.
bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLRenderPipelineDescriptor* plDesc,
const VkGraphicsPipelineCreateInfo* pCreateInfo, const VkGraphicsPipelineCreateInfo* pCreateInfo,
SPIRVToMSLConversionConfiguration& shaderConfig) { SPIRVToMSLConversionConfiguration& shaderConfig,
const VkPipelineShaderStageCreateInfo* pVertexSS,
VkPipelineCreationFeedback* pVertexFB,
const VkPipelineShaderStageCreateInfo*& pFragmentSS) {
shaderConfig.options.entryPointStage = spv::ExecutionModelVertex; 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.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageVertex];
shaderConfig.options.mslOptions.indirect_params_buffer_index = _indirectParamsIndex.stages[kMVKShaderStageVertex]; shaderConfig.options.mslOptions.indirect_params_buffer_index = _indirectParamsIndex.stages[kMVKShaderStageVertex];
shaderConfig.options.mslOptions.shader_output_buffer_index = _outputBufferIndex.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; shaderConfig.options.mslOptions.disable_rasterization = !_isRasterizing;
addVertexInputToShaderConversionConfig(shaderConfig, pCreateInfo); addVertexInputToShaderConversionConfig(shaderConfig, pCreateInfo);
MVKMTLFunction func = getMTLFunction(shaderConfig, _pVertexSS, "Vertex"); MVKMTLFunction func = getMTLFunction(shaderConfig, pVertexSS, pVertexFB, "Vertex");
id<MTLFunction> mtlFunc = func.getMTLFunction(); id<MTLFunction> mtlFunc = func.getMTLFunction();
plDesc.vertexFunction = mtlFunc; plDesc.vertexFunction = mtlFunc;
if ( !mtlFunc ) { return false; } if ( !mtlFunc ) { return false; }
@ -933,7 +1041,7 @@ bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLRenderPipelineDescriptor*
addMTLArgumentEncoders(func, pCreateInfo, shaderConfig, kMVKShaderStageVertex); addMTLArgumentEncoders(func, pCreateInfo, shaderConfig, kMVKShaderStageVertex);
if (funcRslts.isRasterizationDisabled) { 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. // 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, bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLComputePipelineDescriptor* plDesc,
const VkGraphicsPipelineCreateInfo* pCreateInfo, const VkGraphicsPipelineCreateInfo* pCreateInfo,
SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVToMSLConversionConfiguration& shaderConfig,
SPIRVShaderInputs& tcInputs) { SPIRVShaderInputs& tcInputs,
const VkPipelineShaderStageCreateInfo* pVertexSS,
VkPipelineCreationFeedback* pVertexFB,
id<MTLFunction>* vtxFunctions) {
shaderConfig.options.entryPointStage = spv::ExecutionModelVertex; 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.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageVertex];
shaderConfig.options.mslOptions.shader_index_buffer_index = _indirectParamsIndex.stages[kMVKShaderStageVertex]; shaderConfig.options.mslOptions.shader_index_buffer_index = _indirectParamsIndex.stages[kMVKShaderStageVertex];
shaderConfig.options.mslOptions.shader_output_buffer_index = _outputBufferIndex.stages[kMVKShaderStageVertex]; shaderConfig.options.mslOptions.shader_output_buffer_index = _outputBufferIndex.stages[kMVKShaderStageVertex];
@ -988,9 +1099,9 @@ bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLComputePipelineDescriptor
MVKMTLFunction func; MVKMTLFunction func;
for (uint32_t i = 0; i < sizeof(indexTypes)/sizeof(indexTypes[0]); i++) { for (uint32_t i = 0; i < sizeof(indexTypes)/sizeof(indexTypes[0]); i++) {
shaderConfig.options.mslOptions.vertex_index_type = indexTypes[i]; shaderConfig.options.mslOptions.vertex_index_type = indexTypes[i];
func = getMTLFunction(shaderConfig, _pVertexSS, "Vertex"); func = getMTLFunction(shaderConfig, pVertexSS, pVertexFB, "Vertex");
id<MTLFunction> mtlFunc = func.getMTLFunction(); id<MTLFunction> mtlFunc = func.getMTLFunction();
_mtlTessVertexFunctions[i] = [mtlFunc retain]; vtxFunctions[i] = mtlFunc; // not retained
if ( !mtlFunc ) { return false; } if ( !mtlFunc ) { return false; }
auto& funcRslts = func.shaderConversionResults; auto& funcRslts = func.shaderConversionResults;
@ -1029,9 +1140,11 @@ bool MVKGraphicsPipeline::addTessCtlShaderToPipeline(MTLComputePipelineDescripto
const VkGraphicsPipelineCreateInfo* pCreateInfo, const VkGraphicsPipelineCreateInfo* pCreateInfo,
SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVToMSLConversionConfiguration& shaderConfig,
SPIRVShaderOutputs& vtxOutputs, SPIRVShaderOutputs& vtxOutputs,
SPIRVShaderInputs& teInputs) { SPIRVShaderInputs& teInputs,
const VkPipelineShaderStageCreateInfo* pTessCtlSS,
VkPipelineCreationFeedback* pTessCtlFB) {
shaderConfig.options.entryPointStage = spv::ExecutionModelTessellationControl; 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.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageTessCtl];
shaderConfig.options.mslOptions.indirect_params_buffer_index = _indirectParamsIndex.stages[kMVKShaderStageTessCtl]; shaderConfig.options.mslOptions.indirect_params_buffer_index = _indirectParamsIndex.stages[kMVKShaderStageTessCtl];
shaderConfig.options.mslOptions.shader_input_buffer_index = getMetalBufferIndexForVertexAttributeBinding(kMVKTessCtlInputBufferBinding); 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.dynamic_offsets_buffer_index = _dynamicOffsetBufferIndex.stages[kMVKShaderStageTessCtl];
shaderConfig.options.mslOptions.capture_output_to_buffer = true; shaderConfig.options.mslOptions.capture_output_to_buffer = true;
shaderConfig.options.mslOptions.multi_patch_workgroup = 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); addPrevStageOutputToShaderConversionConfig(shaderConfig, vtxOutputs);
addNextStageInputToShaderConversionConfig(shaderConfig, teInputs); addNextStageInputToShaderConversionConfig(shaderConfig, teInputs);
MVKMTLFunction func = getMTLFunction(shaderConfig, _pTessCtlSS, "Tessellation control"); MVKMTLFunction func = getMTLFunction(shaderConfig, pTessCtlSS, pTessCtlFB, "Tessellation control");
id<MTLFunction> mtlFunc = func.getMTLFunction(); id<MTLFunction> mtlFunc = func.getMTLFunction();
if ( !mtlFunc ) { return false; } if ( !mtlFunc ) { return false; }
plDesc.computeFunction = mtlFunc; plDesc.computeFunction = mtlFunc;
@ -1091,9 +1204,12 @@ bool MVKGraphicsPipeline::addTessCtlShaderToPipeline(MTLComputePipelineDescripto
bool MVKGraphicsPipeline::addTessEvalShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, bool MVKGraphicsPipeline::addTessEvalShaderToPipeline(MTLRenderPipelineDescriptor* plDesc,
const VkGraphicsPipelineCreateInfo* pCreateInfo, const VkGraphicsPipelineCreateInfo* pCreateInfo,
SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVToMSLConversionConfiguration& shaderConfig,
SPIRVShaderOutputs& tcOutputs) { SPIRVShaderOutputs& tcOutputs,
const VkPipelineShaderStageCreateInfo* pTessEvalSS,
VkPipelineCreationFeedback* pTessEvalFB,
const VkPipelineShaderStageCreateInfo*& pFragmentSS) {
shaderConfig.options.entryPointStage = spv::ExecutionModelTessellationEvaluation; 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.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageTessEval];
shaderConfig.options.mslOptions.shader_input_buffer_index = getMetalBufferIndexForVertexAttributeBinding(kMVKTessEvalInputBufferBinding); shaderConfig.options.mslOptions.shader_input_buffer_index = getMetalBufferIndexForVertexAttributeBinding(kMVKTessEvalInputBufferBinding);
shaderConfig.options.mslOptions.shader_patch_input_buffer_index = getMetalBufferIndexForVertexAttributeBinding(kMVKTessEvalPatchInputBufferBinding); shaderConfig.options.mslOptions.shader_patch_input_buffer_index = getMetalBufferIndexForVertexAttributeBinding(kMVKTessEvalPatchInputBufferBinding);
@ -1105,7 +1221,7 @@ bool MVKGraphicsPipeline::addTessEvalShaderToPipeline(MTLRenderPipelineDescripto
shaderConfig.options.mslOptions.disable_rasterization = !_isRasterizing; shaderConfig.options.mslOptions.disable_rasterization = !_isRasterizing;
addPrevStageOutputToShaderConversionConfig(shaderConfig, tcOutputs); addPrevStageOutputToShaderConversionConfig(shaderConfig, tcOutputs);
MVKMTLFunction func = getMTLFunction(shaderConfig, _pTessEvalSS, "Tessellation evaluation"); MVKMTLFunction func = getMTLFunction(shaderConfig, pTessEvalSS, pTessEvalFB, "Tessellation evaluation");
id<MTLFunction> mtlFunc = func.getMTLFunction(); id<MTLFunction> mtlFunc = func.getMTLFunction();
plDesc.vertexFunction = mtlFunc; // Yeah, you read that right. Tess. eval functions are a kind of vertex function in Metal. plDesc.vertexFunction = mtlFunc; // Yeah, you read that right. Tess. eval functions are a kind of vertex function in Metal.
if ( !mtlFunc ) { return false; } if ( !mtlFunc ) { return false; }
@ -1120,7 +1236,7 @@ bool MVKGraphicsPipeline::addTessEvalShaderToPipeline(MTLRenderPipelineDescripto
addMTLArgumentEncoders(func, pCreateInfo, shaderConfig, kMVKShaderStageTessEval); addMTLArgumentEncoders(func, pCreateInfo, shaderConfig, kMVKShaderStageTessEval);
if (funcRslts.isRasterizationDisabled) { if (funcRslts.isRasterizationDisabled) {
_pFragmentSS = nullptr; pFragmentSS = nullptr;
} }
if (!verifyImplicitBuffer(_needsTessEvalSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageTessEval, "swizzle")) { if (!verifyImplicitBuffer(_needsTessEvalSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageTessEval, "swizzle")) {
@ -1138,16 +1254,18 @@ bool MVKGraphicsPipeline::addTessEvalShaderToPipeline(MTLRenderPipelineDescripto
bool MVKGraphicsPipeline::addFragmentShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, bool MVKGraphicsPipeline::addFragmentShaderToPipeline(MTLRenderPipelineDescriptor* plDesc,
const VkGraphicsPipelineCreateInfo* pCreateInfo, const VkGraphicsPipelineCreateInfo* pCreateInfo,
SPIRVToMSLConversionConfiguration& shaderConfig, SPIRVToMSLConversionConfiguration& shaderConfig,
SPIRVShaderOutputs& shaderOutputs) { SPIRVShaderOutputs& shaderOutputs,
if (_pFragmentSS) { const VkPipelineShaderStageCreateInfo* pFragmentSS,
VkPipelineCreationFeedback* pFragmentFB) {
if (pFragmentSS) {
shaderConfig.options.entryPointStage = spv::ExecutionModelFragment; shaderConfig.options.entryPointStage = spv::ExecutionModelFragment;
shaderConfig.options.mslOptions.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageFragment]; shaderConfig.options.mslOptions.swizzle_buffer_index = _swizzleBufferIndex.stages[kMVKShaderStageFragment];
shaderConfig.options.mslOptions.buffer_size_buffer_index = _bufferSizeBufferIndex.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.dynamic_offsets_buffer_index = _dynamicOffsetBufferIndex.stages[kMVKShaderStageFragment];
shaderConfig.options.mslOptions.view_mask_buffer_index = _viewRangeBufferIndex.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.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; shaderConfig.options.mslOptions.check_discarded_frag_stores = true;
if (_device->_pMetalFeatures->needsSampleDrefLodArrayWorkaround) { if (_device->_pMetalFeatures->needsSampleDrefLodArrayWorkaround) {
shaderConfig.options.mslOptions.sample_dref_lod_array_as_grad = true; shaderConfig.options.mslOptions.sample_dref_lod_array_as_grad = true;
@ -1163,7 +1281,7 @@ bool MVKGraphicsPipeline::addFragmentShaderToPipeline(MTLRenderPipelineDescripto
} }
addPrevStageOutputToShaderConversionConfig(shaderConfig, shaderOutputs); addPrevStageOutputToShaderConversionConfig(shaderConfig, shaderOutputs);
MVKMTLFunction func = getMTLFunction(shaderConfig, _pFragmentSS, "Fragment"); MVKMTLFunction func = getMTLFunction(shaderConfig, pFragmentSS, pFragmentFB, "Fragment");
id<MTLFunction> mtlFunc = func.getMTLFunction(); id<MTLFunction> mtlFunc = func.getMTLFunction();
plDesc.fragmentFunction = mtlFunc; plDesc.fragmentFunction = mtlFunc;
if ( !mtlFunc ) { return false; } if ( !mtlFunc ) { return false; }
@ -1795,11 +1913,13 @@ bool MVKGraphicsPipeline::isRasterizationDisabled(const VkGraphicsPipelineCreate
MVKMTLFunction MVKGraphicsPipeline::getMTLFunction(SPIRVToMSLConversionConfiguration& shaderConfig, MVKMTLFunction MVKGraphicsPipeline::getMTLFunction(SPIRVToMSLConversionConfiguration& shaderConfig,
const VkPipelineShaderStageCreateInfo* pShaderStage, const VkPipelineShaderStageCreateInfo* pShaderStage,
VkPipelineCreationFeedback* pStageFB,
const char* pStageName) { const char* pStageName) {
MVKShaderModule* shaderModule = (MVKShaderModule*)pShaderStage->module; MVKShaderModule* shaderModule = (MVKShaderModule*)pShaderStage->module;
MVKMTLFunction func = shaderModule->getMTLFunction(&shaderConfig, MVKMTLFunction func = shaderModule->getMTLFunction(&shaderConfig,
pShaderStage->pSpecializationInfo, pShaderStage->pSpecializationInfo,
this); this,
pStageFB);
if ( !func.getMTLFunction() ) { if ( !func.getMTLFunction() ) {
if (shouldFailOnPipelineCompileRequired()) { if (shouldFailOnPipelineCompileRequired()) {
setConfigurationResult(VK_PIPELINE_COMPILE_REQUIRED); setConfigurationResult(VK_PIPELINE_COMPILE_REQUIRED);
@ -1823,15 +1943,11 @@ bool MVKGraphicsPipeline::usesPhysicalStorageBufferAddressesCapability(MVKShader
MVKGraphicsPipeline::~MVKGraphicsPipeline() { MVKGraphicsPipeline::~MVKGraphicsPipeline() {
@synchronized (getMTLDevice()) { @synchronized (getMTLDevice()) {
[_mtlTessVertexStageDesc release];
[_mtlTessVertexStageState release]; [_mtlTessVertexStageState release];
[_mtlTessVertexStageIndex16State release]; [_mtlTessVertexStageIndex16State release];
[_mtlTessVertexStageIndex32State release]; [_mtlTessVertexStageIndex32State release];
[_mtlTessControlStageState release]; [_mtlTessControlStageState release];
[_mtlPipelineState release]; [_mtlPipelineState release];
for (id<MTLFunction> func : _mtlTessVertexFunctions) { [func release]; }
} }
} }
@ -1862,7 +1978,36 @@ MVKComputePipeline::MVKComputePipeline(MVKDevice* device,
if (isUsingMetalArgumentBuffers()) { _descriptorBindingUse.resize(_descriptorSetCount); } if (isUsingMetalArgumentBuffers()) { _descriptorBindingUse.resize(_descriptorSetCount); }
if (isUsingPipelineStageMetalArgumentBuffers()) { _mtlArgumentEncoders.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; _mtlThreadgroupSize = func.threadGroupSize;
_mtlPipelineState = nil; _mtlPipelineState = nil;
@ -1890,6 +2035,10 @@ MVKComputePipeline::MVKComputePipeline(MVKDevice* device,
} else { } else {
_hasValidMTLPipelineStates = false; _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) { 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.")); 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. // 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; const VkPipelineShaderStageCreateInfo* pSS = &pCreateInfo->stage;
if ( !mvkAreAllFlagsEnabled(pSS->stage, VK_SHADER_STAGE_COMPUTE_BIT) ) { return MVKMTLFunctionNull; } 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.dynamic_offsets_buffer_index = _dynamicOffsetBufferIndex.stages[kMVKShaderStageCompute];
shaderConfig.options.mslOptions.indirect_params_buffer_index = _indirectParamsIndex.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 ( !func.getMTLFunction() ) {
if (shouldFailOnPipelineCompileRequired()) { if (shouldFailOnPipelineCompileRequired()) {
setConfigurationResult(VK_PIPELINE_COMPILE_REQUIRED); setConfigurationResult(VK_PIPELINE_COMPILE_REQUIRED);
@ -1998,23 +2148,26 @@ MVKComputePipeline::~MVKComputePipeline() {
MVKShaderLibrary* MVKPipelineCache::getShaderLibrary(SPIRVToMSLConversionConfiguration* pContext, MVKShaderLibrary* MVKPipelineCache::getShaderLibrary(SPIRVToMSLConversionConfiguration* pContext,
MVKShaderModule* shaderModule, MVKShaderModule* shaderModule,
MVKPipeline* pipeline, MVKPipeline* pipeline,
VkPipelineCreationFeedback* pShaderFeedback,
uint64_t startTime) { uint64_t startTime) {
if (_isExternallySynchronized) { if (_isExternallySynchronized) {
return getShaderLibraryImpl(pContext, shaderModule, pipeline, startTime); return getShaderLibraryImpl(pContext, shaderModule, pipeline, pShaderFeedback, startTime);
} else { } else {
lock_guard<mutex> lock(_shaderCacheLock); lock_guard<mutex> lock(_shaderCacheLock);
return getShaderLibraryImpl(pContext, shaderModule, pipeline, startTime); return getShaderLibraryImpl(pContext, shaderModule, pipeline, pShaderFeedback, startTime);
} }
} }
MVKShaderLibrary* MVKPipelineCache::getShaderLibraryImpl(SPIRVToMSLConversionConfiguration* pContext, MVKShaderLibrary* MVKPipelineCache::getShaderLibraryImpl(SPIRVToMSLConversionConfiguration* pContext,
MVKShaderModule* shaderModule, MVKShaderModule* shaderModule,
MVKPipeline* pipeline, MVKPipeline* pipeline,
VkPipelineCreationFeedback* pShaderFeedback,
uint64_t startTime) { uint64_t startTime) {
bool wasAdded = false; bool wasAdded = false;
MVKShaderLibraryCache* slCache = getShaderLibraryCache(shaderModule->getKey()); 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(); } if (wasAdded) { markDirty(); }
else if (pShaderFeedback) { mvkEnableFlags(pShaderFeedback->flags, VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT); }
return shLib; return shLib;
} }

View File

@ -107,7 +107,9 @@ protected:
friend MVKShaderLibraryCache; friend MVKShaderLibraryCache;
friend MVKShaderModule; 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); void handleCompilationError(NSError* err, const char* opDesc);
MTLFunctionConstant* getFunctionConstant(NSArray<MTLFunctionConstant*>* mtlFCs, NSUInteger mtlFCID); MTLFunctionConstant* getFunctionConstant(NSArray<MTLFunctionConstant*>* mtlFCs, NSUInteger mtlFCID);
void compileLibrary(const std::string& msl); void compileLibrary(const std::string& msl);
@ -144,7 +146,8 @@ public:
*/ */
MVKShaderLibrary* getShaderLibrary(SPIRVToMSLConversionConfiguration* pShaderConfig, MVKShaderLibrary* getShaderLibrary(SPIRVToMSLConversionConfiguration* pShaderConfig,
MVKShaderModule* shaderModule, MVKPipeline* pipeline, MVKShaderModule* shaderModule, MVKPipeline* pipeline,
bool* pWasAdded, uint64_t startTime = 0); bool* pWasAdded, VkPipelineCreationFeedback* pShaderFeedback,
uint64_t startTime = 0);
MVKShaderLibraryCache(MVKVulkanAPIDeviceObject* owner) : _owner(owner) {}; MVKShaderLibraryCache(MVKVulkanAPIDeviceObject* owner) : _owner(owner) {};
@ -155,7 +158,9 @@ protected:
friend MVKPipelineCache; friend MVKPipelineCache;
friend MVKShaderModule; 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, MVKShaderLibrary* addShaderLibrary(const SPIRVToMSLConversionConfiguration* pShaderConfig,
const SPIRVToMSLConversionResult& conversionResult); const SPIRVToMSLConversionResult& conversionResult);
MVKShaderLibrary* addShaderLibrary(const SPIRVToMSLConversionConfiguration* pShaderConfig, MVKShaderLibrary* addShaderLibrary(const SPIRVToMSLConversionConfiguration* pShaderConfig,
@ -207,7 +212,8 @@ public:
/** Returns the Metal shader function, possibly specialized. */ /** Returns the Metal shader function, possibly specialized. */
MVKMTLFunction getMTLFunction(SPIRVToMSLConversionConfiguration* pShaderConfig, MVKMTLFunction getMTLFunction(SPIRVToMSLConversionConfiguration* pShaderConfig,
const VkSpecializationInfo* pSpecializationInfo, const VkSpecializationInfo* pSpecializationInfo,
MVKPipeline* pipeline); MVKPipeline* pipeline,
VkPipelineCreationFeedback* pShaderFeedback);
/** Convert the SPIR-V to MSL, using the specified shader conversion configuration. */ /** Convert the SPIR-V to MSL, using the specified shader conversion configuration. */
bool convert(SPIRVToMSLConversionConfiguration* pShaderConfig, bool convert(SPIRVToMSLConversionConfiguration* pShaderConfig,

View File

@ -67,7 +67,9 @@ static uint32_t getWorkgroupDimensionSize(const SPIRVWorkgroupSizeDimension& wgD
return wgDim.size; 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; } if ( !_mtlLibrary ) { return MVKMTLFunctionNull; }
@ -76,9 +78,15 @@ MVKMTLFunction MVKShaderLibrary::getMTLFunction(const VkSpecializationInfo* pSpe
NSString* mtlFuncName = @(_shaderConversionResultInfo.entryPoint.mtlFunctionName.c_str()); NSString* mtlFuncName = @(_shaderConversionResultInfo.entryPoint.mtlFunctionName.c_str());
MVKDevice* mvkDev = _owner->getDevice(); MVKDevice* mvkDev = _owner->getDevice();
uint64_t startTime = mvkDev->getPerformanceTimestamp(); uint64_t startTime = pShaderFeedback ? mvkGetTimestamp() : mvkDev->getPerformanceTimestamp();
id<MTLFunction> mtlFunc = [[_mtlLibrary newFunctionWithName: mtlFuncName] autorelease]; id<MTLFunction> mtlFunc = [[_mtlLibrary newFunctionWithName: mtlFuncName] autorelease];
mvkDev->addActivityPerformance(mvkDev->_performanceStatistics.shaderCompilation.functionRetrieval, startTime); 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 (mtlFunc) {
// If the Metal device supports shader specialization, and the Metal function expects to be specialized, // 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. // Compile the specialized Metal function, and use it instead of the unspecialized Metal function.
MVKFunctionSpecializer fs(_owner); MVKFunctionSpecializer fs(_owner);
if (pShaderFeedback) {
startTime = mvkGetTimestamp();
}
mtlFunc = [fs.newMTLFunction(_mtlLibrary, mtlFuncName, mtlFCVals) autorelease]; mtlFunc = [fs.newMTLFunction(_mtlLibrary, mtlFuncName, mtlFCVals) autorelease];
if (pShaderFeedback) {
pShaderFeedback->duration += mvkGetElapsedNanoseconds(startTime);
}
} }
} }
} }
@ -240,13 +254,17 @@ MVKShaderLibrary::~MVKShaderLibrary() {
MVKShaderLibrary* MVKShaderLibraryCache::getShaderLibrary(SPIRVToMSLConversionConfiguration* pShaderConfig, MVKShaderLibrary* MVKShaderLibraryCache::getShaderLibrary(SPIRVToMSLConversionConfiguration* pShaderConfig,
MVKShaderModule* shaderModule, MVKPipeline* pipeline, MVKShaderModule* shaderModule, MVKPipeline* pipeline,
bool* pWasAdded, uint64_t startTime) { bool* pWasAdded, VkPipelineCreationFeedback* pShaderFeedback,
uint64_t startTime) {
bool wasAdded = false; bool wasAdded = false;
MVKShaderLibrary* shLib = findShaderLibrary(pShaderConfig, startTime); MVKShaderLibrary* shLib = findShaderLibrary(pShaderConfig, pShaderFeedback, startTime);
if ( !shLib && !pipeline->shouldFailOnPipelineCompileRequired() ) { if ( !shLib && !pipeline->shouldFailOnPipelineCompileRequired() ) {
SPIRVToMSLConversionResult conversionResult; SPIRVToMSLConversionResult conversionResult;
if (shaderModule->convert(pShaderConfig, conversionResult)) { if (shaderModule->convert(pShaderConfig, conversionResult)) {
shLib = addShaderLibrary(pShaderConfig, conversionResult); shLib = addShaderLibrary(pShaderConfig, conversionResult);
if (pShaderFeedback) {
pShaderFeedback->duration += mvkGetElapsedNanoseconds(startTime);
}
wasAdded = true; 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. // 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. // If a match is found, the shader config is aligned with the shader config of the matching library.
MVKShaderLibrary* MVKShaderLibraryCache::findShaderLibrary(SPIRVToMSLConversionConfiguration* pShaderConfig, MVKShaderLibrary* MVKShaderLibraryCache::findShaderLibrary(SPIRVToMSLConversionConfiguration* pShaderConfig,
VkPipelineCreationFeedback* pShaderFeedback,
uint64_t startTime) { uint64_t startTime) {
for (auto& slPair : _shaderLibraries) { for (auto& slPair : _shaderLibraries) {
if (slPair.first.matches(*pShaderConfig)) { if (slPair.first.matches(*pShaderConfig)) {
pShaderConfig->alignWith(slPair.first); pShaderConfig->alignWith(slPair.first);
MVKDevice* mvkDev = _owner->getDevice(); MVKDevice* mvkDev = _owner->getDevice();
mvkDev->addActivityPerformance(mvkDev->_performanceStatistics.shaderCompilation.shaderLibraryFromCache, startTime); mvkDev->addActivityPerformance(mvkDev->_performanceStatistics.shaderCompilation.shaderLibraryFromCache, startTime);
if (pShaderFeedback) {
pShaderFeedback->duration += mvkGetElapsedNanoseconds(startTime);
}
return slPair.second; return slPair.second;
} }
} }
@ -309,23 +331,24 @@ MVKShaderLibraryCache::~MVKShaderLibraryCache() {
MVKMTLFunction MVKShaderModule::getMTLFunction(SPIRVToMSLConversionConfiguration* pShaderConfig, MVKMTLFunction MVKShaderModule::getMTLFunction(SPIRVToMSLConversionConfiguration* pShaderConfig,
const VkSpecializationInfo* pSpecializationInfo, const VkSpecializationInfo* pSpecializationInfo,
MVKPipeline* pipeline) { MVKPipeline* pipeline,
VkPipelineCreationFeedback* pShaderFeedback) {
MVKShaderLibrary* mvkLib = _directMSLLibrary; MVKShaderLibrary* mvkLib = _directMSLLibrary;
if ( !mvkLib ) { if ( !mvkLib ) {
uint64_t startTime = _device->getPerformanceTimestamp(); uint64_t startTime = pShaderFeedback ? mvkGetTimestamp() : _device->getPerformanceTimestamp();
MVKPipelineCache* pipelineCache = pipeline->getPipelineCache(); MVKPipelineCache* pipelineCache = pipeline->getPipelineCache();
if (pipelineCache) { if (pipelineCache) {
mvkLib = pipelineCache->getShaderLibrary(pShaderConfig, this, pipeline, startTime); mvkLib = pipelineCache->getShaderLibrary(pShaderConfig, this, pipeline, pShaderFeedback, startTime);
} else { } else {
lock_guard<mutex> lock(_accessLock); lock_guard<mutex> lock(_accessLock);
mvkLib = _shaderLibraryCache.getShaderLibrary(pShaderConfig, this, pipeline, nullptr, startTime); mvkLib = _shaderLibraryCache.getShaderLibrary(pShaderConfig, this, pipeline, nullptr, pShaderFeedback, startTime);
} }
} else { } else {
mvkLib->setEntryPointName(pShaderConfig->options.entryPointName); mvkLib->setEntryPointName(pShaderConfig->options.entryPointName);
pShaderConfig->markAllInterfaceVarsAndResourcesUsed(); pShaderConfig->markAllInterfaceVarsAndResourcesUsed();
} }
return mvkLib ? mvkLib->getMTLFunction(pSpecializationInfo, this) : MVKMTLFunctionNull; return mvkLib ? mvkLib->getMTLFunction(pSpecializationInfo, pShaderFeedback, this) : MVKMTLFunctionNull;
} }
bool MVKShaderModule::convert(SPIRVToMSLConversionConfiguration* pShaderConfig, bool MVKShaderModule::convert(SPIRVToMSLConversionConfiguration* pShaderConfig,

View File

@ -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_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_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_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_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_private_data, EXT_PRIVATE_DATA, DEVICE, 10.11, 8.0)
MVK_EXTENSION(EXT_robustness2, EXT_ROBUSTNESS_2, DEVICE, 10.11, 8.0) MVK_EXTENSION(EXT_robustness2, EXT_ROBUSTNESS_2, DEVICE, 10.11, 8.0)