Skip to content

Optimization of the 2D batching renderer for OpenGL implementations without orphaning #5348

@kisg

Description

@kisg

Describe the project you are working on

Various AR / VR projects using Godot Engine in the automotive industry and on Oculus Quest 2.

Describe the problem or limitation you are having in your project

TL-DR: The current 2D renderer is suboptimal if the OpenGL driver does not implement buffer orphaning. We have identified that the OpenGL driver on the Oculus Quest 2 has this limitation.

Note: We used the Perfetto profiling integration to find and identify this issue, that we proposed here:
#5346

Unlike the 3D renderer of Godot Engine, the 2D batching renderer currently uses only a single vertex and index buffer for uploading vertex data to the GPU which is related to all batches. This presents the problem of buffer object streaming, the frequent modification of a buffer object which might also be used concurrently by draw calls. This can result in implicit synchronization, as the call to the buffer upload command (e.g. glBufferData) needs to wait for all previously queued commands to finish that depend on the previous contents of the buffer that is currently being modified. Such an overhead can be explicitly identified by measuring an increased call time for glBufferData() on the CPU, even without explicit synchronization by glFinish calls.

A common solution to the above mentioned problem - which the 2D batching renderer of Godot Engine currently uses - is buffer orphaning, a buffer object streaming strategy where the previously allocated buffer storage is "detached" from the buffer handle with a NULL-parameterized glBufferData call, which allows the OpenGL implementation to skip implicit synchronization past this detachment point, as it can allocate a new buffer storage for the new contents of the buffer, keeping the previous one for as long as the commands dependent on it did not finish.

The issue with the buffer orphaning approach is that it is heavily implementation-dependent. The OpenGL specification does not require the implementor to write glBufferData in such a way that NULL-parametrization will always result in buffer orphaning, it is only a commonly implemented optimization strategy.

Our team has identified that the OpenGL implementation used by Meta's Oculus Quest 2 does not implement orphaning optimizations in either glBufferData or glMapBufferRange. This results in the near-sequential execution of draw calls in the 2D batching renderer, as implicit synchronization is required between all consecutive buffer updates/draws. In our performance analysis, this resulted in several milliseconds of rendering even fairly simple 2D scenes requiring relatively few batches.

On a VR platform where high framerates are already critical for acceptable user experience, such a severe 2D rendering overhead cannot be accepted.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Our team proposes an alternative adaptive solution to the problem of buffer object streaming in the 2D batching renderer, based on explicit initialization of multiple buffers. Our performance analysis indicates that in some 2D scenarios this results in a 2-3.5x improvement in rendering speed compared to the current implementation on the Oculus Quest 2 platform, while also trying to minimize the increase in GPU memory usage. This option can easily be disabled from the project settings, resulting in the previous single-buffer rendering with orphaning.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

A PR was submitted for initial review: godotengine/godot#65385

The PR was developed by @konczg at Migeran.

The work was sponsored by Voxels.

If this enhancement will not be used often, can it be worked around with a few lines of script?

No

Is there a reason why this should be core and not an add-on in the asset library?

This is a core feature.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions