Avoid recompiling identical SPIR-V code We were creating SpirvShader objects for every shader stage of the pipeline, each with their own unique serial ID. This caused us to compile the same SPIR-V code over an over again when multiple pipelines are created from the same shader module(s). This change essentially moves the serial ID to the shader module. Things that still require us to recompile code from the same shader module are the entry point specification, and specialization constants. The former is taken into account by using a 64-bit ID consisting of the module ID and entry point ID. For the latter we assume any use of specialization constants will result in a unique SPIR-V binary. This is conservative and may still lead to unnecessary recompiles. This change also minimizes the state passed to SpirvShader, to prevent specialization on state not taken into account by the routine caches. Bug: b/135609394 Tests: dEQP-VK.pipeline.render_to_image.core.*.huge.* Change-Id: I204e812265067462f8019af9f6b7b3067ef5dc7f Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/33109 Presubmit-Ready: Nicolas Capens <nicolascapens@google.com> Kokoro-Presubmit: kokoro <noreply+kokoro@google.com> Tested-by: Nicolas Capens <nicolascapens@google.com> Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Vulkan/VkPipeline.cpp b/src/Vulkan/VkPipeline.cpp index 971c7ec..a7f2a41 100644 --- a/src/Vulkan/VkPipeline.cpp +++ b/src/Vulkan/VkPipeline.cpp
@@ -152,8 +152,7 @@ return 0; } -// preprocessSpirv applies and freezes specializations into constants, inlines -// all functions and performs constant folding. +// preprocessSpirv applies and freezes specializations into constants, and inlines all functions. std::vector<uint32_t> preprocessSpirv( std::vector<uint32_t> const &code, VkSpecializationInfo const *specializationInfo) @@ -451,12 +450,16 @@ UNIMPLEMENTED("pStage->flags"); } - auto module = vk::Cast(pStage->module); + const ShaderModule *module = vk::Cast(pStage->module); auto code = preprocessSpirv(module->getCode(), pStage->pSpecializationInfo); + // If the pipeline has specialization constants, assume they're unique and + // use a new serial ID so the shader gets recompiled. + uint32_t codeSerialID = (pStage->pSpecializationInfo ? ShaderModule::nextSerialID() : module->getSerialID()); + // FIXME (b/119409619): use an allocator here so we can control all memory allocations // TODO: also pass in any pipeline state which will affect shader compilation - auto spirvShader = new sw::SpirvShader{pStage, code, vk::Cast(pCreateInfo->renderPass), pCreateInfo->subpass}; + auto spirvShader = new sw::SpirvShader(codeSerialID, pStage->stage, pStage->pName, code, vk::Cast(pCreateInfo->renderPass), pCreateInfo->subpass); switch (pStage->stage) { @@ -542,16 +545,21 @@ void ComputePipeline::compileShaders(const VkAllocationCallbacks* pAllocator, const VkComputePipelineCreateInfo* pCreateInfo) { - auto module = vk::Cast(pCreateInfo->stage.module); + auto &stage = pCreateInfo->stage; + const ShaderModule *module = vk::Cast(stage.module); - auto code = preprocessSpirv(module->getCode(), pCreateInfo->stage.pSpecializationInfo); + auto code = preprocessSpirv(module->getCode(), stage.pSpecializationInfo); ASSERT_OR_RETURN(code.size() > 0); ASSERT(shader == nullptr); - // FIXME(b/119409619): use allocator. - shader = new sw::SpirvShader(&pCreateInfo->stage, code, nullptr, 0); + // If the pipeline has specialization constants, assume they're unique and + // use a new serial ID so the shader gets recompiled. + uint32_t codeSerialID = (stage.pSpecializationInfo ? ShaderModule::nextSerialID() : module->getSerialID()); + + // TODO(b/119409619): use allocator. + shader = new sw::SpirvShader(codeSerialID, stage.stage, stage.pName, code, nullptr, 0); vk::DescriptorSet::Bindings descriptorSets; // FIXME(b/129523279): Delay code generation until invoke time. program = new sw::ComputeProgram(shader, layout, descriptorSets); program->generate();