jmonkeyengine
jmonkeyengine copied to clipboard
UBO/SSBO Improvements, Structs, BufferObject and sub data update
This PR is made by multiple parts that together are used to reimplement UBOs and SSBOs in a more convenient and performant way. The main concepts are explained below
BufferObject
A BufferObject is a generic memory buffer that exists in OpenGL and from which all the other specialized buffers are derived. In JME we currently have some specialized Buffers (eg. VertexBuffer) but not a generic one. This is needed to implement UBOs and SSBOs and can be used as parent class for VertexBuffer in a future refactoring, unlocking some performance improvements (see below)
BufferObject layout and sub data update
This PR introduces the concept of buffer layout, this is an abstraction and doesn't exist in OpenGL, nevertheless it can be used in jme to split the buffer in logical regions that can be marked for update and efficiently pushed to the GPU by the renderer while leaving the rest of the buffer untouched.
StructStd140BufferObject
This is an abstraction on top of BufferObject and BufferObject layouts, it generates an updates a BufferObject from a specially defined object that implements the Struct interface. This makes the creation and update of buffer objects user friendly and matches perfectly the glsl side of the code when using UBO/SSBO. The goal when implementing this was to make it the default way to maintain BufferObjects (even inside the core), for this reason the implementation forces some limitations that are there to avoid any meaningful performance burden that might be caused by a more flexible implementation. The limitations are:
- Only fields that are of one of the types defined in
com.jme3.util.struct.fields.*
are serialized: this permits to track changes to the fields and update only what changes - Every serializable field must be final : this allows the code to trust that the class layout will never change
These limitations can be avoided by creating and maintaining the BufferObject manually with StructUtils, but shouldn't be needed in common usecases.
Usage example
This is an example and unit test for the UBO implementation
- TestUBO.java: https://github.com/riccardobl/jmonkeyengine/blob/ef7982de9217dab3f95bc6fdcbbe0f846b896f77/jme3-examples/src/main/java/jme3test/material/TestUBO.java
- TestUBO.frag: https://github.com/riccardobl/jmonkeyengine/blob/ef7982de9217dab3f95bc6fdcbbe0f846b896f77/jme3-examples/src/main/resources/jme3test/ubo/TestUBO.frag
Final thoughts
This PR solves all the issues in he current UBO/SSBO implementation and, thanks to the sub data update, should always result in a performance improvement over sparse uniforms.
In future the bufferobject regions could be used to split large vertex buffers and allow a more efficient update (eg. for instances position).
There is an important change from the previous proposal (https://github.com/jMonkeyEngine/jmonkeyengine/pull/1317) in the use of hardcoded com.jme3.util.struct.fields.*
types, instead of a generic type StructField object, this is used to avoid ambiguity when using StructFields containing immutable types.
In the previous proposal, two methods were used:
- setValue() : to update immutable fields
- getValueForUpdate() : to update mutable fields
This is now handled internally by the com.jme3.util.struct.fields.*
types.
I am not 100% confident in the correctness of the std140 implementation. I will start using this PR on some real code and see if any issue arises
What is the state of this? I see there are conflicts. Can this become a candidate for the next release? Is anybody willing to review and possibly merge it?
PR updated to the current master.
This PR adds 37 Java source files. Please add the JME license (with appropriate year(s)) at the top of each file.
This PR is now rebased to the current master and with updated licenses