Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using Uniform Buffer Objects with libgdx? #7083

Closed
soundasleep opened this issue Feb 5, 2023 · 2 comments
Closed

Using Uniform Buffer Objects with libgdx? #7083

soundasleep opened this issue Feb 5, 2023 · 2 comments

Comments

@soundasleep
Copy link
Contributor

soundasleep commented Feb 5, 2023

Hello! I'm trying to work out how to use Uniform Buffer Objects with libgdx/lwjgl, and I can't seem to get the data to bind correctly.

Following the UniformBufferObjectsTest.java in #7037 I've put together the following code that should create a new float[] buffer, populate it, and then render it:

int programId = compiled.getShader().getHandle();

// Get the block index for the uniform block
int uniformBlockIndex = Gdx.gl30.glGetUniformBlockIndex(programId, "VertexData");
if (uniformBlockIndex < 0) {
  throw new IllegalStateException(String.format("found no uniformBlockIndex: %s", uniformBlockIndex));
}

// creates a simple FloatBuffer
float[] data = new float[4];
data[0] = 1;
data[1] = 1;
data[2] = 1;
data[3] = 1;
FloatBuffer buf = BufferUtils.newFloatBuffer(data.length);
buf.put(data);
System.out.println("buf[0] = " + buf.get(0)); // yes, the FloatBuffer is being set to 1.0f

// Create a temporary buffer to create a new handle to store buffers (?)
IntBuffer tmpBuffer = BufferUtils.newIntBuffer(16);
Gdx.gl30.glGenBuffers(1, tmpBuffer);
int bufferHandle = tmpBuffer.get(0);

// create the actual UBO using this handle
Gdx.gl30.glBindBuffer(GL30.GL_UNIFORM_BUFFER, bufferHandle);
int size = buf.capacity() * 4 /* a float is 4 bytes */;
Gdx.gl30.glBufferData(GL30.GL_UNIFORM_BUFFER, size, buf, GL30.GL_STATIC_DRAW);
Gdx.gl30.glBindBuffer(GL30.GL_UNIFORM_BUFFER, 0);

// Use the index to bind to a binding point, then bind the buffer
int bindingPoint = 0;
Gdx.gl30.glBindBuffer(GL30.GL_UNIFORM_BUFFER, bufferHandle);
Gdx.gl30.glUniformBlockBinding(programId, uniformBlockIndex, bindingPoint);
Gdx.gl30.glBindBufferBase(GL30.GL_UNIFORM_BUFFER, bindingPoint, bufferHandle);
Gdx.gl30.glBindBuffer(GL30.GL_UNIFORM_BUFFER, 0);

// fill the buffer? possibly not necessary?
Gdx.gl30.glBindBuffer(GL30.GL_UNIFORM_BUFFER, bufferHandle);
Gdx.gl30.glBufferSubData(GL30.GL_UNIFORM_BUFFER, 0 /* offset */, size /* size */, buf);
Gdx.gl30.glBindBuffer(GL30.GL_UNIFORM_BUFFER, 0);

// update the buffer data store (should only be in the render call)
Gdx.gl30.glBindBuffer(GL30.GL_UNIFORM_BUFFER, bufferHandle);
Gdx.gl30.glBufferSubData(GL30.GL_UNIFORM_BUFFER, 0, buf.capacity() * 4, buf);
Gdx.gl30.glBindBuffer(GL30.GL_UNIFORM_BUFFER, 0);

// we're still able to bind other uniforms
compiled.getShader().setUniformf("anotherValue", 0.5f);

// and then draw as normal
batch.draw(texture, area.x, area.y, area.width, area.height);

The fragment shader code I'm using to debug is:

#version 150

uniform sampler2D u_texture;

in vec2 v_texCoords; // provided by vertex shader

layout (std140) uniform VertexData {
  vec2[4] v_polygon;
};
uniform float anotherValue;

out vec4 fragColor;

void main()
{
  // we ignore the texture but libgdx asserts there's always u_texture: https://stackoverflow.com/q/27967788/39531
  vec4 sampledButIgnored = texture(u_texture, v_texCoords);

  if (v_polygon[0].x == 0) {
    // v_polygon is not being populated with any data :(
    fragColor = vec4(anotherValue /* shows we're getting normal uniforms */, 1.0, 0.0, 1.0);
  } else {
    // we have data in v_polygon, we don't get here
    fragColor = vec4(v_polygon[0].x, v_polygon[0].y, v_polygon[1].x, 1.0);
  }

  fragColor += vec4(0, 0, 0, sampledButIgnored.a);
}

As far as I can tell I've got everything set up correctly but data just isn't getting loaded into the UBO/shader. Any ideas would be appreciated.

(I'd love to have first-class support for UBOs in a future version of libgdx!)

@Tom-Ski
Copy link
Member

Tom-Ski commented Feb 5, 2023

You should rewind the buffer after putting in the data

FloatBuffer buf = BufferUtils.newFloatBuffer(data.length);
buf.put(data);
buf.rewind();

You are also populating 4 floats, but in your shader you have 8 floats, (4 vec 2s)

@soundasleep
Copy link
Contributor Author

soundasleep commented Feb 6, 2023

buf.rewind() absolutely helped!! Thank you! 🥳

After that I had a flickering problem but that was probably because I was creating a brand new buffer on every single frame. If instead I created a single UBO then the flickering stops.

Also note that the float[] needs to have padding to align correctly, i.e.

float[] data = new float[8];
data[0] = 0.5f; // first vec2 is at offset=0
data[1] = 0.5f;
data[4] = 0.5f; // second vec2 is at offset=16
data[5] = 0.5f;

FloatBuffer buf = BufferUtils.newFloatBuffer(data.length);
buf.put(data);
buf.rewind(); // essential!
int size = buf.capacity() * 4; // a float is 4 bytes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants