Match failure.

Render State Caching

Reporter: RobertBColton  |  Status: closed  |  Last Modified: April 25, 2019, 05:25:53 PM

This is a problem that I've been thinking about for a while, and I think I may have found an idea that I like. So, newer graphics APIs like Vulkan put all of the render state in a single giant block and you have to basically recreate the entire state block all at once just to change one state. Direct3D11 has state blocks that are more granular, but Direct3D12 is apparently the same as Vulkan according to Rusky. That's obviously too inefficient, so what we would ideally do is just cache all of the render state generically. Then when the user goes to draw, we flush all of the render state at once.

This idea has advantages and disadvantages to it. The biggest advantage is that redundant state changes are ignored, and GM users are prone to making these, so that's a good thing. Another advantage is that it's clearly going to result in the simplification and deduplication of much of our graphics systems. Things like d3d_start will then be possible to move to General/ like Josh has always wanted. Now, one disadvantage I can see right now is that this will mean our graphics are no longer as hookable by user extensions. If a user extension draws its own vertex buffer it won't have any of our render state because none of it has been flushed to the device/context. But that's ok, because the same is true of GMS and we provide flush functions like they do.
https://docs2.yoyogames.com/source/_build/3_scripting/4_gml_reference/drawing/draw_flush.html

codecov[bot]  
>Codecov Report

Merging #1636 into master will increase coverage by 0.03%.
The diff coverage is 29.71%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1636      +/-   ##
==========================================
+ Coverage   18.82%   18.85%   +0.03%     
==========================================
  Files         169      165       -4     
  Lines       16782    16685      -97     
==========================================
- Hits         3159     3146      -13     
+ Misses      13623    13539      -84
Impacted Files Coverage Δ
...Asystem/SHELL/Graphics_Systems/General/GSmodel.cpp 47.67% <0%> (-0.47%) ⬇️
...Asystem/SHELL/Graphics_Systems/General/GSblend.cpp 0% <0%> (ø) ⬆️
...ions/ParticleSystems/PS_particle_bridge_fallback.h 0% <0%> (ø) ⬆️
...NIGMAsystem/SHELL/Graphics_Systems/General/GSd3d.h 0% <0%> (ø)
...em/SHELL/Graphics_Systems/OpenGL/GLtextures_impl.h 100% <100%> (ø) ⬆️
...ystem/SHELL/Graphics_Systems/OpenGL1/OPENGLStd.cpp 18% <100%> (-14.79%) ⬇️
...stem/SHELL/Graphics_Systems/OpenGL1/GLtextures.cpp 36.92% <100%> (+5.88%) ⬆️
...system/SHELL/Graphics_Systems/OpenGL1/GLvertex.cpp 69.23% <100%> (ø) ⬆️
...GMAsystem/SHELL/Graphics_Systems/General/GSd3d.cpp 11.76% <11.76%> (+11.76%) ⬆️
...system/SHELL/Graphics_Systems/General/GScolors.cpp 18.91% <19.71%> (+6.67%) ⬆️
... and 10 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 888e50b...1a43265. Read the comment docs.

EnigmaBot  

UNMATCHED: Regression tests have indicated that graphical changes have been introduced. Carefully review the following image comparison for anomalies and adjust the changeset accordingly.

fca67fc Master Diff
Image Diff Image Diff Screen Save

RobertBColton  

I want to document here an experiment I ran against 48ed55f with GM8.1 and GMS. I've discovered that this is apparently not how GM deals with the 3D shapes functions having repetition. They appear to either maintain their own internal state for repetition on batches or are resetting the state when the batch is flushed.

My experiment basically turns off interpolation. It then draws a d3d floor with repetition greater than 1. It then draws a d3d primitive where I specify repetition in the texture coordinates. What happens in both GM8.1 and GMSv1.4 is that the floor has repetition but the primitive has clamped texturing. What occurs in ENIGMA in this pull request is that both are drawn with repetition, while in master both are drawn without repetition. For now, I find this anomaly acceptable and I don't think many people will notice. We can look a little closer at it again if somebody brings it up, but for now, we know this pull request is no different than our current master.

d3d_set_projection_ortho(0,0,room_width,room_height,0);
var tex;
tex = background_get_texture(background0);

// initially set texture interpolation off
texture_set_repeat(false);
// following overrides texture repetition somehow for last 2 parameters
d3d_draw_floor(0,0,0, room_width/2,room_height,0,tex,3,3);
// lets see whether texture repetition is still on
d3d_primitive_begin_texture(pr_trianglestrip, tex);
d3d_vertex_texture(room_width/2,0,0,0,0);
d3d_vertex_texture(room_width/2,room_height,0,0,3);
d3d_vertex_texture(room_width,0,0,3,0);
d3d_vertex_texture(room_width,room_height,0,3,3);
d3d_primitive_end();

RobertBColton  

Leaving some more research here for people from the future. I did have hugar test out these changes on Linux and he has older drivers that don't support GL2. He didn't have a single problem with these changes by the end of this when I had all the regressions fixed.

I also did some research to discover that Google's ANGLE project is essentially doing what this pull request does for its GLES1 renderer. What they have is a giant block of code that flushes the state starting with features->textures->client state->matrices->shading,materials,&lighting->etc. The only additional thing is they have dirty state tracking like I proposed in #1681.
https://github.com/google/angle/blob/cff6f150eae166d0b3aefb11dba11947a6d12a3d/src/libANGLE/GLES1Renderer.cpp#L49

The D3D9 renderer seems to be a bit messier as it has long and complicated state blocks just for clear.
https://github.com/google/angle/blob/6dfdca836806b661cd0d0e090ef2cf1dc06a2e6a/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp#L1911

Also, it seems changing a single state in the D3D9 renderer for ANGLE works like ENIGMA master did prior to this pull request. We can see that changing the zwriteenable state is a function call specific to their D3D9 renderer.
https://github.com/google/angle/blob/c86c8b0cecf0656763564fc60c761979333f43df/src/libANGLE/renderer/d3d/d3d9/StateManager9.cpp#L740

Overall, it looks like ANGLE's D3D renderers are a mess while their OpenGL ones are abstract. So at least ENIGMA has all of its backends abstract rather than just some of them. It sounds in some ways the work being done on ANGLE since 2013 for multiplatform is mirrored a lot by ENIGMA too.
https://docs.google.com/document/d/17mxRfzXuEWyvGM3t2KqVY4svvfRj_GzysOEpmnDpqeo/edit#

Please sign in to post comments, or you can view this issue on GitHub.