[BUG] EXCEPTION_ACCESS_VIOLATION (Instanced Rendering-related?)

Started by oparisy, January 05, 2015, 21:25:27

Previous topic - Next topic

oparisy

While trying my hand at instanced rendering, I encountered the following crash (the full report is attached to this message):

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000000006926b691, pid=10056, tid=13644
#
# JRE version: 7.0-b147
# Java VM: Java HotSpot(TM) 64-Bit Server VM (21.0-b17 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# C  [atio6axx.dll+0x23b691]
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# E:\Workspace\hs_err_pid10056.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.sun.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#


This is systematically reproducible under LWJGL 2.9.1 and 2.9.2 using the following self-contained snipped (I removed most code, including errors checking, to keep it small):

package crashtest;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.opengl.GL31.*;
import static org.lwjgl.opengl.GL33.*;

import java.nio.FloatBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.Util;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;

public class CrashExample {

	private static final int SCREEN_WIDTH = 640;
	private static final int SCREEN_HEIGHT = 400;

	private static int NB_ENTITIES = 2;
	private static final int FLOAT_SIZE = 4;

	private static final String vertexShader =
			"#version 330 core\r\n" + 
			"in vec2 vertexPosition_modelspace;\r\n" + 
			"in mat4 modelMatrix;\r\n" + 
			"void main() {\r\n" + 
			"    gl_Position = modelMatrix * vec4(vertexPosition_modelspace, 0, 1);\r\n" + 
			"}\r\n";
	
	private static final String fragmentShader =
			"#version 330 core\r\n" + 
			"out vec3 color;\r\n" + 
			"void main() {\r\n" + 
			"	color = vec3(1,1,1);\r\n" + 
			"}";

	private static final float triangle_vertex[] = { 0, 1, 1, 0, 1, 1 };

	private int vertexBuffer;
	private int modelMatrixBuffer;

	private int posID;
	private int modelMatrixID;

	public static int makeShader(int type, String code) {
		int shader = glCreateShader(type);
		glShaderSource(shader, code);
		glCompileShader(shader);
		return shader;
	}

	public static FloatBuffer buildFloatBuffer(float[] data) {
		FloatBuffer dataBuffer = BufferUtils.createFloatBuffer(data.length);
		dataBuffer.put(data);
		dataBuffer.flip();
		return dataBuffer;
	}

	public static FloatBuffer buildFloatBuffer(Matrix4f mat) {
		FloatBuffer dataBuffer = BufferUtils.createFloatBuffer(4 * 4);
		mat.store(dataBuffer);
		dataBuffer.flip();
		return dataBuffer;
	}

	private void setup() throws LWJGLException, Exception, Error {
		Display.setDisplayMode(new DisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT));
		Display.create();

		// Set up shaders and program
		int vs = makeShader(GL_VERTEX_SHADER, vertexShader);
		int fs = makeShader(GL_FRAGMENT_SHADER, fragmentShader);
		int program = glCreateProgram();
		glAttachShader(program, vs);
		glAttachShader(program, fs);
		glLinkProgram(program);
		glUseProgram(program);

		// Get program attributes
		posID = glGetAttribLocation(program, "vertexPosition_modelspace");
		modelMatrixID = glGetAttribLocation(program, "modelMatrix");

		// Create a VAO
		glBindVertexArray(glGenVertexArrays());

		// Vertices for one triangle
		vertexBuffer = glGenBuffers();
		glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
		glBufferData(GL_ARRAY_BUFFER, buildFloatBuffer(triangle_vertex), GL_STATIC_DRAW);

		// One matrix per instance; dynamic data
		modelMatrixBuffer = glGenBuffers();
		glBindBuffer(GL_ARRAY_BUFFER, modelMatrixBuffer);
		glBufferData(GL_ARRAY_BUFFER, FLOAT_SIZE * 4 * 4 * NB_ENTITIES, GL_DYNAMIC_DRAW);

		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

		Util.checkGLError();
	}

	private void render() {

		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glEnableVertexAttribArray(posID);
		glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
		glVertexAttribPointer(posID, 2, GL_FLOAT, false, 0, 0);

		// Build model matrices
		FloatBuffer currentModelMatrices = BufferUtils.createFloatBuffer(4 * 4 * 2);
		Matrix4f modelMatrix1 = new Matrix4f().scale(new Vector3f(0.5f, 0.5f, 0));
		currentModelMatrices.put(buildFloatBuffer(modelMatrix1));
		Matrix4f modelMatrix2 = new Matrix4f().scale(new Vector3f(0.2f, 0.2f, 0));
		currentModelMatrices.put(buildFloatBuffer(modelMatrix2));
		currentModelMatrices.flip();

		// Upload model matrices
		glEnableVertexAttribArray(modelMatrixID);
		glBindBuffer(GL_ARRAY_BUFFER, modelMatrixBuffer);
		glBufferData(GL_ARRAY_BUFFER, currentModelMatrices, GL_DYNAMIC_DRAW);
		glVertexAttribPointer(modelMatrixID, 16, GL_FALSE, false, 0, 0);
		glVertexAttribDivisor(modelMatrixID, 1);

		// Instanced draw
		glDrawArraysInstanced(GL_LINE_LOOP, 0, 3, NB_ENTITIES);
	}

	private void start() throws Exception {
		setup();

		while (!Display.isCloseRequested() && !Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
			render();
			Display.update();
		}

		Display.destroy();
	}

	public static void main(String[] argv) throws Exception {
		new CrashExample().start();
	}
}


So, two questions:

  • Do you still accept bug reports filed under LWJGL 2.9, or should I port this to LWJGL 3 beforehand? :)
  • What am I doing wrong?

regards,
Olivier.

Kai

You have a typo in your code in line 128 in the listing it says *GL_FALSE* for the value type of the vertex attribute, where it should've been *GL_FLOAT* like in line 114 of the same listing.
Instanced rendering is really bitchy also on my machine. If only a single slightest error is in the code (like forgetting to glEnableVertexAttribArray), the whole thing crashes horribly. :)
But that's not LWJGL's fault. As they say (in German): The root of the problem sits in front of the monitor. :)

As a side note: You do not need to glEnableVertexAttribArray and glVertexAttribPointer your data with every frame. This state is captured inside of the VAO. So in your setup() method you could just once do that and in your render method only bind and unbind your VAO (which of course you need to remember the handle of then).

quew8

Quote from: Kai on January 05, 2015, 22:16:50
The root of the problem sits in front of the monitor.

That's a nice saying. I might steal that.

oparisy

Quote from: Kai on January 05, 2015, 22:16:50
You have a typo in your code in line 128 in the listing it says *GL_FALSE* for the value type of the vertex attribute, where it should've been *GL_FLOAT* like in line 114 of the same listing.

Thanks for taking the time to check my code. Now that you've hinted at it, there are actually two errors on line 128. I am specifying a "size" of 16, while it cannot be more than 4. Hence the crash.

So, contrarily to uniforms where I can happily bind a mat4, does this means that my only option to bind matrices to vertex attributes is to decompose them in 4 "vec4" (or an equivalent representation)?

Quote from: Kai on January 05, 2015, 22:16:50
Instanced rendering is really bitchy also on my machine. If only a single slightest error is in the code (like forgetting to glEnableVertexAttribArray), the whole thing crashes horribly. :)
But that's not LWJGL's fault. As they say (in German): The root of the problem sits in front of the monitor. :)

I can see your point, but I beg to differ. Up to now I've been shielded from such low-level crashes by LWJGL (and NIO buffers) checks. To avoid making this mistake in the future, I'll probably write a "glVertexAttribPointerChecked" which will throw an exception if I pass too large a size.

Quote from: Kai on January 05, 2015, 22:16:50
As a side note: You do not need to glEnableVertexAttribArray and glVertexAttribPointer your data with every frame. This state is captured inside of the VAO. So in your setup() method you could just once do that and in your render method only bind and unbind your VAO (which of course you need to remember the handle of then).

Interesting. So my "checked" version won't incur a significant overhead :)

Regards,
Olivier.

Kai

Quote... does this means that my only option to bind matrices to vertex attributes is to decompose them in 4 "vec4" ...
Yes, exactly. See https://www.opengl.org/discussion_boards/showthread.php/164099-how-to-specify-a-matrix-vertex-attribute. They say that too. Also, they say that a mat4 when being used as a vertex attribute would allocated 4 consecutive slots/indexes. So you would be needing to four times bind your buffer containing the matrices.
Thankfully though, you can use interleaved buffers with stride = 16*4 and 4-step-increasing offset respectively to not have 4 buffers for your 4 columns of each matrix.

QuoteI can see your point, but I beg to differ. Up to now I've been shielded from such low-level crashes by LWJGL (and NIO buffers) checks. To avoid making this mistake in the future, I'll probably write a "glVertexAttribPointerChecked" which will throw an exception if I pass too large a size.

Yeah, for vertex attributes you would be needing your own "shielding" code at both the vertex attribute specification commands AND the draw commands, since that is where the crash happens. So, the draw commands must also check that each bound vertex attribute buffer contains sufficient data!

EDIT:

So, the root cause of the problem is actually the draw command trying to source vertex attributes from an unspecified buffer, because I am certain that that glVertexAttribPointer call failed and raised a GL_INVALID_VALUE which you should check. Therefore, at that vertex attribute index there was no backing buffer and as such, the program crashed.

oparisy

Quote from: Kai on January 06, 2015, 21:04:14
Yes, exactly. See https://www.opengl.org/discussion_boards/showthread.php/164099-how-to-specify-a-matrix-vertex-attribute. They say that too. Also, they say that a mat4 when being used as a vertex attribute would allocated 4 consecutive slots/indexes. So you would be needing to four times bind your buffer containing the matrices.
Thankfully though, you can use interleaved buffers with stride = 16*4 and 4-step-increasing offset respectively to not have 4 buffers for your 4 columns of each matrix.
Indeed, now that I know what to search for, I've found similar confirmations.

So there's some syntactic sugar to simplify this binding, at least from the shader side. I think the java side code will get wrapped in an helper, though :)

QuoteYeah, for vertex attributes you would be needing your own "shielding" code at both the vertex attribute specification commands AND the draw commands, since that is where the crash happens. So, the draw commands must also check that each bound vertex attribute buffer contains sufficient data!
Now that feels like overkill, at least in the render loop :) But that could be a debugging option.

Quote
So, the root cause of the problem is actually the draw command trying to source vertex attributes from an unspecified buffer, because I am certain that that glVertexAttribPointer call failed and raised a GL_INVALID_VALUE which you should check. Therefore, at that vertex attribute index there was no backing buffer and as such, the program crashed.

Very interesting, I'll investigate this. I'm surprised this did not show up in my original code, which used a debugging context, though.

Kai

QuoteNow that feels like overkill, at least in the render loop :) But that could be a debugging option.

Agreed! That's probably the reason why the graphics driver is also not doing that, but instead just crashes the application. ;)

oparisy

Quote from: Kai on January 06, 2015, 21:04:14
So, the root cause of the problem is actually the draw command trying to source vertex attributes from an unspecified buffer, because I am certain that that glVertexAttribPointer call failed and raised a GL_INVALID_VALUE which you should check. Therefore, at that vertex attribute index there was no backing buffer and as such, the program crashed.

You nailed it. By calling Util.checkGLError() just after glVertexAttribPointer, I triggered the expected exception:
Exception in thread "main" org.lwjgl.opengl.OpenGLException: Invalid value (1281)
	at org.lwjgl.opengl.Util.checkGLError(Util.java:59)
	at crashtest.CrashExample.render(CrashExample.java:131)
	at crashtest.CrashExample.start(CrashExample.java:143)
	at crashtest.CrashExample.main(CrashExample.java:151)


So that's another incentive to wrap glVertexAttribPointer in a less brittle helper :)

Thanks for your time investigating this.

Regards,
Olivier.

Kai

Oh, you gonna like this one:

Since you are using LWJGL 2.9, you can use the lwjgl-debug.jar during development, which does GL error checking after each GL call and raises a Java exception on error.
So no need to wrap anything. :)

Cheers,
Kai

oparisy

If that means no low-level crashes debugging during development, I will definitely use this :) Since error checking is performed for each call, pinpointing the faulty one will be easy.

Thanks for the information!

Regards,
Olivier.