Help with glGetActiveUniformBlock

Started by TranquilMarmot, January 19, 2012, 10:56:31

Previous topic - Next topic

TranquilMarmot

I'm trying to go about copying a bunch of floats into a uniform block in one of my shaders. The book I'm reading uses memcpy, so I'm having issues translating its code into Java.

In my fragment shader, I have
uniform BlobSettings {
  vec4 InnerColor;
  vec4 OuterColor;
  float RadiusInner;
  float RadiusOuter;
};


Then, to try and set that uniform block
public static void initUniformBlockBuffer(){
	// get the block's index
	int blockIndex = GL31.glGetUniformBlockIndex(program.getHandle(),
			"BlobSettings");
	
	// how many bytes is the block?
	int blockSize = GL31.glGetActiveUniformBlock(program.getHandle(),
			blockIndex, GL31.GL_UNIFORM_BLOCK_DATA_SIZE);
	// allocate a bytebuffer
	ByteBuffer blockBuffer = BufferUtils.createByteBuffer(blockSize);
	
	// this prints out 48, even though there's only 10 floats
	System.out.println(blockSize + " bytes");

	// query for offsets of each block variable
	String[] names = { "InnerColor", "OuterColor", "RadiusInner",
			"RadiusOuter" };

	// get uniform indices
	IntBuffer indices = BufferUtils.createIntBuffer(4);
	GL31.glGetUniformIndices(program.getHandle(), names, indices);

	// get uniform byte offsets
	IntBuffer offset = BufferUtils.createIntBuffer(4);
	GL31.glGetActiveUniforms(program.getHandle(), indices,
			GL31.GL_UNIFORM_OFFSET, offset);

	// data
	float[] outerColor = { 0.0f, 0.0f, 0.0f, 0.0f };
	float[] innerColor = { 1.0f, 1.0f, 0.75f, 1.0f };
	float innerRadius = 0.25f, outerRadius = 0.45f;

	// put outer color colors into the bytebuffer (add 4 * i because 4 bytes/float)
	for (int i = 0; i < innerColor.length; i++) {
		blockBuffer.putFloat(offset.get(0) + (4 * i), innerColor[i]);
	}

	// put inner colors into the bytebuffer
	for (int i = 0; i < outerColor.length; i++) {
		blockBuffer.putFloat(offset.get(1) + (4 * i), outerColor[i]);
	}

	// put inner and outer radius into bytebuffer
	blockBuffer.putFloat(offset.get(2), innerRadius);
	blockBuffer.putFloat(offset.get(3), outerRadius);

	// create buffer object and copy the data
	int uboHandle = GL15.glGenBuffers();
	GL15.glBindBuffer(GL31.GL_UNIFORM_BUFFER, uboHandle);
	GL15.glBufferData(GL31.GL_UNIFORM_BUFFER, blockBuffer,
			GL15.GL_DYNAMIC_DRAW);

	// bind buffer object to the uniform block
	GL30.glBindBufferBase(GL31.GL_UNIFORM_BUFFER, blockIndex, uboHandle);
}


My main point of confusion is that when GL31.glGetActiveUniformBlock(program.getHandle(), blockIndex, GL31.GL_UNIFORM_BLOCK_DATA_SIZE) is called, it returns 48 bytes.
I'm pretty sure that a float in Java is 4 bytes. I have 10 floats in my shader, that should equate to 40 bytes. Why is there room for 2 extra floats?

All of the offsets returned by GL31.glGetActiveUniforms(program.getHandle(), indices, GL31.GL_UNIFORM_OFFSET, offset); show the data being at the correct positions as if each float were 4 bytes- InnerColor is at 0, OuterColor is at 16, InnerRadius is at 32 and OuterRadius is at 36. I checked to make sure my data was copied into the ByteBuffer correctly by using its asFloatBuffer method and printing out the values. Everything was fine except for two blank floats (0.0f) at the end of it after OuterRadius. If I try to print out the active uniforms for my program, it prints out everything in the block at location -1, so it appears as though my data is binding at all, so my problem might be with GL30.glBindBufferBase(GL31.GL_UNIFORM_BUFFER, blockIndex, uboHandle);

Anybody have any advice? Is there a better way to go about this?

spasi

The UNIFORM_BLOCK_DATA_SIZE query returns 48 because your uniform block is being padded to a 16-byte boundary. This is related to the layout qualifier, which is "shared" in your case, the default:

Quote- "packed" uniform blocks have an implementation-dependent data
layout for efficiency, and unused uniforms may be eliminated by
the compiler to save space.
   
- "shared" uniform blocks, the default layout, have an implementation-
dependent data layout for efficiency, but the layout will be uniquely
determined by the structure of the block, allowing data storage to be
shared across programs.
   
- "std140" uniform blocks have a standard cross-platform cross-vendor
layout (see below). Unused uniforms will not be eliminated.

There's a lot of info in the spec if you want to learn more about this.

Quote from: TranquilMarmot on January 19, 2012, 10:56:31If I try to print out the active uniforms for my program, it prints out everything in the block at location -1

This sounds like you're not using "BlobSettings" at all and the compiler is optimizing it away. If that's the case, modify your shader to use it and try again. Otherwise, try the layout(std140) qualifier.

TranquilMarmot

I tried all the different layout qualifiers (shared, packed, std140) and no matter what I still get 48 bytes. When you say 16-byte boundary, are you saying that it's being organized in memory as three 16-byte chunks? So the two vec4s take up the first two 16-byte sections, then the two floats take up half of the last 16-byte section? Do I need to do anything about the last two empty values or do I just leave them at 0.0f?

The compiler is definitely optimizing the uniform block away, which doesn't make any sense to me because I use all the variables in my shader. Here's the whole shader:
#version 400

in vec3 TexCoord;
layout (location = 0) out vec4 FragColor;

layout ( std140 ) uniform BlobSettings {
  vec4 InnerColor;
  vec4 OuterColor;
  float RadiusInner;
  float RadiusOuter;
} Blob;

void main() {
    float dx = TexCoord.x - 0.5;
    float dy = TexCoord.y - 0.5;
    float dist = sqrt(dx * dx + dy * dy);
    FragColor =
       mix( Blob.InnerColor, Blob.OuterColor,
             smoothstep( Blob.RadiusInner, Blob.RadiusOuter, dist )
            );
}


As far as I can tell, I'm doing everything correctly. But I guess that's not the case.

spasi

The problem is here:

// query for offsets of each block variable
String[] names = { "InnerColor", "OuterColor", "RadiusInner", "RadiusOuter" };

// get uniform indices
IntBuffer indices = BufferUtils.createIntBuffer(4);
GL31.glGetUniformIndices(program.getHandle(), names, indices);


The indices returned are all -1. Since you used an instance name ("Blob") for the BlobSettings uniform block, you need to also specify that name at the API level. So if you change the string array above to:

String[] names = { "Blob.InnerColor", "Blob.OuterColor", "Blob.RadiusInner", "Blob.RadiusOuter" };


it should work fine.