Render State Encapsulation and Dirty Tracking

Reporter: RobertBColton  |  Status: open  |  Last Modified: May 06, 2019, 08:50:38 PM

Now that #1636 is in, I want to document some of the next steps that should occur after GLES is added. The biggest is that we should encapsulate all of the render state that's in the general graphics sources now. We should take all of that state and put it in a RenderState class that can be serialized and deserialized. This would enable hot reloading as well as saving of games in the middle of a frame. It will also allow us to easily add the new GMS2 functions.
https://docs2.yoyogames.com/source/_build/3_scripting/4_gml_reference/drawing/gpu/gpu_push_state.html
https://docs2.yoyogames.com/source/_build/3_scripting/4_gml_reference/drawing/gpu/index.html

After encapsulating the render state it would be a good idea to investigate more comprehensive dirty state tracking. Right now, we use only a single dirty flag variable that encompasses all of the render states together. The idea to test is adding an unordered_set<void*> dirty_states to the proposed RenderState class which tracks which render states are dirty by storing the render state variable's address in the dirty states hash set.

Here's how dirty state tracking would then be done in the backends:

if (dirty_states.find(&d3dLighting) != dirty_states.end())
  (d3dLighting?glEnable:glDisable)(GL_LIGHTING);

This may result in a performance boost since not only would #1636 mean we have eliminated redundant state changes, we'd go back to only changing states that actually changed. This also has the potential to clean up the API trace making it easier to debug the graphics.

RobertBColton  
Amending this with something I thought I had realized. So I thought in GMSv1.4 some of the 3D state functions don't do anything when you are in normal/ortho mode. That suggested they must internally be checking if (d3dMode) before flushing those relevant states, we could also do the same.

I created an example GMK as well to demonstrate this, but turned out to prove myself wrong. In current ENIGMA master where we don't do the above and GMSv1.4, the sprite is drawn black.

Download: lighting-d3d-mode-test.zip

Even though I proved myself wrong, this is a change in the behavior from old GM versions. In the past none of the 2D could be mixed with 3D, which has clearly been resolved.

RobertBColton  

So this lack of dirty state tracking means GMS is beating us yet in just one graphical area. If you make state changes of any kind, say you change a state and change it back without drawing anything, it will continuously "break" the batch. The little benchmark below gives me about 400 fps in GMSv1.4 and about 5 fps in ENIGMA master. Comment out the culling change and we get about 600 fps in ENIGMA.

// create event
room_speed = 99999;
set_synchronization(false);

// draw event
repeat (5000) {
    var xx, yy;
    xx = random(room_width);
    yy = random(room_height);
    d3d_set_culling(false); /// <- slow down/batch break
    draw_sprite(spr_0, 0, xx, yy);
}
room_caption = string(fps);

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