From b420d58b595e4f2e9fdcca89d83952833ae9dec3 Mon Sep 17 00:00:00 2001 From: Evan Tang Date: Mon, 24 Apr 2023 13:23:23 -0500 Subject: [PATCH] Add option to dump shaders --- Docs/MoltenVK_Configuration_Parameters.md | 11 ++++++ MoltenVK/MoltenVK/API/mvk_private_api.h | 1 + MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm | 29 +++++++++++++++ .../MoltenVK/GPUObjects/MVKShaderModule.mm | 37 +++++++++++++++++++ .../MoltenVK/Utility/MVKConfigMembers.def | 1 + MoltenVK/MoltenVK/Utility/MVKEnvironment.h | 9 ++++- 6 files changed, 87 insertions(+), 1 deletion(-) diff --git a/Docs/MoltenVK_Configuration_Parameters.md b/Docs/MoltenVK_Configuration_Parameters.md index 5e02256d..3b786edc 100644 --- a/Docs/MoltenVK_Configuration_Parameters.md +++ b/Docs/MoltenVK_Configuration_Parameters.md @@ -676,3 +676,14 @@ features that are difficult to support otherwise. Unlike `MVK_USE_METAL_PRIVATE_API`, this setting may be overridden at run time. This option is not available unless MoltenVK were built with `MVK_USE_METAL_PRIVATE_API` set to `1`. + +--------------------------------------- +#### MVK_CONFIG_SHADER_DUMP_DIR + +##### Type: String +##### Default: `""` + +_(The default value is an empty string)._ + +If not empty, MoltenVK will dump all SPIRV shaders, compiled MSL shaders, and pipeline shader lists to the given directory. +The directory will be non-recursively created if it doesn't already exist. diff --git a/MoltenVK/MoltenVK/API/mvk_private_api.h b/MoltenVK/MoltenVK/API/mvk_private_api.h index 8f4ab832..de1cdc9c 100644 --- a/MoltenVK/MoltenVK/API/mvk_private_api.h +++ b/MoltenVK/MoltenVK/API/mvk_private_api.h @@ -244,6 +244,7 @@ typedef struct { VkBool32 shouldMaximizeConcurrentCompilation; /**< MVK_CONFIG_SHOULD_MAXIMIZE_CONCURRENT_COMPILATION */ float timestampPeriodLowPassAlpha; /**< MVK_CONFIG_TIMESTAMP_PERIOD_LOWPASS_ALPHA */ VkBool32 useMetalPrivateAPI; /**< MVK_CONFIG_USE_METAL_PRIVATE_API */ + const char* shaderDumpDir; /**< MVK_CONFIG_SHADER_DUMP_DIR */ } MVKConfiguration; // Legacy support for renamed struct elements. diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm index 37926253..02170359 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm @@ -27,6 +27,7 @@ #include "MTLRenderPipelineColorAttachmentDescriptor+MoltenVK.h" #endif #include "mvk_datatypes.hpp" +#include #ifndef MVK_USE_CEREAL #define MVK_USE_CEREAL (1) @@ -685,6 +686,34 @@ void MVKGraphicsPipeline::initMTLRenderPipelineState(const VkGraphicsPipelineCre if (isUsingMetalArgumentBuffers()) { _descriptorBindingUse.resize(_descriptorSetCount); } if (isUsingPipelineStageMetalArgumentBuffers()) { _mtlArgumentEncoders.resize(_descriptorSetCount); } + const char* dumpDir = getMVKConfig().shaderDumpDir; + if (dumpDir && *dumpDir) { + char filename[PATH_MAX]; + char text[1024]; + char* ptext = text; + size_t full_hash = 0; + const char* type = pTessCtlSS && pTessEvalSS ? "-tess" : ""; + auto addShader = [&](const char* type, const VkPipelineShaderStageCreateInfo* ss) { + if (!ss) { + return; + } + size_t hash = reinterpret_cast(ss->module)->getKey().codeHash; + full_hash = full_hash * 33 ^ hash; + ptext = std::min(ptext + snprintf(ptext, std::end(text) - ptext, "%s: %016zx\n", type, hash), std::end(text) - 1); + }; + addShader(" VS", pVertexSS); + addShader("TCS", pTessCtlSS); + addShader("TES", pTessEvalSS); + addShader(" FS", pFragmentSS); + mkdir(dumpDir, 0755); + snprintf(filename, sizeof(filename), "%s/pipeline%s-%016zx.txt", dumpDir, type, full_hash); + FILE* file = fopen(filename, "w"); + if (file) { + fwrite(text, 1, ptext - text, file); + fclose(file); + } + } + if (!isTessellationPipeline()) { MTLRenderPipelineDescriptor* plDesc = newMTLRenderPipelineDescriptor(pCreateInfo, reflectData, pVertexSS, pVertexFB, pFragmentSS, pFragmentFB); // temp retain if (plDesc) { diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm index 7c3aa338..0d02baae 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm @@ -19,6 +19,7 @@ #include "MVKShaderModule.h" #include "MVKPipeline.h" #include "MVKFoundation.h" +#include using namespace std; @@ -379,6 +380,42 @@ bool MVKShaderModule::convert(SPIRVToMSLConversionConfiguration* pShaderConfig, bool wasConverted = _spvConverter.convert(*pShaderConfig, conversionResult, shouldLogCode, shouldLogCode, shouldLogEstimatedGLSL); _device->addPerformanceInterval(_device->_performanceStatistics.shaderCompilation.spirvToMSL, startTime); + const char* dumpDir = getMVKConfig().shaderDumpDir; + if (dumpDir && *dumpDir) { + char path[PATH_MAX]; + const char* type; + switch (pShaderConfig->options.entryPointStage) { + case spv::ExecutionModelVertex: type = "-vs"; break; + case spv::ExecutionModelTessellationControl: type = "-tcs"; break; + case spv::ExecutionModelTessellationEvaluation: type = "-tes"; break; + case spv::ExecutionModelFragment: type = "-fs"; break; + case spv::ExecutionModelGeometry: type = "-gs"; break; + case spv::ExecutionModelTaskNV: type = "-ts"; break; + case spv::ExecutionModelMeshNV: type = "-ms"; break; + case spv::ExecutionModelGLCompute: type = "-cs"; break; + default: type = ""; break; + } + mkdir(dumpDir, 0755); + snprintf(path, sizeof(path), "%s/shader%s-%016zx.spv", dumpDir, type, _key.codeHash); + FILE* file = fopen(path, "wb"); + if (file) { + fwrite(_spvConverter.getSPIRV().data(), sizeof(uint32_t), _spvConverter.getSPIRV().size(), file); + fclose(file); + } + snprintf(path, sizeof(path), "%s/shader%s-%016zx.metal", dumpDir, type, _key.codeHash); + file = fopen(path, "wb"); + if (file) { + if (wasConverted) { + fwrite(conversionResult.msl.data(), 1, conversionResult.msl.size(), file); + fclose(file); + } else { + fputs("Failed to convert:\n", file); + fwrite(conversionResult.resultLog.data(), 1, conversionResult.resultLog.size(), file); + fclose(file); + } + } + } + if (wasConverted) { if (shouldLogCode) { MVKLogInfo("%s", conversionResult.resultLog.c_str()); } } else { diff --git a/MoltenVK/MoltenVK/Utility/MVKConfigMembers.def b/MoltenVK/MoltenVK/Utility/MVKConfigMembers.def index b1c08b66..9c7f2913 100644 --- a/MoltenVK/MoltenVK/Utility/MVKConfigMembers.def +++ b/MoltenVK/MoltenVK/Utility/MVKConfigMembers.def @@ -83,6 +83,7 @@ MVK_CONFIG_MEMBER(shaderSourceCompressionAlgorithm, MVKConfigCompressionAl MVK_CONFIG_MEMBER(shouldMaximizeConcurrentCompilation, VkBool32, SHOULD_MAXIMIZE_CONCURRENT_COMPILATION) MVK_CONFIG_MEMBER(timestampPeriodLowPassAlpha, float, TIMESTAMP_PERIOD_LOWPASS_ALPHA) MVK_CONFIG_MEMBER(useMetalPrivateAPI, VkBool32, USE_METAL_PRIVATE_API) +MVK_CONFIG_MEMBER_STRING(shaderDumpDir, char*, SHADER_DUMP_DIR) #undef MVK_CONFIG_MEMBER #undef MVK_CONFIG_MEMBER_STRING diff --git a/MoltenVK/MoltenVK/Utility/MVKEnvironment.h b/MoltenVK/MoltenVK/Utility/MVKEnvironment.h index b131ca38..75a4f67e 100644 --- a/MoltenVK/MoltenVK/Utility/MVKEnvironment.h +++ b/MoltenVK/MoltenVK/Utility/MVKEnvironment.h @@ -86,7 +86,7 @@ #ifdef __cplusplus /** The number of members of MVKConfiguration that are strings. */ -static constexpr uint32_t kMVKConfigurationStringCount = 1; +static constexpr uint32_t kMVKConfigurationStringCount = 2; /** Global function to access MoltenVK configuration info. */ const MVKConfiguration& getGlobalMVKConfig(); @@ -357,5 +357,12 @@ void mvkSetConfig(MVKConfiguration& dstMVKConfig, const MVKConfiguration& srcMVK # define MVK_CONFIG_USE_METAL_PRIVATE_API MVK_USE_METAL_PRIVATE_API #endif +/** + * If set, MVK will dump spirv input, translated msl, and pipelines into the given directory. + */ +#ifndef MVK_CONFIG_SHADER_DUMP_DIR +# define MVK_CONFIG_SHADER_DUMP_DIR "" +#endif + #undef MVK_CONFIG__UNUSED_STRUCT_PADDING #define MVK_CONFIG__UNUSED_STRUCT_PADDING 0