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:
parent
2db85ea060
commit
561e14ba62
@ -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().
|
||||
|
@ -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; }
|
||||
|
@ -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<MTLComputePipelineState> getTessVertexStageState();
|
||||
id<MTLComputePipelineState> getTessVertexStageState() { return _mtlTessVertexStageState; }
|
||||
|
||||
/** 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. */
|
||||
id<MTLComputePipelineState> getTessVertexStageIndex32State();
|
||||
id<MTLComputePipelineState> getTessVertexStageIndex32State() { return _mtlTessVertexStageIndex32State; }
|
||||
|
||||
/** Returns the MTLComputePipelineState object for the tessellation control stage of a tessellated draw. */
|
||||
id<MTLComputePipelineState> getTessControlStageState() { return _mtlTessControlStageState; }
|
||||
@ -318,22 +318,24 @@ protected:
|
||||
|
||||
id<MTLRenderPipelineState> getOrCompilePipeline(MTLRenderPipelineDescriptor* plDesc, id<MTLRenderPipelineState>& plState);
|
||||
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 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<MTLFunction>* 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<MTLFunction>* 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<class T>
|
||||
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<MVKShaderStage> _stagesUsingPhysicalStorageBufferAddressesCapability;
|
||||
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> _mtlTessVertexStageIndex16State = nil;
|
||||
id<MTLComputePipelineState> _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<MTLComputePipelineState> _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);
|
||||
|
@ -337,42 +337,54 @@ bool MVKGraphicsPipeline::supportsDynamicState(VkDynamicState state) {
|
||||
|
||||
static const char vtxCompilerType[] = "Vertex stage pipeline for tessellation";
|
||||
|
||||
id<MTLComputePipelineState> MVKGraphicsPipeline::getTessVertexStageState() {
|
||||
MTLComputePipelineDescriptor* plDesc = [_mtlTessVertexStageDesc copy]; // temp retain a copy to be thread-safe.
|
||||
plDesc.computeFunction = _mtlTessVertexFunctions[0];
|
||||
id<MTLComputePipelineState> plState = getOrCompilePipeline(plDesc, _mtlTessVertexStageState, vtxCompilerType);
|
||||
[plDesc release]; // temp release
|
||||
return plState;
|
||||
}
|
||||
bool MVKGraphicsPipeline::compileTessVertexStageState(MTLComputePipelineDescriptor* vtxPLDesc,
|
||||
id<MTLFunction>* vtxFunctions,
|
||||
VkPipelineCreationFeedback* pVertexFB) {
|
||||
uint64_t startTime = 0;
|
||||
if (pVertexFB) {
|
||||
startTime = mvkGetTimestamp();
|
||||
}
|
||||
vtxPLDesc.computeFunction = vtxFunctions[0];
|
||||
bool res = !!getOrCompilePipeline(vtxPLDesc, _mtlTessVertexStageState, vtxCompilerType);
|
||||
|
||||
id<MTLComputePipelineState> 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<MTLComputePipelineState> 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<MTLComputePipelineState> 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<MTLComputePipelineState> 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<MTLFunction> 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<MTLFunction>* 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<MTLFunction> 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<MTLFunction>* 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<MTLFunction> 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<MTLFunction> 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<MTLFunction> 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<MTLFunction> 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<MTLFunction> 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<mutex> 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;
|
||||
}
|
||||
|
||||
|
@ -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<MTLFunctionConstant*>* 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,
|
||||
|
@ -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<MTLFunction> 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<mutex> 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,
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user