You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
112 lines
6.6 KiB
112 lines
6.6 KiB
# Dirty Bits and State Changes
|
|
|
|
OpenGL render loops typically involve changing some render states followed by
|
|
a draw call. For instance the app might change a few uniforms and invoke
|
|
`glDrawElements`:
|
|
|
|
```
|
|
for (const auto &obj : scene) {
|
|
for (const auto &uni : obj.uniforms) {
|
|
glUniform4fv(uni.loc, uni.data);
|
|
}
|
|
glDrawElements(GL_TRIANGLES, obj.eleCount, GL_UNSIGNED_SHORT, obj.eleOffset);
|
|
}
|
|
```
|
|
|
|
Another update loop may change Texture and Vertex Array state before the draw:
|
|
|
|
```
|
|
for (const auto &obj : scene) {
|
|
glBindBuffer(GL_ARRAY_BUFFER, obj.arrayBuffer);
|
|
glBufferSubData(GL_ARRAY_BUFFER, obj.bufferOffset, obj.bufferSize, obj.bufferData);
|
|
glVertexAttribPointer(obj.arrayIndex, obj.arraySize, GL_FLOAT, GL_FALSE, 0, nullptr);
|
|
glBindTexture(GL_TEXTURE_2D, obj.texture);
|
|
glDrawElements(GL_TRIANGLES, obj.eleCount, GL_UNSIGNED_SHORT, obj.eleOffset);
|
|
}
|
|
```
|
|
|
|
Other update loops may change render states like the blending modes, the depth test, or Framebuffer
|
|
attachments. In each case ANGLE needs to validate, track, and translate these state changes to the
|
|
back-end as efficiently as possible.
|
|
|
|
## Dirty Bits
|
|
|
|
Each OpenGL Context state value is stored in [`gl::State`](../src/libANGLE/State.h). For instance
|
|
the blending state, depth/stencil state, and current object bindings. Our problem is deciding how to
|
|
notify the back-end when app changes front-end state. We decided to bundle changed state into
|
|
bitsets. Each 1 bit indicates a specific changed state value. We call these bitsets "*dirty bits*".
|
|
See [`gl::State::DirtyBitType`][DirtyBitType].
|
|
|
|
Each back-end handles state changes in a `syncState` implementation function that takes a dirty
|
|
bitset. See examples in the [GL back-end][GLSyncState], [D3D11 back-end][D3D11SyncState] and
|
|
[Vulkan back-end][VulkanSyncState].
|
|
|
|
Container objects such as Vertex Array Objects and Framebuffers also have their own OpenGL front-end
|
|
state. [VAOs][VAOState] store vertex arrays and array buffer bindings. [Framebuffers][FBOState]
|
|
store attachment state and the active read and draw buffers. These containers also have internal
|
|
dirty bits and `syncState` methods. See [`gl::Framebuffer::DirtyBitType`][FBODirtyBits] and
|
|
[`rx::FramebufferVk::syncState`][FBOVkSyncState] for example.
|
|
|
|
Dirty bits allow us to efficiently process groups of state updates. We use fast instrinsic functions
|
|
to scan the bitsets for 1 bits. See [`bitset_utils.h`](../src/common/bitset_utils.h) for more
|
|
information.
|
|
|
|
## Cached Validation and State Change Notifications
|
|
|
|
To optimize validation we cache many checks. See [`gl::StateCache`][StateCache] for examples. We
|
|
need to refresh cached values on state changes. For instance, enabling a generic vertex array
|
|
changes a cached mask of active vertex arrays. Changes to a texture's images could change a cached
|
|
framebuffer's completeness when the texture is bound as an attachment. And if the draw framebuffer
|
|
becomes incomplete it changes a cached draw call validation check.
|
|
|
|
See a below example of a call to `glTexImage2D` that can affect draw call validation:
|
|
|
|
<!-- Generated from https://bramp.github.io/js-sequence-diagrams/
|
|
participant App
|
|
participant Context
|
|
participant Framebuffer
|
|
participant Texture
|
|
App->Context: glTexImage2D
|
|
Context->Texture: setImage
|
|
Texture- ->Framebuffer: onSubjectStateChange
|
|
Note over Framebuffer: cache update
|
|
Framebuffer- ->Context: onSubjectStateChange
|
|
Note over Context: cache update
|
|
-->
|
|
|
|

|
|
|
|
We use the [Observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) to implement cache
|
|
invalidation notifications. See [`Observer.h`](../src/libANGLE/Observer.h). In the example the
|
|
`Framebuffer` observes `Texture` attachments via [`angle::ObserverBinding`][ObserverBinding].
|
|
`Framebuffer` implements [`angle::ObserverInterface::onSubjectStateChange`][FBOStateChange] to
|
|
receive a notification to update its completeness cache. The `STORAGE_CHANGED` message triggers a
|
|
call to [`gl::Context::onSubjectStateChange`][ContextStateChange] which in turn calls
|
|
[`gl::StateCache::updateBasicDrawStatesError`][StateCacheUpdate] to re-validate the draw
|
|
framebuffer's completeness. On subsequent draw calls we skip re-validation at minimal cost.
|
|
|
|
See the below diagram for the dependency relations between Subjects and Observers.
|
|
|
|

|
|
|
|
## Back-end specific Optimizations
|
|
|
|
See [Fast OpenGL State Transitions][FastStateTransitions] in [Vulkan documents][VulkanREADME] for
|
|
additional information for how we implement state change optimization on the Vulkan back-end.
|
|
|
|
[DirtyBitType]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/State.h#483
|
|
[GLSyncState]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/renderer/gl/StateManagerGL.cpp#1576
|
|
[D3D11SyncState]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp#852
|
|
[VulkanSyncState]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/renderer/vulkan/ContextVk.cpp#642
|
|
[VAOState]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/VertexArray.h#35
|
|
[FBOState]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/Framebuffer.h#52
|
|
[FBODirtyBits]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/Framebuffer.h#319
|
|
[FBOVkSyncState]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/renderer/vulkan/FramebufferVk.cpp#726
|
|
[StateCache]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/Context.h#98
|
|
[ObserverBinding]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/Observer.h#103
|
|
[FBOStateChange]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/Framebuffer.cpp#1811
|
|
[ContextStateChange]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/Context.cpp#7981
|
|
[StateCacheUpdate]: https://chromium.googlesource.com/angle/angle/+/5f662c0042703344eb0eef6d1c123e902e3aefbf/src/libANGLE/Context.cpp#8190
|
|
[FastStateTransitions]: ../src/libANGLE/renderer/vulkan/doc/FastOpenGLStateTransitions.md
|
|
[VulkanREADME]: ../src/libANGLE/renderer/vulkan/README.md
|