mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-10-25 20:22:22 +00:00
Support more glDrawElement overloads in display lists (#1895)
This commit is contained in:
1 parent
3abc89b7b2
commit
26135124f4
6 files changed
+377
-93
No files matched your search
@@ -133,7 +133,10 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
public static UnsupportedOperationException unsupportedInList(String what) {
|
||||
return new UnsupportedOperationException(what + " in display lists not yet implemented - if you see this, please report! " + describeCurrentCompilation());
|
||||
final UnsupportedOperationException e = new UnsupportedOperationException( what + " in display lists not yet implemented - if you see this, please report! " + describeCurrentCompilation());
|
||||
GLStateManager.warnOnce("unsupported:" + what, "Aborting display list {} compilation: {} unsupported during recording", glListId, what);
|
||||
abortCompilation();
|
||||
return e;
|
||||
}
|
||||
|
||||
public static String describeCurrentCompilation() {
|
||||
@@ -798,7 +801,8 @@ public class DisplayListManager {
|
||||
*/
|
||||
public static void glEndList() {
|
||||
if (glListMode == 0) {
|
||||
throw new RuntimeException("glEndList called outside of a display list!");
|
||||
GLStateManager.warnOnce("endlist-outside", "glEndList called outside of a display list!");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
finishCurrentList();
|
||||
@@ -906,17 +910,56 @@ public class DisplayListManager {
|
||||
// Parent's COMPILING callback is now active again
|
||||
} else {
|
||||
// Not nested - clear all compilation state
|
||||
currentRecorder = null;
|
||||
recordingThread = null;
|
||||
accumulatedDraws = null;
|
||||
pendingDraw = null;
|
||||
transformCallback = null;
|
||||
compilationStackTrace = null;
|
||||
pendingTransformOps = null;
|
||||
multMatrixSources = null;
|
||||
drawRangeSources = null;
|
||||
glListId = -1;
|
||||
glListMode = 0;
|
||||
resetCompilationState();
|
||||
}
|
||||
}
|
||||
|
||||
/** Clear all root-level compilation state back to "not recording". */
|
||||
private static void resetCompilationState() {
|
||||
currentRecorder = null;
|
||||
recordingThread = null;
|
||||
accumulatedDraws = null;
|
||||
pendingDraw = null;
|
||||
transformCallback = null;
|
||||
compilationStackTrace = null;
|
||||
pendingTransformOps = null;
|
||||
multMatrixSources = null;
|
||||
drawRangeSources = null;
|
||||
glListId = -1;
|
||||
glListMode = 0;
|
||||
}
|
||||
|
||||
public static void abortCompilation() {
|
||||
if (glListMode == 0 && compilationStack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int levels = compilationStack.size() + (glListMode != 0 ? 1 : 0);
|
||||
for (int i = 0; i < levels; i++) {
|
||||
try {
|
||||
TessellatorManager.stopCapturingDirect();
|
||||
} catch (IllegalStateException ignored) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentRecorder != null) {
|
||||
currentRecorder.delete();
|
||||
}
|
||||
for (CompilationContext ctx : compilationStack) {
|
||||
if (ctx.recorder() != null) {
|
||||
ctx.recorder().delete();
|
||||
}
|
||||
}
|
||||
compilationStack.clear();
|
||||
|
||||
resetCompilationState();
|
||||
}
|
||||
|
||||
public static void abortIfLeaked() {
|
||||
if (glListMode != 0 || !compilationStack.isEmpty()) {
|
||||
GLStateManager.warnOnce("frame-leak", "Display list {} left open across a frame boundary - aborting to recover", glListId);
|
||||
abortCompilation();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.IntSupplier;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.Vendor.AMD;
|
||||
@@ -120,6 +121,12 @@ public class GLStateManager {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger("GLSM");
|
||||
|
||||
private static final Set<String> WARN_ONCE = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public static void warnOnce(String key, String fmt, Object... args) {
|
||||
if (WARN_ONCE.add(key)) LOGGER.warn(fmt, args);
|
||||
}
|
||||
|
||||
// Thread Checking - must be early in static init order so isMainThread() works for state initialization
|
||||
@Getter private static final Thread MainThread = Thread.currentThread();
|
||||
|
||||
@@ -2248,70 +2255,99 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, ByteBuffer indices) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glDrawElements");
|
||||
}
|
||||
if (FeedbackManager.isFeedbackMode()) {
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, indices.remaining(), GL11.GL_UNSIGNED_BYTE, MemoryUtilities.memAddress(indices), indices.remaining());
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, indices);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices.remaining(), GL11.GL_UNSIGNED_BYTE, indices);
|
||||
return;
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices.remaining(), GL11.GL_UNSIGNED_BYTE, indices);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, IntBuffer indices) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glDrawElements");
|
||||
}
|
||||
if (FeedbackManager.isFeedbackMode()) {
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, indices.remaining(), GL11.GL_UNSIGNED_INT, MemoryUtilities.memAddress(indices), (long) indices.remaining() << 2);
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, indices);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices);
|
||||
return;
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, ShortBuffer indices) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glDrawElements");
|
||||
}
|
||||
if (FeedbackManager.isFeedbackMode()) {
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, indices.remaining(), GL11.GL_UNSIGNED_SHORT, MemoryUtilities.memAddress(indices), (long) indices.remaining() << 1);
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, indices);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices);
|
||||
return;
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices);
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, int count, int type, ByteBuffer indices) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glDrawElements");
|
||||
}
|
||||
if (FeedbackManager.isFeedbackMode()) {
|
||||
CommandRecorder savedRecorder = null;
|
||||
final RecordMode recordMode = DisplayListManager.getRecordMode();
|
||||
if (recordMode != RecordMode.NONE) {
|
||||
final IndexedDrawCapture capture = IndexedDrawCapture.createFromClientIndices(mode, count, type, MemoryUtilities.memAddress(indices), indices.remaining());
|
||||
if (capture != null) DisplayListManager.recordIndexedDrawCapture(capture);
|
||||
if (recordMode == RecordMode.COMPILE) return;
|
||||
savedRecorder = DisplayListManager.pauseRecording();
|
||||
} else if (FeedbackManager.isFeedbackMode()) {
|
||||
FeedbackManager.processDrawElements(mode, count, type, indices);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(count, type, indices);
|
||||
return;
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(count, type, indices);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, count, type, indices);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, count, type, indices);
|
||||
}
|
||||
|
||||
public static void glDrawElements(int mode, int indices_count, int type, long indices_buffer_offset) {
|
||||
@@ -2332,14 +2368,16 @@ public class GLStateManager {
|
||||
FeedbackManager.processDrawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
return;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset);
|
||||
return;
|
||||
try {
|
||||
if (mode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset);
|
||||
return;
|
||||
}
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
} finally {
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
preDraw(mode);
|
||||
RENDER_BACKEND.drawElements(mode, indices_count, type, indices_buffer_offset);
|
||||
if (savedRecorder != null) DisplayListManager.resumeRecording(savedRecorder);
|
||||
}
|
||||
|
||||
public static void glDrawBuffer(int mode) {
|
||||
|
||||
+53
-33
@@ -9,8 +9,6 @@ import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetInt;
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetLong;
|
||||
@@ -52,20 +50,14 @@ public final class IndexedDrawCapture {
|
||||
return freed;
|
||||
}
|
||||
|
||||
private static final Set<String> WARN_ONCE = ConcurrentHashMap.newKeySet();
|
||||
|
||||
private static void warnOnce(String key, String fmt, Object... args) {
|
||||
if (WARN_ONCE.add(key)) GLStateManager.LOGGER.warn(fmt, args);
|
||||
}
|
||||
|
||||
public static IndexedDrawCapture create(int mode, int indicesCount, int srcIndexType, long indicesOffset, int eboId) {
|
||||
if (indicesCount == 0) return null;
|
||||
if (eboId == 0) {
|
||||
warnOnce("no-ebo", "[IndexedDrawCapture] glDrawElements in display list with no EBO bound — skipping (mode={} count={} offset={})", mode, indicesCount, indicesOffset);
|
||||
GLStateManager.warnOnce("no-ebo", "[IndexedDrawCapture] glDrawElements in display list with no EBO bound — skipping (mode={} count={} offset={})", mode, indicesCount, indicesOffset);
|
||||
return null;
|
||||
}
|
||||
if (mode == GL11.GL_QUADS && (indicesCount % 4) != 0) {
|
||||
warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 — skipping", indicesCount);
|
||||
GLStateManager.warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 — skipping", indicesCount);
|
||||
return null;
|
||||
}
|
||||
final int srcIndexSize = VAOManager.Attrib.glTypeSizeBytes(srcIndexType);
|
||||
@@ -74,29 +66,53 @@ public final class IndexedDrawCapture {
|
||||
final int prevEBO = GLStateManager.getBoundEBO();
|
||||
final int prevVBO = GLStateManager.getBoundVBO();
|
||||
ByteBuffer eboSrc = null;
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||
final int eboBufferSize = GLStateManager.glGetBufferParameteri(GL15.GL_ELEMENT_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
|
||||
if (eboBufferSize <= 0) {
|
||||
GLStateManager.warnOnce("ebo-size", "[IndexedDrawCapture] GL_BUFFER_SIZE={} for EBO {}", eboBufferSize, eboId);
|
||||
return null;
|
||||
}
|
||||
final long eboReadSize = (long) indicesCount * srcIndexSize;
|
||||
if (indicesOffset < 0 || indicesOffset + eboReadSize > eboBufferSize) {
|
||||
GLStateManager.warnOnce("ebo-range", "[IndexedDrawCapture] EBO range {}+{} exceeds buffer size {} — skipping", indicesOffset, eboReadSize, eboBufferSize);
|
||||
return null;
|
||||
}
|
||||
eboSrc = MemoryUtilities.memAlloc((int) eboReadSize);
|
||||
GLStateManager.glGetBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesOffset, eboSrc);
|
||||
return bake(mode, indicesCount, srcIndexType, MemoryUtilities.memAddress0(eboSrc));
|
||||
} finally {
|
||||
if (eboSrc != null) MemoryUtilities.memFree(eboSrc);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEBO);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, prevVBO);
|
||||
}
|
||||
}
|
||||
|
||||
public static IndexedDrawCapture createFromClientIndices(int mode, int indicesCount, int srcIndexType, long indexSrcAddr, long srcByteLength) {
|
||||
if (indicesCount == 0) return null;
|
||||
if (indexSrcAddr == 0L) return null;
|
||||
if (mode == GL11.GL_QUADS && (indicesCount % 4) != 0) {
|
||||
GLStateManager.warnOnce("quads-misaligned", "[IndexedDrawCapture] GL_QUADS with indicesCount={} not a multiple of 4 - skipping", indicesCount);
|
||||
return null;
|
||||
}
|
||||
final int idxSize = VAOManager.Attrib.glTypeSizeBytes(srcIndexType);
|
||||
if (idxSize <= 0) return null;
|
||||
if ((long) indicesCount * idxSize > srcByteLength) {
|
||||
GLStateManager.warnOnce("client-idx-range", "[IndexedDrawCapture] index count {} ({} bytes) exceeds client buffer {} bytes - skipping", indicesCount, (long) indicesCount * idxSize, srcByteLength);
|
||||
return null;
|
||||
}
|
||||
return bake(mode, indicesCount, srcIndexType, indexSrcAddr);
|
||||
}
|
||||
|
||||
private static IndexedDrawCapture bake(int mode, int indicesCount, int srcIndexType, long indexSrcAddr) {
|
||||
AttribSnapshot snap = null;
|
||||
ByteBuffer vertexData = null;
|
||||
ByteBuffer indexData = null;
|
||||
boolean success = false;
|
||||
try {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||
final int eboBufferSize = GLStateManager.glGetBufferParameteri(GL15.GL_ELEMENT_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
|
||||
if (eboBufferSize <= 0) {
|
||||
warnOnce("ebo-size", "[IndexedDrawCapture] GL_BUFFER_SIZE={} for EBO {}", eboBufferSize, eboId);
|
||||
return null;
|
||||
}
|
||||
final long eboReadSize = (long) indicesCount * srcIndexSize;
|
||||
if (indicesOffset < 0 || indicesOffset + eboReadSize > eboBufferSize) {
|
||||
warnOnce("ebo-range", "[IndexedDrawCapture] EBO range {}+{} exceeds buffer size {} — skipping", indicesOffset, eboReadSize, eboBufferSize);
|
||||
return null;
|
||||
}
|
||||
eboSrc = MemoryUtilities.memAlloc((int) eboReadSize);
|
||||
GLStateManager.glGetBufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesOffset, eboSrc);
|
||||
final long eboSrcAddr = MemoryUtilities.memAddress0(eboSrc);
|
||||
|
||||
final long minMax = QuadConverter.scanMinMaxIndex(eboSrcAddr, srcIndexType, indicesCount);
|
||||
final long minMax = QuadConverter.scanMinMaxIndex(indexSrcAddr, srcIndexType, indicesCount);
|
||||
if (minMax == -1L) {
|
||||
warnOnce("min-max", "[IndexedDrawCapture] failed to determine min/max vertex for indexType={}", srcIndexType);
|
||||
GLStateManager.warnOnce("min-max", "[IndexedDrawCapture] failed to determine min/max vertex for indexType={}", srcIndexType);
|
||||
return null;
|
||||
}
|
||||
final int minVtx = (int) (minMax & 0xFFFFFFFFL);
|
||||
@@ -110,7 +126,7 @@ public final class IndexedDrawCapture {
|
||||
if (snap.get(i) != null) enabledCount++;
|
||||
}
|
||||
if (enabledCount == 0) {
|
||||
warnOnce("no-attribs", "[IndexedDrawCapture] no enabled attributes — skipping");
|
||||
GLStateManager.warnOnce("no-attribs", "[IndexedDrawCapture] no enabled attributes — skipping");
|
||||
return null;
|
||||
}
|
||||
final int[] locations = new int[enabledCount];
|
||||
@@ -137,6 +153,13 @@ public final class IndexedDrawCapture {
|
||||
for (int a = 0; a < enabledCount; a++) {
|
||||
final AttribSnapshot.AttribDesc d = snap.get(locations[a]);
|
||||
final int relOffset = (int) (d.offset() - d.readBufferBaseOffset());
|
||||
|
||||
final long readStart = relOffset + (long) minVtx * d.effectiveStride();
|
||||
final long readEnd = relOffset + (long) maxVtx * d.effectiveStride() + (long) d.size() * d.typeSizeBytes();
|
||||
if (readStart < 0 || readEnd > d.readBuffer().capacity()) {
|
||||
GLStateManager.warnOnce("vtx-oob", "[IndexedDrawCapture] attrib {} read extent {} exceeds snapshot {} bytes - skipping", d.location(), readEnd, d.readBuffer().capacity());
|
||||
return null;
|
||||
}
|
||||
if (a == 0) {
|
||||
sharedSrcBuffer = d.readBuffer();
|
||||
sharedSrcStride = d.effectiveStride();
|
||||
@@ -169,12 +192,12 @@ public final class IndexedDrawCapture {
|
||||
final int quadCount = indicesCount / 4;
|
||||
bakedIndexCount = quadCount * 6;
|
||||
indexData = MemoryUtilities.memAlloc(bakedIndexCount * 4);
|
||||
QuadConverter.triangulateQuads(eboSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, quadCount, minVtx);
|
||||
QuadConverter.triangulateQuads(indexSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, quadCount, minVtx);
|
||||
bakedDrawMode = GL11.GL_TRIANGLES;
|
||||
} else {
|
||||
bakedIndexCount = indicesCount;
|
||||
indexData = MemoryUtilities.memAlloc(bakedIndexCount * 4);
|
||||
QuadConverter.widenIndices(eboSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, indicesCount, minVtx);
|
||||
QuadConverter.widenIndices(indexSrcAddr, srcIndexType, MemoryUtilities.memAddress0(indexData), GL11.GL_UNSIGNED_INT, indicesCount, minVtx);
|
||||
bakedDrawMode = mode;
|
||||
}
|
||||
|
||||
@@ -184,14 +207,11 @@ public final class IndexedDrawCapture {
|
||||
success = true;
|
||||
return out;
|
||||
} finally {
|
||||
if (eboSrc != null) MemoryUtilities.memFree(eboSrc);
|
||||
if (snap != null) snap.free();
|
||||
if (!success) {
|
||||
if (vertexData != null) MemoryUtilities.memFree(vertexData);
|
||||
if (indexData != null) MemoryUtilities.memFree(indexData);
|
||||
}
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEBO);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, prevVBO);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+28
@@ -9,9 +9,11 @@ import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
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.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/** Tests GLSM display list behavior matches OpenGL spec. */
|
||||
@@ -257,4 +259,30 @@ class DisplayListCompilationGLSMTest {
|
||||
|
||||
GLStateManager.glEndList();
|
||||
}
|
||||
|
||||
@Test
|
||||
void glsm_unsupportedOpInList_abortsCleanly() {
|
||||
testList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
assertTrue(DisplayListManager.isRecording(), "recording after glNewList");
|
||||
|
||||
assertThrows(UnsupportedOperationException.class, () -> GLStateManager.glReadBuffer(GL11.GL_FRONT));
|
||||
|
||||
assertFalse(DisplayListManager.isRecording(), "recording state must be reset after an in-list throw");
|
||||
assertEquals(DisplayListManager.RecordMode.NONE, DisplayListManager.getRecordMode());
|
||||
|
||||
assertDoesNotThrow(() -> GLStateManager.glReadBuffer(GL11.GL_FRONT));
|
||||
assertDoesNotThrow(GLStateManager::glEndList);
|
||||
}
|
||||
|
||||
@Test
|
||||
void glsm_abortIfLeaked_recoversOpenList() {
|
||||
testList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
assertTrue(DisplayListManager.isRecording());
|
||||
|
||||
DisplayListManager.abortIfLeaked();
|
||||
|
||||
assertFalse(DisplayListManager.isRecording(), "frame-begin backstop must clear a leaked open list");
|
||||
}
|
||||
}
|
||||
+154
-1
@@ -9,6 +9,9 @@ import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatch;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.support.DisplayListTestFixture;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -17,7 +20,10 @@ import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -25,6 +31,7 @@ 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.assertTrue;
|
||||
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||
|
||||
class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
|
||||
@@ -521,7 +528,153 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
"listBA: batch[1] must match the second-recorded layout");
|
||||
}
|
||||
|
||||
// === local helpers ===
|
||||
private static Stream<Arguments> clientIndexedDrawCases() {
|
||||
return Stream.of(
|
||||
arguments("byte indices", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final ByteBuffer idx = BufferUtils.createByteBuffer(3);
|
||||
idx.put(new byte[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
arguments("short indices", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final ShortBuffer idx = BufferUtils.createShortBuffer(3);
|
||||
idx.put(new short[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
arguments("int indices", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(3);
|
||||
idx.put(new int[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
arguments("explicit count+type", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final ByteBuffer idx = BufferUtils.createByteBuffer(3 * 2).order(ByteOrder.nativeOrder());
|
||||
idx.putShort((short) 0).putShort((short) 1).putShort((short) 2).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
arguments("quads triangulate at record", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(4);
|
||||
idx.put(new int[]{0, 1, 2, 3}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_QUADS, idx);
|
||||
}, GL11.GL_TRIANGLES, 6),
|
||||
arguments("compile-and-execute fires live and bakes", GL11.GL_COMPILE_AND_EXECUTE, (Runnable) () -> {
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(3);
|
||||
idx.put(new int[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("clientIndexedDrawCases")
|
||||
void clientIndexedDraw_capturedAndWidenedToUnsignedInt(String name, int listMode, Runnable draw, int expectedMode, int expectedCount) {
|
||||
final int list = recordClientIndexDraw(listMode, draw);
|
||||
final BatchedIndexedDrawCmd cmd = firstBatchedCmd(list);
|
||||
assertNotNull(cmd);
|
||||
assertEquals(expectedMode, cmd.getDrawMode());
|
||||
assertEquals(expectedCount, cmd.getIndexCount());
|
||||
assertEquals(GL11.GL_UNSIGNED_INT, cmd.getIndexType(), "client indices always widen to UNSIGNED_INT");
|
||||
}
|
||||
|
||||
@Test
|
||||
void clientVerticesAndIndices_pointersSetDuringRecording_bake() {
|
||||
final float[][] verts = {{0f, 0f, 0f}, {1f, 0f, 0f}, {1f, 1f, 0f}, {0f, 1f, 0f}};
|
||||
final ByteBuffer clientVerts = BufferUtils.createByteBuffer(verts.length * 12).order(ByteOrder.nativeOrder());
|
||||
for (float[] v : verts) {
|
||||
clientVerts.putFloat(v[0]);
|
||||
clientVerts.putFloat(v[1]);
|
||||
clientVerts.putFloat(v[2]);
|
||||
}
|
||||
clientVerts.flip();
|
||||
final IntBuffer indices = BufferUtils.createIntBuffer(6);
|
||||
indices.put(new int[]{0, 1, 2, 2, 3, 0}).flip();
|
||||
|
||||
final int vao = newVao();
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
VAOManager.setAttribute(0, 3, GL11.GL_FLOAT, false, 0, clientVerts);
|
||||
VAOManager.enableAttribute(0);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, indices);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
final BatchedIndexedDrawCmd cmd = firstBatchedCmd(list);
|
||||
assertNotNull(cmd);
|
||||
assertEquals(GL11.GL_TRIANGLES, cmd.getDrawMode());
|
||||
assertEquals(6, cmd.getIndexCount());
|
||||
assertEquals(GL11.GL_UNSIGNED_INT, cmd.getIndexType());
|
||||
|
||||
final ByteBuffer owned = readBufferBytes(GL15.GL_ARRAY_BUFFER, firstBatch(list).getSharedVBO(), verts.length * 12);
|
||||
for (int i = 0; i < verts.length; i++) {
|
||||
assertEquals(verts[i][0], owned.getFloat(i * 12), 0f, "pos.x v=" + i);
|
||||
assertEquals(verts[i][1], owned.getFloat(i * 12 + 4), 0f, "pos.y v=" + i);
|
||||
assertEquals(verts[i][2], owned.getFloat(i * 12 + 8), 0f, "pos.z v=" + i);
|
||||
}
|
||||
MemoryUtilities.memFree(owned);
|
||||
}
|
||||
|
||||
@Test
|
||||
void quadOffsetDraw_compileAndExecute_doesNotPauseRecordingForRestOfList() {
|
||||
final int vao = setupPosColorVao(defaultQuadVbo());
|
||||
final int quadEbo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2, 3);
|
||||
final int triEbo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2);
|
||||
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, quadEbo);
|
||||
GLStateManager.glDrawElements(GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
assertTrue(DisplayListManager.isRecording(), "recording must resume after a GL_QUADS offset draw");
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, triEbo);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
assertEquals(2, allBatchedCmds(list).size(), "both the QUADS draw and the following triangle draw must be recorded");
|
||||
}
|
||||
|
||||
private enum MalformedClientDraw { OVERSIZED_COUNT, INDEX_PAST_VERTEX_BUFFER }
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@EnumSource(MalformedClientDraw.class)
|
||||
void malformedClientDraw_skippedAndRecordingSurvives(MalformedClientDraw scenario) {
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
switch (scenario) {
|
||||
case OVERSIZED_COUNT -> {
|
||||
GLStateManager.glBindVertexArray(setupPosColorVao(defaultQuadVbo()));
|
||||
final ByteBuffer idx = BufferUtils.createByteBuffer(3 * 2).order(ByteOrder.nativeOrder());
|
||||
idx.putShort((short) 0).putShort((short) 1).putShort((short) 2).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 100, GL11.GL_UNSIGNED_SHORT, idx);
|
||||
}
|
||||
case INDEX_PAST_VERTEX_BUFFER -> {
|
||||
final int vbo = uploadPosColorVbo(new float[]{0f, 0f, 1f, 0f}, new byte[][]{{1, 1, 1, 1}, {2, 2, 2, 2}});
|
||||
GLStateManager.glBindVertexArray(setupPosColorVao(vbo));
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(4);
|
||||
idx.put(new int[]{0, 1, 2, 3}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}
|
||||
}
|
||||
assertTrue(DisplayListManager.isRecording(), "recording survives a skipped malformed draw");
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
assertNull(firstBatchedCmd(list), "malformed draw must be skipped, not captured or over-read");
|
||||
}
|
||||
|
||||
private int recordClientIndexDraw(int listMode, Runnable draw) {
|
||||
final int vao = setupPosColorVao(defaultQuadVbo());
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, listMode);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
draw.run();
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
return list;
|
||||
}
|
||||
|
||||
private static void assertUintIndices(ByteBuffer ib, int... expected) {
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.mixins.early.angelica;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -20,6 +21,7 @@ public class MixinMinecraft_FrameHook {
|
||||
|
||||
@Inject(method = "runGameLoop", at = @At("HEAD"))
|
||||
private void angelica$onFrameBegin(CallbackInfo ci) {
|
||||
DisplayListManager.abortIfLeaked();
|
||||
RENDER_BACKEND.onFrameBegin();
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user