Skip to content

Conversation

@DarioSamo
Copy link
Contributor

@DarioSamo DarioSamo commented Oct 9, 2025

Disclaimer

re-spirv is a project I started with another contributor on our own with the express purpose of doing shader optimization in SPIR-V in real time environments. The project uses the MIT License and should be compatible with Godot's licensing. This PR adds a new dependency to the third party folder.

Background

Since the ubershaders PR was merged, Godot has been making use of specialization constants to heavily eliminate parts of code that are unused by materials depending on the environment's configuration or proximity to nodes such as lights or reflection probes. This is specially important in the mobile renderer, which will generate specializations based on the amount of lights close to it and compile them on the fly on the background while relying on the ubershader to display it while it's getting ready.

Specialization constants are used as the main flags for eliminating unused code, but not all drivers will prioritize eliminating code based on them first. re-spirv was developed with the explicit goal of prioritizing DCE based on eliminating code branches that can be determined to be dead once the values for the constants are known. As measured in that project and in Godot itself, it is possible to get significant reductions in pipeline creation time by eliminating code from the SPIR-V instead on the application side at the cost of some very small extra processing time.

Why not apply spirv-opt instead?

We should eventually add shader optimization to Godot using spirv-opt. There's even a PR here which adds it and gives substantial benefits to shader sizes and pipeline creation times too. spirv-opt even features options to freeze specialization constants to certain values and run optimization passes.

However, the performance leaves a lot to be desired and is basically not applicable for real-time cases. While re-spirv is unlikely to ever feature the great size reductions that spirv-opt can achieve, the size reduction that is possible is very much worth it for the very little extra processing time it adds to the pipeline creation.

At the time being, I think spirv-opt will be best suited for optimizing shaders when using the new shader baker option, which can afford to take more processing time during a step where the user does not expect real-time performance.

For purposes of comparison to spirv-opt, re-spirv currently only features the following optimizations:

  • Specialization Constant Patching
  • Constant Evaluation & Branch Pruning
  • Dead Code Elimination
  • Single Store & Load Elimination
  • Single Block Store & Load Elimination

Procedure

We have multiple possible places to apply SPIR-V optimization, and for the purposes of this PR, it has been moved to live solely inside the Vulkan driver. Some benefits have been found by running re-spirv for non-specialized shaders because they're currently compiled without optimizations, but I believe it would be best suited to rely on spirv-opt at some point in the future to optimize these shaders and leave re-spirv solely for specialized shaders.

The implementation of re-spirv in the driver is very simple: a parsing step is performed during shader creation to generate the analysis DAG that will be used during optimization. The results of this step are reusable and the cost only has to be paid once per shader and isn't paid again for each specialization that is created.

Before a pipeline is created, the optimizer is called with the known values of the specialization constants. The resulting SPIR-V is passed to the driver to create the pipeline. This means a new unique SPIR-V is generated on the fly and there's no need for the driver to parse the specialization constants.

Results

In theory, we shouldn't be seeing substantial decreases in pipeline creation time, as drivers already apply several optimization steps to achieve optimal shader performance when converting the SPIR-V to actual GPU code. However, not all drivers are built the same, and not all of them may prioritize the passes featured by re-spirv which are known to be the most effective first given the nature of the shaders. These drivers may reach the optimal point by applying different passes first or may pay a higher price from converting SPIR-V as-is to their own intermediate format first.

Some of these measurements might be within a margin of error due to the highly parallelized nature of the workload depending on the system and the driver in question. However, there's hardware that shows significant improvements and it should make it clear as to why we pursued this path.

Not all of these were measured using the same project or renderer so the numerical values can't be compared between platforms. The main purpose of the chart is to show the reductions on each platform. For example, the NVIDIA measurement was done on a much larger project using Forward+ to better represent the improvements on a project that fits the platform's demands better.

Average Adreno 640 Quest 3 (Adreno 740) Samsung S25 (Adreno 830) Samsung S21 (Mali-G78) Intel Xe (Mesa) NVIDIA RTX 3090 Ti
Specializations (master) 285 ms 235 ms 21.4 ms 49.0 ms 146 ms 124 ms
Specializations (re-spirv) 138 ms 114 ms 18.6 ms 43.7 ms 136 ms 114 ms
Regular (master) 799 ms 444 ms 63.2 ms 161 ms 289 ms 267 ms
Regular (re-spirv) 868 ms 446 ms 60.4 ms 133 ms 298 ms 257 ms
  • The times for Adreno are one of the main reasons behind this PR. Older Adreno drivers see over a 2x reduction in pipeline compilation times, making the games run better much faster than before and using a lot less power in the process. Given driver updates are not an option usually available on these platforms, we see a very big incentive to have this optimizer built into Godot if we wish to target these devices.
  • We see newer devices with more up-to-date drivers don't get nearly as much of a benefit as expected. However, re-spirv having any kind of improvement in these drivers means there's potential room for optimizations by vendors in these areas to further reduce pipeline creation times.
  • Some benefits can be achieved consistently on some platforms when applying re-spirv to regular shaders (e.g. without specialization constants), but it doesn't seem this benefit is consistent across all platforms. However, this could be attributed to a measurement variance rather than a consistent error. We could potentially discard this item if we decide to rely on spirv-opt instead to optimize the shader during shader baking.
  • One detail that wasn't included in these results is that re-spirv will cause a small performance regression if the pipelines are already cached. However, the regression has been measured to be in the order of less than one millisecond per pipeline, and given these pipelines are compiled on the background, this can be considered an acceptable cost to pay given the reduction on the uncached cases.

Testing

  • Pipeline caches from the project need to be cleared if enabled before testing. Some crude macros in the form of PRINT_PIPELINE_COMPILATION_TIMES are provided on the source code itself, but my hope is to further polish the measuring and provide proper monitors and statistics as the PR progresses more.
  • re-spirv has not been really used in production yet in any big projects, so it is very possible visual regressions are incorrectly introduced. Any regressions should be reported as they'd invalidate some of the findings so far and should be promptly fixed before merging is considered.

TODO

  • re-spirv should be disabled when using shader debugging explicitly through the command line option. Otherwise, the code elimination would make it impossible to debug the shader correctly.
  • A Vulkan header makes for most of the code included in this PR. It's my understanding that we already have a Vulkan header elsewhere in the project that we could use, although I've not verified yet if it includes everything re-spirv needs. If it doesn't, we could probably just update that header instead and make re-spirv use it.
  • Expand benchmarking with proper monitoring and correlation of shaders to their pipeline times, shader size reductions, and allow dumping this data to analyze it more effectively.
  • Determine whether we should apply re-spirv to regular non-specialized shaders or not based on the findings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants