Fix crash replaying display lists compiled during the splash screen (#2250)

This commit is contained in:
Jason Mitchell authored and GitHub committed 2026-10-10 20:38:35 -07:00
1 parent 69428c12f6
commit 23aa04361d
10 files changed
+210 -53

No files matched your search

@@ -1025,7 +1025,7 @@ public class DisplayListManager {
}
final DisplayListCommand[] complexObjects = commandBuffer.getComplexObjects();
compiled = new CompiledDisplayList(commandBuffer.finish(), complexObjects, compiledBuffers, indexedBatches);
compiled = new CompiledDisplayList(commandBuffer.finish(), complexObjects, compiledBuffers, indexedBatches, GLStateManager.ctx());
currentRecorder = null;
} catch (Exception e) {
GLStateManager.LOGGER.error("Encountered a fatal issue while trying to compile a display list.");
@@ -525,8 +525,9 @@ public final class CommandBufferExecutor {
ptr += 4;
}
case GLCommand.BIND_VAO -> {
GLStateManager.glBindVertexArray(memGetInt(ptr));
final int vao = memGetInt(ptr);
ptr += 4;
if (compiledDisplayList.replaysVertexArrayBind(vao)) GLStateManager.glBindVertexArray(vao);
}
// === Complex object reference ===
@@ -1,6 +1,7 @@
package com.gtnewhorizons.angelica.glsm.recording;
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
import com.gtnewhorizons.angelica.glsm.GLContextState;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.recording.commands.DisplayListCommand;
import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatch;
@@ -30,12 +31,13 @@ public final class CompiledDisplayList {
* Empty display list singleton. Per OpenGL spec, an empty list is still valid.
* This instance does nothing when rendered and is never deleted (shared singleton).
*/
public static final CompiledDisplayList EMPTY = new CompiledDisplayList(null, null, null, Collections.emptyList());
public static final CompiledDisplayList EMPTY = new CompiledDisplayList(null, null, null, Collections.emptyList(), null);
private final ByteBuffer commandBuffer; // Off-heap command storage, must be freed
private final DisplayListCommand[] complexObjects; // Complex commands (TexImage2D, etc.)
private final DisplayListVBO ownedVbos; // GPU resources referenced by index
private final List<IndexedDrawBatch> indexedBatches; // Shared VAO/VBO/EBO triples per layout group
private GLContextState compileCtx;
private Int2IntMap replayPrograms;
private long programLifetimeGeneration = -1;
@@ -55,11 +57,16 @@ public final class CompiledDisplayList {
return valid ? program : 0;
}
public CompiledDisplayList(ByteBuffer commandBuffer, DisplayListCommand[] complexObjects, DisplayListVBO ownedVbos, List<IndexedDrawBatch> indexedBatches) {
public CompiledDisplayList(ByteBuffer commandBuffer, DisplayListCommand[] complexObjects, DisplayListVBO ownedVbos, List<IndexedDrawBatch> indexedBatches, GLContextState compileCtx) {
this.commandBuffer = commandBuffer;
this.complexObjects = complexObjects;
this.ownedVbos = ownedVbos;
this.indexedBatches = indexedBatches;
this.compileCtx = compileCtx;
}
boolean replaysVertexArrayBind(int vao) {
return vao == 0 || compileCtx == GLStateManager.ctx();
}
/**
@@ -101,6 +108,7 @@ public final class CompiledDisplayList {
if (commandBuffer != null) {
memFree(commandBuffer);
}
compileCtx = null;
}
/**
@@ -3,21 +3,20 @@ package com.gtnewhorizons.angelica.glsm.recording.commands;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
public final class BatchedIndexedDrawCmd implements DisplayListCommand {
private int sharedVAO;
private IndexedDrawBatch batch;
private int drawMode;
private int indexCount;
private int indexType;
private long indexByteOffset;
public void fill(int sharedVAO, int drawMode, int indexCount, int indexType, long indexByteOffset) {
this.sharedVAO = sharedVAO;
public void fill(IndexedDrawBatch batch, int drawMode, int indexCount, int indexType, long indexByteOffset) {
this.batch = batch;
this.drawMode = drawMode;
this.indexCount = indexCount;
this.indexType = indexType;
this.indexByteOffset = indexByteOffset;
}
public int getSharedVAO() { return sharedVAO; }
public int getDrawMode() { return drawMode; }
public int getIndexCount() { return indexCount; }
public int getIndexType() { return indexType; }
@@ -25,13 +24,13 @@ public final class BatchedIndexedDrawCmd implements DisplayListCommand {
@Override
public void execute() {
if (sharedVAO == 0) return;
GLStateManager.glBindVertexArray(sharedVAO);
if (batch == null) return;
batch.bind();
GLStateManager.glDrawElements(drawMode, indexCount, indexType, indexByteOffset);
}
@Override
public String toString() {
return "BatchedIndexedDrawCmd(vao=" + sharedVAO + ", mode=" + drawMode + ", count=" + indexCount + ", type=" + indexType + ", offset=" + indexByteOffset + ")";
return "BatchedIndexedDrawCmd(batch=" + batch + ", mode=" + drawMode + ", count=" + indexCount + ", type=" + indexType + ", offset=" + indexByteOffset + ")";
}
}
@@ -1,34 +1,60 @@
package com.gtnewhorizons.angelica.glsm.recording.commands;
import com.gtnewhorizons.angelica.glsm.GLContextState;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import org.lwjgl.opengl.GL15;
public final class IndexedDrawBatch {
private int sharedVAO;
private final AttribLayoutKey layout;
private int sharedVBO;
private int sharedEBO;
private int vao;
private GLContextState vaoCtx;
private boolean deleted;
public IndexedDrawBatch(int sharedVAO, int sharedVBO, int sharedEBO) {
this.sharedVAO = sharedVAO;
public IndexedDrawBatch(AttribLayoutKey layout, int sharedVBO, int sharedEBO) {
this.layout = layout;
this.sharedVBO = sharedVBO;
this.sharedEBO = sharedEBO;
}
public int getSharedVAO() { return sharedVAO; }
public int getSharedVBO() { return sharedVBO; }
public int getSharedEBO() { return sharedEBO; }
public void bind() {
final GLContextState glCtx = GLStateManager.ctx();
if (vaoCtx == glCtx) {
GLStateManager.glBindVertexArray(vao);
return;
}
final int prevVBO = GLStateManager.getBoundVBO();
final int v = GLStateManager.glGenVertexArrays();
GLStateManager.glBindVertexArray(v);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, sharedVBO);
final int stride = layout.stride();
for (int i = 0; i < layout.locations.length; i++) {
final int loc = layout.locations[i];
GLStateManager.glVertexAttribPointer(loc, layout.sizes[i], layout.types[i], layout.normalized[i], stride, layout.offset(i));
GLStateManager.glEnableVertexAttribArray(loc);
}
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, sharedEBO);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, prevVBO);
vao = v;
vaoCtx = glCtx;
}
public void delete() {
if (deleted) return;
deleted = true;
if (sharedVAO != 0) GLStateManager.glDeleteVertexArrays(sharedVAO);
if (vaoCtx == GLStateManager.ctx()) GLStateManager.glDeleteVertexArrays(vao);
if (sharedVBO != 0) GLStateManager.glDeleteBuffers(sharedVBO);
if (sharedEBO != 0) GLStateManager.glDeleteBuffers(sharedEBO);
sharedVAO = sharedVBO = sharedEBO = 0;
vao = sharedVBO = sharedEBO = 0;
vaoCtx = null;
}
@Override
public String toString() {
return "IndexedDrawBatch(vao=" + sharedVAO + ", vbo=" + sharedVBO + ", ebo=" + sharedEBO + ")";
return "IndexedDrawBatch(vao=" + vao + ", vbo=" + sharedVBO + ", ebo=" + sharedEBO + ")";
}
}
@@ -35,7 +35,6 @@ public final class IndexedDrawBatchBuilder {
final List<IndexedDrawBatch> out = new ArrayList<>();
if (captures.isEmpty()) return out;
final int prevVAO = GLStateManager.getBoundVAO();
final int prevVBO = GLStateManager.getBoundVBO();
final int prevEBO = GLStateManager.getBoundEBO();
@@ -58,7 +57,6 @@ public final class IndexedDrawBatchBuilder {
if (!success) {
for (GroupResult r : results) r.batch.delete();
}
GLStateManager.glBindVertexArray(prevVAO);
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEBO);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, prevVBO);
}
@@ -69,9 +67,8 @@ public final class IndexedDrawBatchBuilder {
private record GroupResult(IndexedDrawBatch batch, List<IndexedDrawCapture> captures,
int[] drawModes, int[] indexCounts, long[] indexOffsets) {
void commit() {
final int sharedVAO = batch.getSharedVAO();
for (int i = 0; i < captures.size(); i++) {
captures.get(i).placeholder.fill(sharedVAO, drawModes[i], indexCounts[i], GL11.GL_UNSIGNED_INT, indexOffsets[i]);
captures.get(i).placeholder.fill(batch, drawModes[i], indexCounts[i], GL11.GL_UNSIGNED_INT, indexOffsets[i]);
}
}
}
@@ -87,7 +84,6 @@ public final class IndexedDrawBatchBuilder {
throw new IllegalStateException("batch exceeds 2GiB — split needed");
}
int sharedVAO = 0;
int sharedVBO = 0;
int sharedEBO = 0;
ByteBuffer eboData = null;
@@ -99,12 +95,10 @@ public final class IndexedDrawBatchBuilder {
final long[] indexOffsets = new long[n];
try {
sharedVAO = GLStateManager.glGenVertexArrays();
sharedVBO = GLStateManager.glGenBuffers();
sharedEBO = GLStateManager.glGenBuffers();
eboData = MemoryUtilities.memAlloc((int) totalIndexBytes);
GLStateManager.glBindVertexArray(sharedVAO);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, sharedVBO);
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, totalVertexBytes, GL15.GL_STATIC_DRAW);
@@ -146,23 +140,12 @@ public final class IndexedDrawBatchBuilder {
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, sharedEBO);
GLStateManager.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, eboData, GL15.GL_STATIC_DRAW);
final AttribLayoutKey layout = group.get(0).layoutKey;
final int stride = layout.stride();
for (int i = 0; i < layout.locations.length; i++) {
final int loc = layout.locations[i];
final int size = layout.sizes[i];
final int type = layout.types[i];
GLStateManager.glVertexAttribPointer(loc, size, type, layout.normalized[i], stride, layout.offset(i));
GLStateManager.glEnableVertexAttribArray(loc);
}
success = true;
return new GroupResult(new IndexedDrawBatch(sharedVAO, sharedVBO, sharedEBO),
return new GroupResult(new IndexedDrawBatch(group.get(0).layoutKey, sharedVBO, sharedEBO),
group, drawModes, indexCounts, indexOffsets);
} finally {
if (eboData != null) MemoryUtilities.memFree(eboData);
if (!success) {
if (sharedVAO != 0) GLStateManager.glDeleteVertexArrays(sharedVAO);
if (sharedVBO != 0) GLStateManager.glDeleteBuffers(sharedVBO);
if (sharedEBO != 0) GLStateManager.glDeleteBuffers(sharedEBO);
}
@@ -3,6 +3,8 @@ package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.glsm.ffp.FfpFixture;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatch;
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
import com.gtnewhorizons.angelica.glsm.testutil.TestThreads;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
@@ -14,6 +16,7 @@ import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.SharedDrawable;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.concurrent.CountDownLatch;
@@ -29,14 +32,22 @@ import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
@GLCoreTest
public class GLSM_SharedDrawableContext_GLTest {
private SharedDrawable sharedDrawable;
private int listVbo;
private int listEbo;
private int indexedList;
private int sharedContextVao;
@AfterEach
void cleanup() {
if (indexedList != 0) GLStateManager.glDeleteLists(indexedList, 1);
if (listVbo != 0) GLStateManager.glDeleteBuffers(listVbo);
if (listEbo != 0) GLStateManager.glDeleteBuffers(listEbo);
if (sharedDrawable != null) {
DrawableContexts.destroy(sharedDrawable);
sharedDrawable = null;
@@ -222,6 +233,113 @@ public class GLSM_SharedDrawableContext_GLTest {
});
}
private int compileIndexedListOnSharedDrawable() throws Exception {
sharedDrawable = new SharedDrawable(Display.getDrawable());
DrawableContexts.run(sharedDrawable, "SharedDrawableContext-IndexedList-Thread", () -> {
final FloatBuffer vertices = BufferUtils.createFloatBuffer(9);
vertices.put(new float[] { -0.5f, -0.5f, 0f, 0.5f, -0.5f, 0f, 0f, 0.5f, 0f });
vertices.flip();
final ByteBuffer indices = BufferUtils.createByteBuffer(6);
indices.putShort((short) 0).putShort((short) 1).putShort((short) 2);
indices.flip();
listVbo = GLStateManager.glGenBuffers();
listEbo = GLStateManager.glGenBuffers();
GLStateManager.glBindVertexArray(GLStateManager.glGenVertexArrays());
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, listVbo);
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, vertices, GL15.GL_STATIC_DRAW);
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0L);
GLStateManager.glEnableVertexAttribArray(0);
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, listEbo);
GLStateManager.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indices, GL15.GL_STATIC_DRAW);
indexedList = GLStateManager.glGenLists(1);
GLStateManager.glNewList(indexedList, GL11.GL_COMPILE);
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);
GLStateManager.glEndList();
final IndexedDrawBatch batch = indexedBatch();
batch.bind();
sharedContextVao = Reflect.<Integer>get(batch, "vao");
});
return indexedList;
}
private IndexedDrawBatch indexedBatch() {
return DisplayListManager.getDisplayList(indexedList).getIndexedBatches().get(0);
}
@Test
void indexedListCompiledOnSharedDrawableReplaysOnDisplay() throws Exception {
final int list = compileIndexedListOnSharedDrawable();
final ShaderManager sm = ShaderManager.getInstance();
final boolean wasEnabled = ShaderManager.isEnabled();
ShaderManager.enable();
sm.activate();
try {
GLStateManager.glCallList(list);
indexedBatch().bind();
assertEquals(indexedBatch().getSharedEBO(), GL11.glGetInteger(GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING));
} finally {
sm.deactivate();
if (!wasEnabled) ShaderManager.disable();
GLStateManager.glBindVertexArray(0);
}
}
@Test
void deletingListLeavesDisplayVaoWithTheSharedContextName() throws Exception {
final int list = compileIndexedListOnSharedDrawable();
final IntBuffer made = BufferUtils.createIntBuffer(4096);
while (!GL30.glIsVertexArray(sharedContextVao) && made.hasRemaining()) {
final int v = GLStateManager.glGenVertexArrays();
made.put(v);
if (v == sharedContextVao) GLStateManager.glBindVertexArray(v);
}
GLStateManager.glBindVertexArray(0);
assumeTrue(GL30.glIsVertexArray(sharedContextVao));
GLStateManager.glDeleteLists(list, 1);
indexedList = 0;
assertTrue(GL30.glIsVertexArray(sharedContextVao));
made.flip();
while (made.hasRemaining()) GLStateManager.glDeleteVertexArrays(made.get());
}
@Test
void recordedVaoBindReplaysOnlyOnItsCompileContext() throws Exception {
final int bound = GLStateManager.glGenVertexArrays();
final int local = GLStateManager.glGenVertexArrays();
final int localList = GLStateManager.glGenLists(1);
final int foreignList = GLStateManager.glGenLists(1);
GLStateManager.glNewList(localList, GL11.GL_COMPILE);
GLStateManager.glBindVertexArray(local);
GLStateManager.glEndList();
sharedDrawable = new SharedDrawable(Display.getDrawable());
DrawableContexts.run(sharedDrawable, "SharedDrawableContext-VaoBind-Thread", () -> {
int foreign;
do foreign = GLStateManager.glGenVertexArrays(); while (foreign == bound);
GLStateManager.glNewList(foreignList, GL11.GL_COMPILE);
GLStateManager.glBindVertexArray(foreign);
GLStateManager.glEndList();
});
GLStateManager.glBindVertexArray(bound);
GLStateManager.glCallList(foreignList);
assertEquals(bound, GLStateManager.getBoundVAO());
GLStateManager.glCallList(localList);
assertEquals(local, GLStateManager.getBoundVAO());
GLStateManager.glBindVertexArray(0);
GLStateManager.glDeleteVertexArrays(bound);
GLStateManager.glDeleteVertexArrays(local);
GLStateManager.glDeleteLists(localList, 1);
GLStateManager.glDeleteLists(foreignList, 1);
}
@Test
void deferredTextureDeletesWaitForSplashAndScrubDrawableContexts() throws Exception {
sharedDrawable = new SharedDrawable(Display.getDrawable());
@@ -27,9 +27,10 @@ import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.params.provider.Arguments.arguments;
@@ -234,8 +235,9 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
final int vao = setupPosColorVao(vbo);
final int list = recordSingleIndexedDraw(vao, ebo, GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT);
replay(list);
final IndexedDrawBatch batch = firstBatch(list);
final int sharedVAO = batch.getSharedVAO();
final int sharedVAO = vaoOf(batch);
final int sharedVBO = batch.getSharedVBO();
final int sharedEBO = batch.getSharedEBO();
assertTrue(GL15.glIsBuffer(sharedVBO), "shared VBO live");
@@ -257,8 +259,9 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
final int vao = setupPosColorVao(vbo);
final int list = recordSingleIndexedDraw(vao, ebo, GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT);
replay(list);
final IndexedDrawBatch first = firstBatch(list);
final int oldVAO = first.getSharedVAO();
final int oldVAO = vaoOf(first);
final int oldVBO = first.getSharedVBO();
final int oldEBO = first.getSharedEBO();
@@ -341,8 +344,8 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
final List<IndexedDrawBatch> batches = batchesOf(list);
assertEquals(1, batches.size(), "same layout → one shared batch");
assertEquals(cmds.get(0).getSharedVAO(), cmds.get(1).getSharedVAO(), "shared VAO across draws in the same batch");
assertEquals(batches.getFirst().getSharedVAO(), cmds.get(0).getSharedVAO());
assertSame(batchOf(cmds.get(0)), batchOf(cmds.get(1)), "shared batch across draws in the same layout");
assertSame(batches.getFirst(), batchOf(cmds.get(0)));
assertEquals(0L, cmds.get(0).getIndexByteOffset());
assertEquals(3 * 4L, cmds.get(1).getIndexByteOffset(), "second draw offset = first.indexCount * sizeof(UINT)");
@@ -396,7 +399,7 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
final List<BatchedIndexedDrawCmd> cmds = allBatchedCmds(list);
assertEquals(2, cmds.size());
assertEquals(2, batchesOf(list).size(), "incompatible layouts → two batches");
assertNotEquals(cmds.get(0).getSharedVAO(), cmds.get(1).getSharedVAO(), "VAOs from separate batches must be distinct");
assertNotSame(batchOf(cmds.get(0)), batchOf(cmds.get(1)), "separate layouts must get distinct batches");
}
@Test
@@ -435,7 +438,7 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
final List<BatchedIndexedDrawCmd> cmds = allBatchedCmds(list);
assertEquals(2, cmds.size());
assertEquals(2, batchesOf(list).size(), "different enabled locations → two batches");
assertNotEquals(cmds.get(0).getSharedVAO(), cmds.get(1).getSharedVAO(), "VAOs from separate batches must be distinct");
assertNotSame(batchOf(cmds.get(0)), batchOf(cmds.get(1)), "separate layouts must get distinct batches");
}
@Test
@@ -495,13 +498,13 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
final List<BatchedIndexedDrawCmd> ba = allBatchedCmds(listBA);
// Placeholders sit in command-stream order (A-first vs B-first).
// Batches must be ordered so that placeholder[0] points at batch[0].
assertEquals(ab.get(0).getSharedVAO(), batchesOf(listAB).get(0).getSharedVAO(),
assertSame(batchesOf(listAB).get(0), batchOf(ab.get(0)),
"listAB: batch[0] must match the first-recorded layout");
assertEquals(ab.get(1).getSharedVAO(), batchesOf(listAB).get(1).getSharedVAO(),
assertSame(batchesOf(listAB).get(1), batchOf(ab.get(1)),
"listAB: batch[1] must match the second-recorded layout");
assertEquals(ba.get(0).getSharedVAO(), batchesOf(listBA).get(0).getSharedVAO(),
assertSame(batchesOf(listBA).get(0), batchOf(ba.get(0)),
"listBA: batch[0] must match the first-recorded layout");
assertEquals(ba.get(1).getSharedVAO(), batchesOf(listBA).get(1).getSharedVAO(),
assertSame(batchesOf(listBA).get(1), batchOf(ba.get(1)),
"listBA: batch[1] must match the second-recorded layout");
}
@@ -18,7 +18,10 @@ import java.util.List;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -72,7 +75,8 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
assertNotNull(batched);
final IndexedDrawBatch batch = firstBatch(list);
GLStateManager.glBindVertexArray(batched.getSharedVAO());
replay(list);
GLStateManager.glBindVertexArray(vaoOf(batch));
assertEquals(batch.getSharedEBO(), GLStateManager.getBoundEBO(),
"batched VAO's EBO binding corrupted");
GLStateManager.glBindVertexArray(0);
@@ -120,8 +124,6 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
final List<BatchedIndexedDrawCmd> cmds = allBatchedCmds(list);
assertEquals(2, cmds.size());
final int sharedVAO = cmds.getFirst().getSharedVAO();
assertEquals(sharedVAO, cmds.get(1).getSharedVAO());
final int distractor = newVao();
GLStateManager.glBindVertexArray(distractor);
@@ -129,6 +131,9 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
compiledOf(list).render();
assertSame(batchOf(cmds.getFirst()), batchOf(cmds.get(1)));
final int sharedVAO = vaoOf(batchOf(cmds.getFirst()));
assertNotEquals(0, sharedVAO);
assertEquals(sharedVAO, GLStateManager.getBoundVAO());
GLStateManager.glBindVertexArray(0);
}
@@ -160,8 +165,8 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
try {
assertThrows(IllegalStateException.class, builder::build);
assertEquals(0, good.placeholder.getSharedVAO());
assertEquals(0, huge.placeholder.getSharedVAO());
assertNull(batchOf(good.placeholder));
assertNull(batchOf(huge.placeholder));
} finally {
MemoryUtilities.memFree(tinyVtx);
MemoryUtilities.memFree(tinyIdx);
@@ -8,6 +8,7 @@ import com.gtnewhorizons.angelica.glsm.GLTypes;
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
import com.gtnewhorizons.angelica.glsm.recording.commands.BatchedIndexedDrawCmd;
import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatch;
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
import org.junit.jupiter.api.AfterEach;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
@@ -176,6 +177,19 @@ public abstract class DisplayListTestFixture {
return batchesOf(listId).getFirst();
}
protected static void replay(int listId) {
compiledOf(listId).render();
GLStateManager.glBindVertexArray(0);
}
protected static IndexedDrawBatch batchOf(BatchedIndexedDrawCmd cmd) {
return Reflect.get(cmd, "batch");
}
protected static int vaoOf(IndexedDrawBatch batch) {
return Reflect.<Integer>get(batch, "vao");
}
protected static ByteBuffer readBufferBytes(int target, int bufferId, int size) {
final int prev;
if (target == GL15.GL_ARRAY_BUFFER) prev = GLStateManager.getBoundVBO();