[CLOSED] v. 2.8.3 - GL15.glBufferSubData throwing IllegalArgumentException

Started by Jonas, January 26, 2012, 14:53:28

Previous topic - Next topic

Jonas

This problem does not occur with lwjgl 2.8.2, but does happen with lwjgl 2.8.3.  I do think this is a bug since the exception message talks about needing space in the Buffer for a 'faux' return value for a function( http://www.opengl.org/sdk/docs/man/xhtml/glBufferSubData.xml ) that does not, as far as I understand it, only accepts in-data and doesn't produce any kind of return value, 'faux' or otherwise.

The exception is very easy to reproduce, so here's..

The exception:
   Exception in thread "main" java.lang.IllegalArgumentException: Number of remaining buffer elements is 0, must be at least 1. Because at most 1 elements can be returned, a buffer with at least 1 elements is required, regardless of actual returned element count
	    	at org.lwjgl.BufferChecks.throwBufferSizeException(BufferChecks.java:162)
	    	at org.lwjgl.BufferChecks.checkBufferSize(BufferChecks.java:189)
	    	at org.lwjgl.BufferChecks.checkBuffer(BufferChecks.java:258)
	    	at org.lwjgl.opengl.GL15.glBufferSubData(GL15.java:192)
	    	at proceduralterraingenerator.test.LWJGL283Bug.main(LWJGL283Bug.java:33)


The code that produces it:
package proceduralterraingenerator.test;

import java.nio.FloatBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;

public class LWJGL283Bug {
	
	public static void main(String[] args) throws LWJGLException {
		System.out.println("Creating display.");
		Display.create();
		
		System.out.println("Creating VAO.");
		int vaoValue = GL30.glGenVertexArrays();
	    GL30.glBindVertexArray(vaoValue);
	    
	    System.out.println("Creating VBO.");
	    int vbo = GL15.glGenBuffers();
	    GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);

	    System.out.println("Allocating some space in the VBO.");
	    GL15.glBufferData(GL15.GL_ARRAY_BUFFER, 100, GL15.GL_STATIC_DRAW);
	    
	    System.out.println("Loading up 0 bytes of data into the VBO.");
	    FloatBuffer buffer = BufferUtils.createFloatBuffer(0);
	    GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, buffer);
	    
	    
	    //Resulting output:
	    /*
	    Creating display.
	    Creating VAO.
	    Creating VBO.
	    Allocating some space in the VBO.
	    Loading up 0 bytes of data into the VBO.
	    Exception in thread "main" java.lang.IllegalArgumentException: Number of remaining buffer elements is 0, must be at least 1. Because at most 1 elements can be returned, a buffer with at least 1 elements is required, regardless of actual returned element count
	    	at org.lwjgl.BufferChecks.throwBufferSizeException(BufferChecks.java:162)
	    	at org.lwjgl.BufferChecks.checkBufferSize(BufferChecks.java:189)
	    	at org.lwjgl.BufferChecks.checkBuffer(BufferChecks.java:258)
	    	at org.lwjgl.opengl.GL15.glBufferSubData(GL15.java:192)
	    	at proceduralterraingenerator.test.LWJGL283Bug.main(LWJGL283Bug.java:33)
	    	*/
	}
}

spasi

Indeed this is new behavior in 2.8.3, you can find more info here. What you're doing is basically a no-op, so I don't see a bug here. Why would you want to upload zero bytes to a VBO?

Jonas

Quote from: spasi on January 26, 2012, 17:41:48
Indeed this is new behavior in 2.8.3, you can find more info here. What you're doing is basically a no-op, so I don't see a bug here.
Crucially, it is a *legal* no-op as far as OpenGL is concerned, but LWJGL is making it an illegal no-op, which is what makes it a bug in my book.  My reasoning is that the closer to the 'main' OpenGL behavior that LWJGL sticks the better, and in this case there are extra limitations added in LWJGL for no good reason.

Quote from: spasi on January 26, 2012, 17:41:48
Why would you want to upload zero bytes to a VBO?
Or to put it another way, why would I want to be forced to code specific exclusions around every single point where I *might* use a 0-length buffer, even though being allowed to do so would be perfectly harmless and very nearly free, performance-wise?

Edit: Last but not least, let's not forget that this change is going to potentially break every existing application that relies on this function working according to the OpenGL spec.  In the long run it is also probably going to cause trouble since I assume subtle changes like this won't come with an effort to also document where LWJGL now behaves differently than the spec and in which way, beyond just the in/out-data differences that the javadoc already details.

spasi

I agree with all your points. It's why this buffer check didn't exist for 6 years.

But the fact remains that a) it's a corner case and b) we've had countless questions/reports by new LWJGL users that were the result of a missing .rewind() on a buffer passed to BO functions. Which is what this check is meant to catch. My argument is that reducing b) is much more important than allowing a).

Even if zero-length buffers are not as rare as I might think, throwing an exception could still be useful. It causes a developer to investigate why it's happening and it might expose a legitimate bug in their code.

You can also disable all buffer checks with this VM param: -Dorg.lwjgl.util.NoChecks=true

princec

I would prefer if this check had instead been enabled with an extra -Dlwjgl.extrachecks setting rather than just unexpectedly applied and breaking things. Because the exception is an unchecked exception this causes previously working code to simply fail unexpectedly, randomly, at runtime which did not fail before, without the compiler now informing us that this is even possible. This is very naughty and should be fixed as a matter of priority.

Cas :)

spasi

I removed the check from BufferSubData and GetBufferSubData, but kept it for BufferData. It's that ok with you guys?

Jonas

Quote from: spasi on January 28, 2012, 19:37:58
I removed the check from BufferSubData and GetBufferSubData, but kept it for BufferData. It's that ok with you guys?
I guess you mean 'removed the check from BufferSubData and BufferData, but kept it for GetBufferSubData', since GetBufferSubData is the only one of the three that uses the buffer to 'return' data.

That sounds great as far as I'm concerned, thanks!

spasi

No, I meant I kept it for BufferData. What difference does it make if the function reads or writes data from/to the buffer? A 0-length buffer is equally pointless with all 3. I can see how an existing algorithm could pass a 0-buffer to the SubData functions, but I don't think the same could be said about BufferData, without it being an actual bug in user code.

Jonas

You're right, it doesn't make sense to differentiate between a function that writes and one that reads.  In that case I have to stick with my initial opinion, a legal no-op shouldn't be turned into an illegal operation in LWJGL, that feels like a slippery slope towards some kind of parallel, vaguely documented spec beyond the original.

However arguing this with a developer feels a little awkward since I'm just an outsider looking in, while you have an inside view and more experience in the matter..so basically that's my opinion, but if you decide otherwise that's that, I can use the provided VM flags if need be.

princec

My opinion is that the LWJGL should precisely follow the OpenGL specifications of what's allowed and what's not.

Cas :)