Commit ac4cb04c authored by Matthias Betz's avatar Matthias Betz
Browse files

Fix EBO ordering and context-loss handling in GL viewport



setIndexSet could run before the scene's buffers were generated (ebo == 0),
causing GL_INVALID_OPERATION "buffer not bound". Now the index set is retained
and applied only once the scene is uploaded, re-applied after a fresh upload,
and GL handles are dropped/re-queued on context (re)init.

Co-Authored-By: default avatarClaude Opus 4.8 <noreply@anthropic.com>
parent 60c2c05b
...@@ -30,6 +30,9 @@ public class GLViewport { ...@@ -30,6 +30,9 @@ public class GLViewport {
// state set off-thread, consumed in render callback // state set off-thread, consumed in render callback
private final AtomicReference<SceneData> pendingScene = new AtomicReference<>(); private final AtomicReference<SceneData> pendingScene = new AtomicReference<>();
private final AtomicReference<int[]> pendingIndexSet = new AtomicReference<>(); private final AtomicReference<int[]> pendingIndexSet = new AtomicReference<>();
// last index set seen; retained so it can be (re)applied after the scene is uploaded or the GL
// context is recreated (the index buffer only exists once the scene's buffers have been generated)
private volatile int[] lastIndices;
private volatile int selectedId = 0; private volatile int selectedId = 0;
private volatile boolean wireframe = false; private volatile boolean wireframe = false;
private volatile boolean cull = false; private volatile boolean cull = false;
...@@ -53,7 +56,15 @@ public class GLViewport { ...@@ -53,7 +56,15 @@ public class GLViewport {
public GLViewport() { public GLViewport() {
canvas = new GLCanvas(new JOGLExecutor(), GLProfile.CORE); canvas = new GLCanvas(new JOGLExecutor(), GLProfile.CORE);
canvas.addOnInitEvent(ev -> initialized = false); canvas.addOnInitEvent(ev -> {
// GL context (re)created: shaders/buffers from any previous context are gone. Drop our
// handles and re-queue the current scene so it is re-uploaded into the new context.
initialized = false;
scene.markContextLost();
if (currentScene != null) {
pendingScene.set(currentScene);
}
});
canvas.addOnRenderEvent(ev -> render((JOGLRenderEvent) ev)); canvas.addOnRenderEvent(ev -> render((JOGLRenderEvent) ev));
canvas.addOnReshapeEvent(ev -> { /* aspect handled per-frame from width/height */ }); canvas.addOnReshapeEvent(ev -> { /* aspect handled per-frame from width/height */ });
installInputHandlers(); installInputHandlers();
...@@ -110,13 +121,20 @@ public class GLViewport { ...@@ -110,13 +121,20 @@ public class GLViewport {
} }
SceneData newScene = pendingScene.getAndSet(null); SceneData newScene = pendingScene.getAndSet(null);
boolean uploadedNow = false;
if (newScene != null) { if (newScene != null) {
scene.upload(gl, newScene); scene.upload(gl, newScene);
currentScene = newScene; currentScene = newScene;
uploadedNow = true;
} }
int[] newIndices = pendingIndexSet.getAndSet(null); int[] newIndices = pendingIndexSet.getAndSet(null);
if (newIndices != null) { if (newIndices != null) {
scene.setIndexSet(gl, newIndices); lastIndices = newIndices;
}
// Apply the index set only once the scene's buffers exist (the element buffer is created by
// upload). Re-apply after a fresh upload so the indices survive a scene swap or context loss.
if (scene.hasData() && lastIndices != null && (newIndices != null || uploadedNow)) {
scene.setIndexSet(gl, lastIndices);
} }
if (highlightsDirty) { if (highlightsDirty) {
overlay.setPoints(gl, hlPoints, hlPointColors); overlay.setPoints(gl, hlPoints, hlPointColors);
......
...@@ -58,6 +58,10 @@ public class GlScene { ...@@ -58,6 +58,10 @@ public class GlScene {
/** Re-uploads which triangles to draw (element indices). Cheap; called on filter/LOD changes. */ /** Re-uploads which triangles to draw (element indices). Cheap; called on filter/LOD changes. */
public void setIndexSet(GL3 gl, int[] indices) { public void setIndexSet(GL3 gl, int[] indices) {
if (!uploaded || vao == 0) {
// buffers not generated yet; the caller re-applies once the scene is uploaded
return;
}
gl.glBindVertexArray(vao); gl.glBindVertexArray(vao);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, ebo); gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, ebo);
IntBuffer ib = toIntBuffer(indices); IntBuffer ib = toIntBuffer(indices);
...@@ -78,6 +82,16 @@ public class GlScene { ...@@ -78,6 +82,16 @@ public class GlScene {
public boolean hasData() { return uploaded; } public boolean hasData() { return uploaded; }
/**
* Forgets GL handles after the context was lost/recreated, without issuing GL calls (the old
* handles belong to a dead context). The next {@link #upload} regenerates everything.
*/
public void markContextLost() {
vao = 0;
indexCount = 0;
uploaded = false;
}
public void dispose(GL3 gl) { public void dispose(GL3 gl) {
if (vao != 0) { if (vao != 0) {
gl.glDeleteVertexArrays(1, new int[]{vao}, 0); gl.glDeleteVertexArrays(1, new int[]{vao}, 0);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment