Shader, Uniform variables and buffer sizes

Started by Qudus, March 26, 2007, 18:20:09

Previous topic - Next topic

Qudus

hi

I was recently trying to improve shader performance. Therefore I wanted to reduce GC overhead, which I achieved, but I'm not very happy with my current solution.

I want to avoid to recreated the Float/IntBuffers needed for the glUniform1ARB() call. So I stored a global instance, which I only recreate, if the limit is lower than needed. But the glUniform1ARB() method didn't accept it. It demands a buffer of the exactly correct size. So I had to store one buffer for each variable. Is this really necessary? Could this be done with only one buffer?

Marvin

Matzon

You can slice the buffer into several ? - not sure how that would work out tho...

Qudus

Quote from: Matzon on March 26, 2007, 19:56:20
You can slice the buffer into several ? - not sure how that would work out tho...

What do you mean by "slice the buffer into several"?

Marvin


Qudus

Ah, ok. Thanks.

This is not bad, but there's still a GC overhead, if used. Is this an LWJGL implementation/API flaw? Or is there a way to handle this method GC fiendly an easy way? Maybe it is a good idea to improve the implementation for the next version to accept bigger buffers than needed. What do you think?

Marvin.

Matzon

I'm not sure that it is entirely possible since we do pass buffer.remaining() in some cases. We will investigate it though.

elias

I don't get it - glUniform1ARB doesn't check the buffer size. This is the (generated) code for glUniform1ARB:

    public static void glUniform1ARB(int location, FloatBuffer values) {
        ContextCapabilities caps = GLContext.getCapabilities();
        long function_pointer = caps.ARB_shader_objects_glUniform1fvARB_pointer;
        BufferChecks.checkFunctionAddress(function_pointer); 
        BufferChecks.checkDirect(values);
        nglUniform1fvARB(location, (values.remaining()), values, values.position(), function_pointer);
    }
    private static native void nglUniform1fvARB(int location, int count, FloatBuffer values, int values_position, long function_pointer);


as you can see, it uses values.position() for the start index and values.remaining() for the number of elements. You should be able to avoid garbage if you set the position and limit on your single buffer appropriately.

- elias

Qudus

Quote from: elias on March 27, 2007, 08:58:11
as you can see, it uses values.position() for the start index and values.remaining() for the number of elements. You should be able to avoid garbage if you set the position and limit on your single buffer appropriately.

I already tried to set the limit (and of course the position) by calling limit( int ) and revert() without success. But yould you advise me to use glUniform1ARB() instead of glUniveromARB()?

Marvin

elias

I don't know, you mentioned glUniform1ARB and I assumed that's the function you need. What exactly goes wrong if you set the limit and position correctly?

- elias

Qudus

Quote from: elias on March 27, 2007, 10:51:24
I don't know, you mentioned glUniform1ARB and I assumed that's the function you need. What exactly goes wrong if you set the limit and position correctly?

This is the stack trace, if I simply use a FloatBuffer of 128 size:
org.lwjgl.opengl.OpenGLException: Invalid operation (1282)
	at org.lwjgl.opengl.Util.checkGLError(Util.java:56)
	at org.lwjgl.opengl.Display.swapBuffers(Display.java:555)
	at org.lwjgl.opengl.Display.update(Display.java:571)
	at org.xith3d.render.lwjgl.CanvasPeerImplNative.renderDone(CanvasPeerImplNative.java:278)
	at org.xith3d.render.lwjgl.CanvasPeerImplBase.display(CanvasPeerImplBase.java:1072)
	at org.xith3d.render.lwjgl.CanvasPeerImplNative.doRender(CanvasPeerImplNative.java:298)
	at org.xith3d.render.lwjgl.CanvasPeerImplBase.render(CanvasPeerImplBase.java:1116)
	at org.xith3d.render.DefaultRenderer.renderOnceInternal(DefaultRenderer.java:459)
	at org.xith3d.render.DefaultRenderer.renderOnce(DefaultRenderer.java:480)
	at org.xith3d.render.base.Xith3DEnvironment.render(Xith3DEnvironment.java:436)
	at org.xith3d.render.loop.RenderLoop.renderNextFrame(RenderLoop.java:559)
	at org.xith3d.render.loop.RenderLoop.loopIteration(RenderLoop.java:582)
	at org.xith3d.render.loop.RenderLoop.update(RenderLoop.java:635)
	at org.xith3d.render.loop.UpdatingThread.nextIteration(UpdatingThread.java:175)
	at org.xith3d.render.loop.RenderLoop.loop(RenderLoop.java:691)
	at org.xith3d.render.loop.UpdatingThread.run(UpdatingThread.java:223)
	at org.xith3d.render.loop.RenderLoop.run(RenderLoop.java:712)
	at java.lang.Thread.run(Thread.java:619)
Exception in thread "Thread-4" java.lang.Error: org.lwjgl.opengl.OpenGLException: Invalid operation (1282)
	at org.xith3d.render.lwjgl.CanvasPeerImplBase.display(CanvasPeerImplBase.java:1093)
	at org.xith3d.render.lwjgl.CanvasPeerImplNative.doRender(CanvasPeerImplNative.java:298)
	at org.xith3d.render.lwjgl.CanvasPeerImplBase.render(CanvasPeerImplBase.java:1116)
	at org.xith3d.render.DefaultRenderer.renderOnceInternal(DefaultRenderer.java:459)
	at org.xith3d.render.DefaultRenderer.renderOnce(DefaultRenderer.java:480)
	at org.xith3d.render.base.Xith3DEnvironment.render(Xith3DEnvironment.java:436)
	at org.xith3d.render.loop.RenderLoop.renderNextFrame(RenderLoop.java:559)
	at org.xith3d.render.loop.RenderLoop.loopIteration(RenderLoop.java:582)
	at org.xith3d.render.loop.RenderLoop.update(RenderLoop.java:635)
	at org.xith3d.render.loop.UpdatingThread.nextIteration(UpdatingThread.java:175)
	at org.xith3d.render.loop.RenderLoop.loop(RenderLoop.java:691)
	at org.xith3d.render.loop.UpdatingThread.run(UpdatingThread.java:223)
	at org.xith3d.render.loop.RenderLoop.run(RenderLoop.java:712)
	at java.lang.Thread.run(Thread.java:619)
Caused by: org.lwjgl.opengl.OpenGLException: Invalid operation (1282)
	at org.lwjgl.opengl.Util.checkGLError(Util.java:56)
	at org.lwjgl.opengl.Display.swapBuffers(Display.java:555)
	at org.lwjgl.opengl.Display.update(Display.java:571)
	at org.xith3d.render.lwjgl.CanvasPeerImplNative.renderDone(CanvasPeerImplNative.java:278)
	at org.xith3d.render.lwjgl.CanvasPeerImplBase.display(CanvasPeerImplBase.java:1072)
	... 13 more


I get the same stack trace for a FloatBuffer of 128 size and position at 0 and limit at value.length (for a float array). I'm using glUniform1ARB( int, FloatBuffer ).

Marvin

elias

What are you passing in as the int parameter in glUniform1ARB? Further, does it work if you pass a buffer with one remaining() element? Try to add a org.lwjgl.opengl.Util.checkGLError() just before and after the glUniform1ARB call, just to make sure the 1281 error comes from that.

- elias

Qudus

Quote from: elias on March 28, 2007, 04:49:42
What are you passing in as the int parameter in glUniform1ARB?

It's the result of glGetUniformLocationARB.

Here is the relevant code: http://xith3d.svn.sourceforge.net/viewvc/xith3d/trunk/src/org/xith3d/render/lwjgl/GLSLShaderProgramShaderPeer.java?view=markup
(lines #310 - #508)

In this version I use the workarund with the stored buffers per uniform.

Quote from: elias on March 28, 2007, 04:49:42
Further, does it work if you pass a buffer with one remaining() element? Try to add a org.lwjgl.opengl.Util.checkGLError() just before and after the glUniform1ARB call, just to make sure the 1281 error comes from that.

It does work with a correctly sized buffer.

btw. Do I understand the glUniform1ARB, glUniform2ARB, ... thing right? Does the (1) only take one uniform parameter and the (2) exactly two and so forth?

Marvin

elias

Yes, glUniform1ARB takes one float if the target is a float uniform, but can take more than one variable if the target is an array of floats. Take a look at http://www.opengl.org/sdk/docs/man for a better explanation. Make sure you're setting the position and limit correctly and that the target is an array of the float array type if passing in more than one value.

- elias

Qudus

The position and limit HAVE the correct values and I'm storing the falues of a float array to the buffer as you can see here:
if (value instanceof float[])
{
    float[] v = (float[])value;
    
    setupTempFloatBuffer( v.length );
    
    tmpFloatBuffer.put( v );
    tmpFloatBuffer.rewind();
    ARBShaderObjects.glUniform1ARB( location, tmpFloatBuffer );
}


And this is the code, that sets up the temp FloatBuffer:
private void setupTempFloatBuffer(int minCap)
{
    if (tmpFloatBuffer.capacity() < minCap)
    {
        tmpFloatBuffer = BufferUtils.createFloatBuffer( (int)(minCap * 1.5) );
    }
    
    if (tmpFloatBuffer.limit() != minCap)
    {
        tmpFloatBuffer.limit( minCap );
    }
    
    tmpFloatBuffer.clear();
}


Is there anything incorrect?

The JavaDoc of FloatBuffer.limit( int ) says, that it only sets the limit to the given value, if it is larger than the current limit. Is there a way to set the limit in the case it is lower? My buffer has an initial capacity/limit of 128.

Marvin

Qudus

ok. I found a workaround:
private void setupTempFloatBuffer(int minCap)
{
    if (tmpFloatBuffer.capacity() < minCap)
    {
        tmpFloatBuffer = BufferUtils.createFloatBuffer( (int)(minCap * 1.5) );
    }
    
    tmpFloatBuffer.position( tmpFloatBuffer.limit() - minCap );
    tmpFloatBuffer.mark();
}


And I call it with:
if (value instanceof float[])
{
    float[] v = (float[])value;
    
    setupTempFloatBuffer( v.length );
    
    tmpFloatBuffer.put( v );
    tmpFloatBuffer.reset();
    ARBShaderObjects.glUniform1ARB( location, tmpFloatBuffer );
}


But it doesn't work for glGetUniformLocationARB. I use it this way:
private void setupTempByteBuffer(int minCap)
{
    if (tmpByteBuffer.capacity() < minCap)
    {
        tmpByteBuffer = BufferUtils.createByteBuffer( (int)(minCap * 1.5) );
    }
    
    tmpByteBuffer.position( tmpByteBuffer.limit() - minCap );
    tmpByteBuffer.mark();
}


called:
final String key = uniformVarNames.get( k );

// create a location for this key
setupTempByteBuffer( key.length() + 1 );
tmpByteBuffer.put( key.getBytes() );
tmpByteBuffer.reset();
int location = ARBShaderObjects.glGetUniformLocationARB( shaderProgram.getGlHandle(), tmpByteBuffer );


I get this stack trace when I use it like this:
java.lang.IllegalArgumentException: Missing null termination
	at org.lwjgl.BufferChecks.checkNullTerminated(BufferChecks.java:76)
	at org.lwjgl.opengl.ARBShaderObjects.glGetUniformLocationARB(ARBShaderObjects.java:373)
	at org.xith3d.render.lwjgl.GLSLShaderProgramShaderPeer.applyUniformVariables(GLSLShaderProgramShaderPeer.java:283)
	at org.xith3d.render.lwjgl.GLSLShaderProgramShaderPeer.shade(GLSLShaderProgramShaderPeer.java:513)
	at org.xith3d.render.CanvasPeer.setState(CanvasPeer.java:629)
	at org.xith3d.render.CanvasPeer.renderAtom(CanvasPeer.java:650)
	at org.xith3d.render.lwjgl.CanvasPeerImplBase.drawBin(CanvasPeerImplBase.java:451)
	at org.xith3d.render.lwjgl.CanvasPeerImplBase.renderMain(CanvasPeerImplBase.java:862)
	at org.xith3d.render.lwjgl.CanvasPeerImplBase.display(CanvasPeerImplBase.java:1049)
	at org.xith3d.render.lwjgl.CanvasPeerImplNative.doRender(CanvasPeerImplNative.java:298)
	at org.xith3d.render.lwjgl.CanvasPeerImplBase.render(CanvasPeerImplBase.java:1116)
	at org.xith3d.render.DefaultRenderer.renderOnceInternal(DefaultRenderer.java:459)
	at org.xith3d.render.DefaultRenderer.renderOnce(DefaultRenderer.java:480)
	at org.xith3d.base.Xith3DEnvironment.render(Xith3DEnvironment.java:427)
	at org.xith3d.loop.RenderLoop.renderNextFrame(RenderLoop.java:559)
	at org.xith3d.loop.RenderLoop.loopIteration(RenderLoop.java:582)
	at org.xith3d.loop.RenderLoop.update(RenderLoop.java:635)
	at org.xith3d.loop.UpdatingThread.nextIteration(UpdatingThread.java:175)
	at org.xith3d.loop.RenderLoop.loop(RenderLoop.java:691)
	at org.xith3d.loop.UpdatingThread.run(UpdatingThread.java:223)
	at org.xith3d.loop.RenderLoop.run(RenderLoop.java:712)
	at java.lang.Thread.run(Thread.java:619)


Any ideas?

Marvin