1# Dirty Bits and State Changes 2 3OpenGL render loops typically involve changing some render states followed by 4a draw call. For instance the app might change a few uniforms and invoke 5`glDrawElements`: 6 7``` 8for (const auto &obj : scene) { 9 for (const auto &uni : obj.uniforms) { 10 glUniform4fv(uni.loc, uni.data); 11 } 12 glDrawElements(GL_TRIANGLES, obj.eleCount, GL_UNSIGNED_SHORT, obj.eleOffset); 13} 14``` 15 16Another update loop may change Texture and Vertex Array state before the draw: 17 18``` 19for (const auto &obj : scene) { 20 glBindBuffer(GL_ARRAY_BUFFER, obj.arrayBuffer); 21 glBufferSubData(GL_ARRAY_BUFFER, obj.bufferOffset, obj.bufferSize, obj.bufferData); 22 glVertexAttribPointer(obj.arrayIndex, obj.arraySize, GL_FLOAT, GL_FALSE, 0, nullptr); 23 glBindTexture(GL_TEXTURE_2D, obj.texture); 24 glDrawElements(GL_TRIANGLES, obj.eleCount, GL_UNSIGNED_SHORT, obj.eleOffset); 25} 26``` 27 28Other update loops may change render states like the blending modes, the depth test, or Framebuffer 29attachments. In each case ANGLE needs to validate, track, and translate these state changes to the 30back-end as efficiently as possible. 31 32## Dirty Bits 33 34Each OpenGL Context state value is stored in [`gl::State`](../src/libANGLE/State.h). For instance 35the blending state, depth/stencil state, and current object bindings. Our problem is deciding how to 36notify the back-end when app changes front-end state. We decided to bundle changed state into 37bitsets. Each 1 bit indicates a specific changed state value. We call these bitsets "*dirty bits*". 38See [`gl::State::DirtyBitType`][DirtyBitType]. 39 40Each back-end handles state changes in a `syncState` implementation function that takes a dirty 41bitset. See examples in the [GL back-end][GLSyncState], [D3D11 back-end][D3D11SyncState] and 42[Vulkan back-end][VulkanSyncState]. 43 44Container objects such as Vertex Array Objects and Framebuffers also have their own OpenGL front-end 45state. [VAOs][VAOState] store vertex arrays and array buffer bindings. [Framebuffers][FBOState] 46store attachment state and the active read and draw buffers. These containers also have internal 47dirty bits and `syncState` methods. See [`gl::Framebuffer::DirtyBitType`][FBODirtyBits] and 48[`rx::FramebufferVk::syncState`][FBOVkSyncState] for example. 49 50Dirty bits allow us to efficiently process groups of state updates. We use fast instrinsic functions 51to scan the bitsets for 1 bits. See [`bitset_utils.h`](../src/common/bitset_utils.h) for more 52information. 53 54## Cached Validation and State Change Notifications 55 56To optimize validation we cache many checks. See [`gl::StateCache`][StateCache] for examples. We 57need to refresh cached values on state changes. For instance, enabling a generic vertex array 58changes a cached mask of active vertex arrays. Changes to a texture's images could change a cached 59framebuffer's completeness when the texture is bound as an attachment. And if the draw framebuffer 60becomes incomplete it changes a cached draw call validation check. 61 62See a below example of a call to `glTexImage2D` that can affect draw call validation: 63 64<!-- Generated from https://bramp.github.io/js-sequence-diagrams/ 65participant App 66participant Context 67participant Framebuffer 68participant Texture 69App->Context: glTexImage2D 70Context->Texture: setImage 71Texture- ->Framebuffer: onSubjectStateChange 72Note over Framebuffer: cache update 73Framebuffer- ->Context: onSubjectStateChange 74Note over Context: cache update 75--> 76 77 78 79We use the [Observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) to implement cache 80invalidation notifications. See [`Observer.h`](../src/libANGLE/Observer.h). In the example the 81`Framebuffer` observes `Texture` attachments via [`angle::ObserverBinding`][ObserverBinding]. 82`Framebuffer` implements [`angle::ObserverInterface::onSubjectStateChange`][FBOStateChange] to 83receive a notification to update its completeness cache. The `STORAGE_CHANGED` message triggers a 84call to [`gl::Context::onSubjectStateChange`][ContextStateChange] which in turn calls 85[`gl::StateCache::updateBasicDrawStatesError`][StateCacheUpdate] to re-validate the draw 86framebuffer's completeness. On subsequent draw calls we skip re-validation at minimal cost. 87 88See the below diagram for the dependency relations between Subjects and Observers. 89 90 91 92## Back-end specific Optimizations 93 94See [Fast OpenGL State Transitions][FastStateTransitions] in [Vulkan documents][VulkanREADME] for 95additional information for how we implement state change optimization on the Vulkan back-end. 96 97[DirtyBitType]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/State.h#483 98[GLSyncState]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/renderer/gl/StateManagerGL.cpp#1576 99[D3D11SyncState]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp#852 100[VulkanSyncState]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/renderer/vulkan/ContextVk.cpp#642 101[VAOState]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/VertexArray.h#35 102[FBOState]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/Framebuffer.h#52 103[FBODirtyBits]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/Framebuffer.h#319 104[FBOVkSyncState]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/renderer/vulkan/FramebufferVk.cpp#726 105[StateCache]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/Context.h#98 106[ObserverBinding]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/Observer.h#103 107[FBOStateChange]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/Framebuffer.cpp#1811 108[ContextStateChange]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/Context.cpp#7981 109[StateCacheUpdate]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/Context.cpp#8190 110[FastStateTransitions]: ../src/libANGLE/renderer/vulkan/doc/FastOpenGLStateTransitions.md 111[VulkanREADME]: ../src/libANGLE/renderer/vulkan/README.md 112