Buffer Objects in jME

I have started to work on implementing SSBO for jME. I already have some “draft” implementation of this here:

But @nehon think that we need to discuss here how it should look like. I agree with him.
Let’s go :slight_smile:

1 Like

Yes. All my points in the PR are based on what I read of UBOs and SSBOs but I never used them in real life so if someone with more insight could chime in, his/her input may be very valuable.

I started to work on this feature because one my friend started to work on some graphic things which requires this feature :wink:

Yes Maybe we could describe what it is and the features that would allow too.

On the top of my head:
UBOs are basically uniform groups that can be shared between shaders. and only take one uniform slot. Though there is a limit on how much data you can put in them (specs says 16kb). UBO are core since opengl 3.1

SSBO are similar with 2 major differences, They can store a lot more data (spec says up to 128mb) and they can be written atomically in the shader. SSBO are core since opengl4.3

UBOs could be used for in pass shadows, there are a lot of uniforms to bind to each material for shadows, having only one UBO available for all materials would help a lot.

SSBO could be used for many things considering the amount of data they can hold,but i don’t have a specific use case in mind. Though the fact that they are only available in opengl 4.3 will probably limit their usage, at least in the engine.

Maybe you could describe this feature.

@nehon About:

UBO and SSBO can be shared across shaders, so maybe it has no sense to set it as a material param, because as soon as you set it on a material it’s available in all shader programs so in all materials.
Maybe it should be set on the Renderer directly and bond as a g_ global parameter.

but we should bind a SSBO to each our shader manually, so I have no ideas how to do it without using material parameters.

as far as I understand, it relates to voxel things. He wants to send big data to a geometry shader to draw a lot of voxels.

ssbo are used in tile based deferred rendering to hold the light data. in some order independent translucency techniques to hold the fragment data.
If you need to pass not uniform distributed data to, or from the gpu ssbo are the way to go.
All the above techs are using the ssbo mainly as buffer to build some sort of linked list in combination with a atomic integer texture.

Additionally it should be allowed to bind any buffer object as ssbo. A regular vertex buffer for examples. And voila, you have gpu particles.


mhh, I have to read more about it… IMO since they can be shared they are more like global uniforms, but indeed they need to be bound to the shader, so inherently to the material. Maybe leverage the MaterialParamOverride system to use them.

@nehon what do you think about this API?

       final UniformBufferObject ubo = new UniformBufferObject(3, Layout.std140,
           new BufferObjectField("light_1", VarType.Vector4),
           new BufferObjectField("light_2", VarType.Vector4),
           new BufferObjectField("array", VarType.FloatArray)
       ubo.setValue("light_1", ColorRGBA.Black);
       ubo.setValue("light_2", ColorRGBA.Gray);
       ubo.setValue("array", new float[] {1F, 2F, 3F});

       material.setBufferObject("uboTest", ubo);

@nehon also, about checking data limits, the limits are different for different kinds of shaders :frowning:

Looks good but:

  1. Do we really need the layout param? Maybe we coudl default to std140 for ubos and std430 for ssbo and let the user change it on the bufferObject if ever he wants to use the other layout types.
  2. Do we really need to declare all fields before hand?
    I’d see more something like
UniformBufferObject ubo = new UniformBufferObject(3);
ubo.setFiels("light_1",  ColorRGBA.Black); // sets or creates and sets a field,  VarType can be inferred depending on the value type.
  1. IMO we don’t need separate java objects for UBO and SSBO. specs says clearly that you can use a bufferObject as an UBO and as a SSBO. (though that would make the layout param more mandatory …)

Mhhh where did you see this information?

I implemented loading these limits:

if (hasExtension("GL_ARB_shader_storage_buffer_object")) {
            limits.put(Limits.ShaderStorageBufferObjectMaxBlockSize, getInteger(GL4.GL_MAX_SHADER_STORAGE_BLOCK_SIZE));
            limits.put(Limits.ShaderStorageBufferObjectMaxComputeBlocks, getInteger(GL4.GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS));
            limits.put(Limits.ShaderStorageBufferObjectMaxGeometryBlocks, getInteger(GL4.GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS));
            limits.put(Limits.ShaderStorageBufferObjectMaxFragmentBlocks, getInteger(GL4.GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS));
            limits.put(Limits.ShaderStorageBufferObjectMaxVertexBlocks, getInteger(GL4.GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS));
            limits.put(Limits.ShaderStorageBufferObjectMaxTessControlBlocks, getInteger(GL4.GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS));
            limits.put(Limits.ShaderStorageBufferObjectMaxTessEvaluationBlocks, getInteger(GL4.GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS));
            limits.put(Limits.ShaderStorageBufferObjectMaxCombineBlocks, getInteger(GL4.GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS));

        if (hasExtension("GL_ARB_uniform_buffer_object")) {
            limits.put(Limits.UniformBufferObjectMaxBlockSize, getInteger(GL3.GL_MAX_UNIFORM_BLOCK_SIZE));
            limits.put(Limits.UniformBufferObjectMaxGeometryBlocks, getInteger(GL3.GL_MAX_GEOMETRY_UNIFORM_BLOCKS));
            limits.put(Limits.UniformBufferObjectMaxFragmentBlocks, getInteger(GL3.GL_MAX_FRAGMENT_UNIFORM_BLOCKS));
            limits.put(Limits.UniformBufferObjectMaxVertexBlocks, getInteger(GL3.GL_MAX_VERTEX_UNIFORM_BLOCKS));

I can add default values for constructors, but I prefer to have full control of this, SSBO can use std140 and std430.

How are you going to guarantee correct result during converting java objects to byte buffer?

but I need to use different openGL functions to work with it :slight_smile:

@nehon some updates:

       final UniformBufferObject ubo = new UniformBufferObject(3,
           field("light_1", VarType.Vector4),
           field("light_2", VarType.Vector4),
           field("array", VarType.FloatArray)

       ubo.setValue("light_1", ColorRGBA.Black);
       ubo.setValue("light_2", ColorRGBA.Gray);
       ubo.setValue("array", new float[] {1F, 2F, 3F});

       material.setBufferObject("uboTest", ubo);

I’m not saying we don’t need the fields, I’m saying do we need to ask the user for explicit declaration?

ubo.setField("light_1",  ColorRGBA.Black);

Has all the information we need. We already do a mapping between java objects and VarTypes in the engine.

This doesn’t mean we need a different BufferObject.
Maybe we can do something like

material.setUniformBufferObject("paramName", bufferObject, Layout.std140);
material.setShaderStorageBufferObject("paramName", bufferObject, Layout.std430);

hm, how do we can do that :open_mouth: ?

I think a layout should be a part of BO, because in your case, we can apply the same BO to different materials with different layouts.

I have no ideas else how we can decide BO’s type :frowning: