GPU: Use lazy checks for specialization state
This PR adds a new class, the SpecializationStateUpdater, that allows elements of specialization state to be updated individually, and signal the state is checked when it changes between draws, instead of building and checking it on every draw. This also avoids building spec state switching shaders.
Most state updates have been moved behind the shader state update, so that their specialization state updates make it in before shaders are fetched.
Downside: Fields in GpuChannelGraphicsState are no longer readonly. To counteract copies that might be caused this I pass it as ref when possible, though maybe in would be better? Not really sure about the quirks of in and the difference probably won't show on a benchmark.
The result is around 2 extra FPS on SMO here in the usual spot. Not much right now, but it will remove costs when we're doing more expensive specialization checks, such as fragment output type specialization for macos. It may also help more on other games with more draws.
Download the artifacts for this pull request:
- ryujinx-Release-1.1.0+389b488-linux_x64
- ryujinx-Release-1.1.0+389b488-osx_x64
- ryujinx-Release-1.1.0+389b488-win_x64
Experimental GUI (Avalonia)
GUI-less (SDL2)
Only for Developers
- ava-ryujinx-Debug-1.1.0+389b488-linux_x64
- ava-ryujinx-Debug-1.1.0+389b488-osx_x64
- ava-ryujinx-Debug-1.1.0+389b488-win_x64
- ryujinx-Debug-1.1.0+389b488-linux_x64
- ryujinx-Debug-1.1.0+389b488-osx_x64
- ryujinx-Debug-1.1.0+389b488-win_x64
- sdl2-ryujinx-headless-Debug-1.1.0+389b488-linux_x64
- sdl2-ryujinx-headless-Debug-1.1.0+389b488-osx_x64
- sdl2-ryujinx-headless-Debug-1.1.0+389b488-win_x64
I assumed since the spec check was done there before, the state update had already happened, but that wouldn't make sense as it sets the shader dirty flag. I've updated it to reload after (UpdateShaderState rather than set the flag), and added a comment about how the behaviour should not change _vtgWritesRtLayer.
It kind of sucks, but I can at least explain how it works now:
- UpdateShaderState is up first. If the shader state is dirty, it does the full lookup with textures and everything, the correct shader is loaded for the current state of both.
- Check before bindings to see if graphics state is dirty after processing updates, and reloads the shader if it is different. If the shader was changed, this will always do nothing, as the state dirty flag was reset as it used the current state fo find the shader earlier.
- I wasn't comfortable combining this one with the texture bindings check, as that would mean that none of the bindings are allowed to change due to spec state. I'm not sure that would always be true for graphics spec state now or in the future.
- Texture bindings check as they are fetched, and reload the shader if different. If the shader was changed by either of the above two things, the texture state will already match.
At least it should only ever reload once, it's just a case of when.