Use spirv-tools optimizer in GraphicsPipeline
Run some optimization passes before lowering the code, to allow for a
simpler compiler.
Current passes:
- Exhaustively inline functions
- Apply and freeze specialization constants
I expect this set of passes will change somewhat as the
compiler evolves. We may want to stop inlining at some point.
Bug: b/124058197
Bug: b/124056625
Change-Id: Ib2a3dc2d2599449d45ac7ebb1de107deba8eb2c3
Reviewed-on: https://swiftshader-review.googlesource.com/c/24288
Tested-by: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Vulkan/VkPipeline.cpp b/src/Vulkan/VkPipeline.cpp
index 103720c..6fd96df 100644
--- a/src/Vulkan/VkPipeline.cpp
+++ b/src/Vulkan/VkPipeline.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include <Pipeline/SpirvShader.hpp>
+#include <spirv-tools/optimizer.hpp>
#include "VkPipeline.hpp"
#include "VkShaderModule.hpp"
@@ -382,28 +383,50 @@
void GraphicsPipeline::compileShaders(const VkAllocationCallbacks* pAllocator, const VkGraphicsPipelineCreateInfo* pCreateInfo)
{
- for (auto pStage = pCreateInfo->pStages; pStage != pCreateInfo->pStages + pCreateInfo->stageCount; pStage++) {
+ for (auto pStage = pCreateInfo->pStages; pStage != pCreateInfo->pStages + pCreateInfo->stageCount; pStage++)
+ {
auto module = Cast(pStage->module);
- // TODO: apply prep passes using SPIRV-Opt here.
- // - Apply and freeze specializations, etc.
auto code = module->getCode();
+ spvtools::Optimizer opt{SPV_ENV_VULKAN_1_1};
+ opt.RegisterPass(spvtools::CreateInlineExhaustivePass());
- // TODO: pass in additional information here:
- // - any NOS from pCreateInfo which we'll actually need
- auto spirvShader = new sw::SpirvShader{code};
+ // If the pipeline uses specialization, apply the specializations before freezing
+ if (pStage->pSpecializationInfo)
+ {
+ std::unordered_map<uint32_t, std::vector<uint32_t>> specializations;
+ for (auto i = 0u; i < pStage->pSpecializationInfo->mapEntryCount; ++i)
+ {
+ auto const &e = pStage->pSpecializationInfo->pMapEntries[i];
+ auto value_ptr =
+ static_cast<uint32_t const *>(pStage->pSpecializationInfo->pData) + e.offset / sizeof(uint32_t);
+ specializations.emplace(e.constantID,
+ std::vector<uint32_t>{value_ptr, value_ptr + e.size / sizeof(uint32_t)});
+ }
+ opt.RegisterPass(spvtools::CreateSetSpecConstantDefaultValuePass(specializations));
+ }
+ // Freeze specialization constants into normal constants, and propagate through
+ opt.RegisterPass(spvtools::CreateFreezeSpecConstantValuePass());
+ opt.RegisterPass(spvtools::CreateFoldSpecConstantOpAndCompositePass());
- switch (pStage->stage) {
- case VK_SHADER_STAGE_VERTEX_BIT:
- context.vertexShader = vertexShader = spirvShader;
- break;
+ std::vector<uint32_t> postOptCode;
+ opt.Run(code.data(), code.size(), &postOptCode);
- case VK_SHADER_STAGE_FRAGMENT_BIT:
- context.pixelShader = fragmentShader = spirvShader;
- break;
+ // TODO: also pass in any pipeline state which will affect shader compilation
+ auto spirvShader = new sw::SpirvShader{postOptCode};
- default:
- UNIMPLEMENTED("Unsupported stage");
+ switch (pStage->stage)
+ {
+ case VK_SHADER_STAGE_VERTEX_BIT:
+ context.vertexShader = vertexShader = spirvShader;
+ break;
+
+ case VK_SHADER_STAGE_FRAGMENT_BIT:
+ context.pixelShader = fragmentShader = spirvShader;
+ break;
+
+ default:
+ UNIMPLEMENTED("Unsupported stage");
}
}
}