mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-10-26 14:24:30 +00:00
Give each GL drawable its own GLSM context (#2142)
This commit is contained in:
1 parent
7c28a69bfe
commit
a8c29fa3f9
67 files changed
+1817
-1009
No files matched your search
@@ -117,6 +117,7 @@ public class CompatUniformManager {
|
||||
|
||||
private static final class ProgramUniforms {
|
||||
final int[] locs;
|
||||
GLContextState owner;
|
||||
int lastMvGen = -1, lastMvLinearGen = -1, lastProjGen = -1, lastTexMatGen = -1;
|
||||
int lastFragmentGen = -1, lastLightingGen = -1, lastClipPlaneGen = -1;
|
||||
ProgramUniforms(int[] locs) { this.locs = locs; }
|
||||
@@ -161,7 +162,13 @@ public class CompatUniformManager {
|
||||
}
|
||||
|
||||
if (hasAny) {
|
||||
programUniforms.put(program, new ProgramUniforms(locs));
|
||||
final ProgramUniforms uniforms = new ProgramUniforms(locs);
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
programUniforms.put(program, uniforms);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
GLStateManager.LOGGER.debug("CompatUniformManager: program {} has compat uniforms", program);
|
||||
}
|
||||
}
|
||||
@@ -171,16 +178,21 @@ public class CompatUniformManager {
|
||||
*
|
||||
* @return Whether the program needs emulation (FFP or Iris Program)
|
||||
*/
|
||||
public static boolean refreshCompatUniforms(int program) {
|
||||
public static boolean refreshCompatUniforms(int program, GLContextState glCtx) {
|
||||
final ProgramUniforms pu = programUniforms.get(program);
|
||||
if (pu == null) return false;
|
||||
final int[] locs = pu.locs;
|
||||
if (pu.owner != glCtx) {
|
||||
pu.owner = glCtx;
|
||||
pu.lastMvGen = pu.lastMvLinearGen = pu.lastProjGen = pu.lastTexMatGen = -1;
|
||||
pu.lastFragmentGen = pu.lastLightingGen = pu.lastClipPlaneGen = -1;
|
||||
}
|
||||
|
||||
// Matrix uniforms — skip if this program's storage already holds the current generation
|
||||
final int mvGen = GLStateManager.getMvGeneration();
|
||||
final int mvLinearGen = GLStateManager.getMvLinearGeneration();
|
||||
final int projGen = GLStateManager.getProjGeneration();
|
||||
final int texMatGen = GLStateManager.getTexMatrixGeneration();
|
||||
final int mvGen = glCtx.mvGeneration;
|
||||
final int mvLinearGen = glCtx.mvLinearGeneration;
|
||||
final int projGen = glCtx.projGeneration;
|
||||
final int texMatGen = glCtx.texMatrixGeneration;
|
||||
final boolean mvChanged = mvGen != pu.lastMvGen;
|
||||
final boolean mvLinearChanged = mvLinearGen != pu.lastMvLinearGen;
|
||||
final boolean projChanged = projGen != pu.lastProjGen;
|
||||
@@ -194,14 +206,14 @@ public class CompatUniformManager {
|
||||
}
|
||||
|
||||
// Fragment-category uniforms (fog, alpha) — skip if generation unchanged
|
||||
final int fragGen = GLStateManager.getFragmentGeneration();
|
||||
final int fragGen = glCtx.fragmentGeneration;
|
||||
if (fragGen != pu.lastFragmentGen) {
|
||||
pu.lastFragmentGen = fragGen;
|
||||
uploadFragmentUniforms(locs);
|
||||
}
|
||||
|
||||
// Lighting-derived uniforms (scene color, light sources, material)
|
||||
final int litGen = GLStateManager.getLightingGeneration();
|
||||
final int litGen = glCtx.lightingGeneration;
|
||||
if (litGen != pu.lastLightingGen) {
|
||||
pu.lastLightingGen = litGen;
|
||||
if (locs[LOC_SCENE_COLOR] != -1) uploadSceneColor(locs);
|
||||
@@ -211,7 +223,7 @@ public class CompatUniformManager {
|
||||
|
||||
// Clip plane equations + enabled bool — uploaded when enable state or equations change
|
||||
if (locs[LOC_CLIP_PLANES] != -1 || locs[LOC_CLIP_PLANES_ENABLED] != -1) {
|
||||
final int cpGen = GLStateManager.getClipPlaneGeneration();
|
||||
final int cpGen = glCtx.clipPlaneGeneration;
|
||||
if (cpGen != pu.lastClipPlaneGen) {
|
||||
pu.lastClipPlaneGen = cpGen;
|
||||
uploadClipPlanes(locs);
|
||||
@@ -438,7 +450,12 @@ public class CompatUniformManager {
|
||||
}
|
||||
|
||||
public static void onDeleteProgram(int program) {
|
||||
programUniforms.remove(program);
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
programUniforms.remove(program);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasProgram(int program) {
|
||||
|
||||
@@ -59,7 +59,7 @@ public class DisplayListIDAllocator {
|
||||
numSetElements = 1;
|
||||
}
|
||||
|
||||
public int allocRange(int count) {
|
||||
public synchronized int allocRange(int count) {
|
||||
if (count <= 0) throw new IllegalArgumentException("count must be > 0");
|
||||
if (count == 1) return allocSingle();
|
||||
return allocMultiple(count);
|
||||
@@ -132,7 +132,7 @@ public class DisplayListIDAllocator {
|
||||
return numElements;
|
||||
}
|
||||
|
||||
public void free(int id) {
|
||||
public synchronized void free(int id) {
|
||||
if (id <= 0) return; // ID 0 is reserved
|
||||
int idx = id / 32;
|
||||
if (idx >= numElements) return;
|
||||
@@ -147,7 +147,7 @@ public class DisplayListIDAllocator {
|
||||
}
|
||||
}
|
||||
|
||||
public void freeRange(int base, int count) {
|
||||
public synchronized void freeRange(int base, int count) {
|
||||
if (count <= 0 || base <= 0) return;
|
||||
|
||||
int startIdx = base / 32;
|
||||
@@ -166,7 +166,7 @@ public class DisplayListIDAllocator {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAllocated(int id) {
|
||||
public synchronized boolean isAllocated(int id) {
|
||||
if (id <= 0) return false;
|
||||
int idx = id / 32;
|
||||
if (idx >= numSetElements) return false;
|
||||
|
||||
@@ -144,7 +144,7 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
public static String describeCurrentCompilation() {
|
||||
if (currentRecorder == null) {
|
||||
if (!isRecording()) {
|
||||
return "(not compiling)";
|
||||
}
|
||||
final String mode = glListMode == GL11.GL_COMPILE_AND_EXECUTE ? "GL_COMPILE_AND_EXECUTE" : "GL_COMPILE";
|
||||
@@ -153,16 +153,20 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
public static boolean isCompileAndExecute() {
|
||||
return glListMode == GL11.GL_COMPILE_AND_EXECUTE;
|
||||
return isRecording() && glListMode == GL11.GL_COMPILE_AND_EXECUTE;
|
||||
}
|
||||
|
||||
public static CommandRecorder pauseRecording() {
|
||||
if (Thread.currentThread() != recordingThread) return null;
|
||||
final CommandRecorder r = currentRecorder;
|
||||
currentRecorder = null;
|
||||
return r;
|
||||
}
|
||||
|
||||
public static void resumeRecording(CommandRecorder r) {
|
||||
if (Thread.currentThread() != recordingThread) {
|
||||
throw new IllegalStateException("Cannot resume another thread's display list compilation");
|
||||
}
|
||||
currentRecorder = r;
|
||||
}
|
||||
|
||||
@@ -229,15 +233,15 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
public static int getRecordingListId() {
|
||||
return glListId;
|
||||
return Thread.currentThread() == recordingThread ? glListId : -1;
|
||||
}
|
||||
|
||||
public static int getListMode() {
|
||||
return glListMode;
|
||||
return Thread.currentThread() == recordingThread ? glListMode : 0;
|
||||
}
|
||||
|
||||
public static void trackDrawRangeSource(String source) {
|
||||
if (drawRangeSources != null) {
|
||||
if (Thread.currentThread() == recordingThread && drawRangeSources != null) {
|
||||
drawRangeSources.add(source);
|
||||
}
|
||||
}
|
||||
@@ -761,8 +765,11 @@ public class DisplayListManager {
|
||||
* @return true if the display list exists, false otherwise
|
||||
*/
|
||||
public static boolean displayListExists(int list) {
|
||||
if (displayListCache.containsKey(list)) {
|
||||
return true;
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
if (displayListCache.containsKey(list)) return true;
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
// Check VBOManager for GTNHLib compatibility (negative IDs)
|
||||
if (list < -1) {
|
||||
@@ -778,7 +785,12 @@ public class DisplayListManager {
|
||||
* @return The CompiledDisplayList, or null if not found
|
||||
*/
|
||||
public static CompiledDisplayList getDisplayList(int list) {
|
||||
return displayListCache.get(list);
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
return displayListCache.get(list);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -787,73 +799,100 @@ public class DisplayListManager {
|
||||
* GL_COMPILE: Commands are recorded only (not executed), GLSM cache unchanged
|
||||
* GL_COMPILE_AND_EXECUTE: Commands are recorded AND executed (GLSM cache updated)
|
||||
*/
|
||||
public static void glNewList(int list, int mode) {
|
||||
// Handle nested compilation - push current context onto stack
|
||||
public static synchronized void glNewList(int list, int mode) {
|
||||
final Thread recorder = recordingThread;
|
||||
if (recorder != null && recorder != Thread.currentThread()) {
|
||||
throw new IllegalStateException("glNewList(" + list + ") on " + Thread.currentThread().getName() + " while " + recorder.getName() + " is compiling a display list");
|
||||
}
|
||||
final boolean isNested = glListMode > 0;
|
||||
final int captureDepth = TessellatorManager.getDirectCaptureDepth();
|
||||
boolean pushed = false;
|
||||
if (!isNested) recordingThread = Thread.currentThread();
|
||||
try {
|
||||
if (isNested) {
|
||||
flushAll();
|
||||
final CompilationContext parentContext = new CompilationContext(
|
||||
glListId, glListMode, currentRecorder, accumulatedDraws, transformCallback,
|
||||
compilationStackTrace, recordedStatePushes, pendingTransformOps, multMatrixSources, drawRangeSources
|
||||
);
|
||||
compilationStack.push(parentContext);
|
||||
pushed = true;
|
||||
}
|
||||
|
||||
if (isNested) {
|
||||
flushAll();
|
||||
// Nested display list compilation violates OpenGL spec, but some of our optimizations require it
|
||||
// Save current compilation context and start fresh for nested list
|
||||
final CompilationContext parentContext = new CompilationContext(
|
||||
glListId, glListMode, currentRecorder, accumulatedDraws, transformCallback,
|
||||
compilationStackTrace, recordedStatePushes, pendingTransformOps, multMatrixSources, drawRangeSources
|
||||
);
|
||||
compilationStack.push(parentContext);
|
||||
glListId = list;
|
||||
glListMode = mode;
|
||||
currentRecorder = new CommandRecorder();
|
||||
accumulatedDraws = new ArrayList<>(8);
|
||||
transformCallback = new DisplayListCallback();
|
||||
recordedStatePushes = 0;
|
||||
compilationStackTrace = SystemProperties.LOG_DISPLAY_LIST_COMPILATION ? Thread.currentThread().getStackTrace() : null;
|
||||
|
||||
if (SystemProperties.LOG_DISPLAY_LIST_COMPILATION) {
|
||||
pendingTransformOps = new ArrayList<>();
|
||||
multMatrixSources = new ArrayList<>();
|
||||
drawRangeSources = new ArrayList<>();
|
||||
} else {
|
||||
pendingTransformOps = null;
|
||||
multMatrixSources = null;
|
||||
drawRangeSources = null;
|
||||
}
|
||||
|
||||
TessellatorManager.startCapturingDirect(transformCallback);
|
||||
} catch (RuntimeException | Error failure) {
|
||||
try {
|
||||
if (TessellatorManager.getDirectCaptureDepth() > captureDepth) TessellatorManager.stopCapturingDirect();
|
||||
} catch (RuntimeException | Error cleanup) {
|
||||
failure.addSuppressed(cleanup);
|
||||
}
|
||||
try {
|
||||
if (pushed) {
|
||||
final CommandRecorder parent = compilationStack.peek().recorder();
|
||||
try {
|
||||
if (currentRecorder != null && currentRecorder != parent) currentRecorder.delete();
|
||||
} finally {
|
||||
popCompilationContext();
|
||||
}
|
||||
} else if (!isNested) {
|
||||
try {
|
||||
if (currentRecorder != null) currentRecorder.delete();
|
||||
} finally {
|
||||
resetCompilationState();
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException | Error cleanup) {
|
||||
failure.addSuppressed(cleanup);
|
||||
}
|
||||
throw failure;
|
||||
}
|
||||
|
||||
// Initialize fresh context for this (possibly nested) list
|
||||
glListId = list;
|
||||
glListMode = mode;
|
||||
recordingThread = Thread.currentThread(); // Track which thread is recording
|
||||
currentRecorder = new CommandRecorder(); // Create command recorder
|
||||
accumulatedDraws = new ArrayList<>(8); // Fewer draws than commands typically
|
||||
transformCallback = new DisplayListCallback();
|
||||
recordedStatePushes = 0;
|
||||
compilationStackTrace = SystemProperties.LOG_DISPLAY_LIST_COMPILATION ? Thread.currentThread().getStackTrace() : null;
|
||||
|
||||
// Initialize debug logging fields (only when logging enabled)
|
||||
if (SystemProperties.LOG_DISPLAY_LIST_COMPILATION) {
|
||||
pendingTransformOps = new ArrayList<>();
|
||||
multMatrixSources = new ArrayList<>();
|
||||
drawRangeSources = new ArrayList<>();
|
||||
} else {
|
||||
pendingTransformOps = null;
|
||||
multMatrixSources = null;
|
||||
drawRangeSources = null;
|
||||
}
|
||||
|
||||
TessellatorManager.startCapturingDirect(transformCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* End display list compilation and build optimized/unoptimized versions.
|
||||
*/
|
||||
public static void glEndList() {
|
||||
public static synchronized void glEndList() {
|
||||
final Thread recorder = recordingThread;
|
||||
if (recorder != null && recorder != Thread.currentThread()) {
|
||||
throw new IllegalStateException("glEndList on " + Thread.currentThread().getName() + " while " + recorder.getName() + " is compiling a display list");
|
||||
}
|
||||
if (glListMode == 0) {
|
||||
GLStateManager.warnOnce("endlist-outside", "glEndList called outside of a display list!");
|
||||
return;
|
||||
}
|
||||
if (Tracy.ENABLED) Tracy.beginZone(Z_GL_LIST_COMPILE);
|
||||
try {
|
||||
TessellatorManager.stopCapturingDirect();
|
||||
finishCurrentList();
|
||||
} finally {
|
||||
if (Tracy.ENABLED) Tracy.endZone();
|
||||
// Non-null here means finishCurrentList threw before transferring or releasing
|
||||
// ownership of the recorder.
|
||||
if (currentRecorder != null) {
|
||||
currentRecorder.delete();
|
||||
try {
|
||||
if (currentRecorder != null) currentRecorder.delete();
|
||||
} finally {
|
||||
popCompilationContext();
|
||||
}
|
||||
// Must always run, even when compilation fails - a leaked context leaves
|
||||
// isRecording() true forever and every guarded GL call throws afterward.
|
||||
popCompilationContext();
|
||||
}
|
||||
}
|
||||
|
||||
private static void finishCurrentList() {
|
||||
// Stop compiling mode (works for both root and nested lists now)
|
||||
TessellatorManager.stopCapturingDirect();
|
||||
|
||||
boolean unbalancedStatePush = false;
|
||||
while (recordPopStateIfPending()) unbalancedStatePush = true;
|
||||
if (unbalancedStatePush) {
|
||||
@@ -966,7 +1005,6 @@ public class DisplayListManager {
|
||||
/** Clear all root-level compilation state back to "not recording". */
|
||||
private static void resetCompilationState() {
|
||||
currentRecorder = null;
|
||||
recordingThread = null;
|
||||
accumulatedDraws = null;
|
||||
pendingDraw = null;
|
||||
transformCallback = null;
|
||||
@@ -977,9 +1015,13 @@ public class DisplayListManager {
|
||||
glListId = -1;
|
||||
glListMode = 0;
|
||||
recordedStatePushes = 0;
|
||||
recordingThread = null;
|
||||
}
|
||||
|
||||
public static void abortCompilation() {
|
||||
public static synchronized void abortCompilation() {
|
||||
if (recordingThread != null && recordingThread != Thread.currentThread()) {
|
||||
throw new IllegalStateException("Cannot abort another thread's display list compilation");
|
||||
}
|
||||
if (glListMode == 0 && compilationStack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -1007,7 +1049,7 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
public static void abortIfLeaked() {
|
||||
if (glListMode != 0 || !compilationStack.isEmpty()) {
|
||||
if (Thread.currentThread() == recordingThread && (glListMode != 0 || !compilationStack.isEmpty())) {
|
||||
GLStateManager.warnOnce("frame-leak", "Display list {} left open across a frame boundary - aborting to recover", glListId);
|
||||
abortCompilation();
|
||||
}
|
||||
@@ -1043,7 +1085,7 @@ public class DisplayListManager {
|
||||
* Execute a compiled display list.
|
||||
*/
|
||||
public static void glCallList(int list) {
|
||||
if (currentRecorder != null) {
|
||||
if (isRecording()) {
|
||||
recordCallList(list);
|
||||
|
||||
if (getListMode() != GL11.GL_COMPILE) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ViewportState;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -130,30 +129,32 @@ public class FeedbackManager {
|
||||
feedbackVertex(winX, winY, winZ, winW);
|
||||
}
|
||||
|
||||
private static int positionStride() {
|
||||
final int s = VAOManager.getAttribEffectiveStride(0);
|
||||
private static int positionStride(GLContextState glCtx) {
|
||||
final int s = glCtx.vaos.attribEffectiveStride(0);
|
||||
return (s != 0) ? s : 12;
|
||||
}
|
||||
|
||||
private static long positionOffset() {
|
||||
return VAOManager.getAttribOffset(0);
|
||||
private static long positionOffset(GLContextState glCtx) {
|
||||
return glCtx.vaos.attribOffset(0);
|
||||
}
|
||||
|
||||
public static void processDrawArrays(int mode, int first, int count) {
|
||||
computeMVP();
|
||||
final int stride = positionStride();
|
||||
final long posOffset = positionOffset();
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final int stride = positionStride(glCtx);
|
||||
final long posOffset = positionOffset(glCtx);
|
||||
final int byteCount = count * stride;
|
||||
ensureReadbackCapacity(byteCount);
|
||||
readbackBuf.clear().limit(byteCount);
|
||||
readPositionBuffer((long) first * stride + posOffset, readbackBuf);
|
||||
readPositionBuffer((long) first * stride + posOffset, readbackBuf, glCtx);
|
||||
|
||||
emitAllPrimitives(mode, count, i -> i, stride);
|
||||
}
|
||||
|
||||
public static void processDrawElements(int mode, int indexCount, int type, long eboOffset) {
|
||||
computeMVP();
|
||||
final int stride = positionStride();
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final int stride = positionStride(glCtx);
|
||||
|
||||
final int elementSize = GLTypes.sizeBytes(type);
|
||||
final int indexByteCount = indexCount * elementSize;
|
||||
@@ -169,12 +170,12 @@ public class FeedbackManager {
|
||||
if (idx > maxIdx) maxIdx = idx;
|
||||
}
|
||||
|
||||
final long posOffset = positionOffset();
|
||||
final long posOffset = positionOffset(glCtx);
|
||||
final int vertexCount = maxIdx - minIdx + 1;
|
||||
final int byteCount = vertexCount * stride;
|
||||
ensureReadbackCapacity(byteCount);
|
||||
readbackBuf.clear().limit(byteCount);
|
||||
readPositionBuffer((long) minIdx * stride + posOffset, readbackBuf);
|
||||
readPositionBuffer((long) minIdx * stride + posOffset, readbackBuf, glCtx);
|
||||
|
||||
final int base = minIdx;
|
||||
emitAllPrimitives(mode, indexCount, i -> ImmediateModeRecorder.readIndex(indexBuf, type, 0, i) - base, stride);
|
||||
@@ -208,7 +209,8 @@ public class FeedbackManager {
|
||||
|
||||
private static void processDrawElementsImpl(int mode, int count, IndexReader reader) {
|
||||
computeMVP();
|
||||
final int stride = positionStride();
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final int stride = positionStride(glCtx);
|
||||
|
||||
int minIdx = Integer.MAX_VALUE, maxIdx = Integer.MIN_VALUE;
|
||||
for (int i = 0; i < count; i++) {
|
||||
@@ -217,7 +219,7 @@ public class FeedbackManager {
|
||||
if (idx > maxIdx) maxIdx = idx;
|
||||
}
|
||||
|
||||
readVerticesFromVBO(minIdx, maxIdx, stride);
|
||||
readVerticesFromVBO(minIdx, maxIdx, stride, glCtx);
|
||||
final int base = minIdx;
|
||||
emitAllPrimitives(mode, count, i -> reader.read(i) - base, stride);
|
||||
}
|
||||
@@ -249,18 +251,18 @@ public class FeedbackManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static void readVerticesFromVBO(int minIdx, int maxIdx, int stride) {
|
||||
final long posOffset = positionOffset();
|
||||
private static void readVerticesFromVBO(int minIdx, int maxIdx, int stride, GLContextState glCtx) {
|
||||
final long posOffset = positionOffset(glCtx);
|
||||
final int vertexCount = maxIdx - minIdx + 1;
|
||||
final int byteCount = vertexCount * stride;
|
||||
ensureReadbackCapacity(byteCount);
|
||||
readbackBuf.clear().limit(byteCount);
|
||||
readPositionBuffer((long) minIdx * stride + posOffset, readbackBuf);
|
||||
readPositionBuffer((long) minIdx * stride + posOffset, readbackBuf, glCtx);
|
||||
}
|
||||
|
||||
private static void readPositionBuffer(long offset, ByteBuffer destination) {
|
||||
final int positionVbo = VAOManager.getAttribVBO(0);
|
||||
final int previousVbo = GLStateManager.getBoundVBO();
|
||||
private static void readPositionBuffer(long offset, ByteBuffer destination, GLContextState glCtx) {
|
||||
final int positionVbo = glCtx.vaos.attribVBO(0);
|
||||
final int previousVbo = glCtx.boundVBO;
|
||||
|
||||
if (positionVbo != 0 && positionVbo != previousVbo) {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, positionVbo);
|
||||
|
||||
@@ -3,8 +3,6 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.AlphaStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BlendStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
@@ -35,8 +33,14 @@ import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ImageUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.SamplerUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.Instancing;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.StreamingVaos;
|
||||
import org.joml.Matrix4d;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fStack;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
@@ -46,6 +50,7 @@ import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.DoubleBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -214,7 +219,6 @@ public class GLContextState {
|
||||
public int boundCopyReadBuffer;
|
||||
public int boundCopyWriteBuffer;
|
||||
public final Int2IntMap boundOtherBuffers = new Int2IntOpenHashMap();
|
||||
public final IntSet writeMappedBuffers = new IntOpenHashSet();
|
||||
public PixelUnpackState pixelUnpackState = PixelUnpackState.DEFAULT;
|
||||
public final int[] clientAttribSavedTextureUnit = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final int[] clientAttribSavedVertexFlags = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
@@ -227,6 +231,31 @@ public class GLContextState {
|
||||
public int drawFramebuffer = 0;
|
||||
public int readFramebuffer = 0;
|
||||
public int texGenGeneration;
|
||||
public int defaultVAO;
|
||||
public final VAOManager vaos = new VAOManager();
|
||||
public final StreamingVaos streamingVaos = new StreamingVaos();
|
||||
public int fontVao;
|
||||
public boolean lockBindCallback;
|
||||
public int foreignDrawDepth;
|
||||
public int internalGlDepth;
|
||||
public int internalDrawDepth;
|
||||
public int savedFilterTextureId;
|
||||
public boolean wideLineEmulationActive = false;
|
||||
public boolean lineStippleActive = false;
|
||||
public Instancing ffpInstancing = Instancing.NONE;
|
||||
public float lastBrightnessX;
|
||||
public float lastBrightnessY;
|
||||
public final Matrix4f multMatrix = new Matrix4f();
|
||||
public final Matrix4d conversionMatrix4d = new Matrix4d();
|
||||
public final Matrix4f conversionMatrix4f = new Matrix4f();
|
||||
public final Matrix4f gluMatrix = new Matrix4f();
|
||||
public final Matrix4f gluMatrix2 = new Matrix4f();
|
||||
public final Vector4f gluVec4 = new Vector4f();
|
||||
public final FloatBuffer gluBuffer = BufferUtils.createFloatBuffer(16);
|
||||
public final float[] texGenTempPlane = new float[4];
|
||||
public final IntBuffer shaderBuf = BufferUtils.createIntBuffer(8);
|
||||
public final FloatBuffer projectionMatrixBuffer = BufferUtils.createFloatBuffer(16);
|
||||
public final ShaderManager ffp = new ShaderManager();
|
||||
|
||||
GLContextState() {
|
||||
allStacksBuilder.addAll(textures.idStacks());
|
||||
@@ -242,11 +271,11 @@ public class GLContextState {
|
||||
for (int i = 0; i < lightDataStates.length; i++) {
|
||||
lightDataStates[i] = member(new LightStateStack(GL11.GL_LIGHT0 + i, StackIdAllocator.nextId()));
|
||||
}
|
||||
allStacksBuilder.add(ShaderManager.getNormalStack());
|
||||
allStacksBuilder.add(ShaderManager.getTexCoordStack());
|
||||
allStacksBuilder.add(ffp.normalStack);
|
||||
allStacksBuilder.add(ffp.texCoordStack);
|
||||
allStacks = allStacksBuilder.toArray(new IStateStack<?>[0]);
|
||||
|
||||
final int capacity = StackIdAllocator.capacity();
|
||||
final int capacity = StackIdAllocator.allocated();
|
||||
stackById = new CowStateStack<?>[capacity];
|
||||
depthsById = new CowDepths[capacity];
|
||||
restoreBitById = new int[capacity];
|
||||
@@ -257,6 +286,7 @@ public class GLContextState {
|
||||
final int id = s.stackId();
|
||||
stackById[id] = s;
|
||||
depthsById[id] = s.cowDepths();
|
||||
depthsById[id].owner = this;
|
||||
restoreBitById[id] = s.restoreBit();
|
||||
restoreUnitById[id] = s.restoreUnit();
|
||||
kindById[id] = CowDispatch.kindOf(s);
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -82,17 +82,17 @@ public final class QuadConverter {
|
||||
* @param first first vertex index (must be aligned to quad boundary, i.e. multiple of 4)
|
||||
* @param vertexCount number of vertices (must be multiple of 4)
|
||||
*/
|
||||
public static void drawQuadsAsTriangles(int first, int vertexCount) {
|
||||
drawSharedQuadEbo(first, vertexCount, 1, false);
|
||||
public static void drawQuadsAsTriangles(GLContextState glCtx, int first, int vertexCount) {
|
||||
drawSharedQuadEbo(glCtx, first, vertexCount, 1, false);
|
||||
}
|
||||
|
||||
public static void drawQuadsAsTrianglesInstanced(int first, int vertexCount, int primcount) {
|
||||
drawSharedQuadEbo(first, vertexCount, primcount, true);
|
||||
public static void drawQuadsAsTrianglesInstanced(GLContextState glCtx, int first, int vertexCount, int primcount) {
|
||||
drawSharedQuadEbo(glCtx, first, vertexCount, primcount, true);
|
||||
}
|
||||
|
||||
private static int misalignedDraws;
|
||||
|
||||
private static void drawSharedQuadEbo(int first, int vertexCount, int primcount, boolean instanced) {
|
||||
private static void drawSharedQuadEbo(GLContextState glCtx, int first, int vertexCount, int primcount, boolean instanced) {
|
||||
final int quadCount = vertexCount / 4;
|
||||
if ((first & 3) != 0) {
|
||||
misalignedDraws++;
|
||||
@@ -111,26 +111,21 @@ public final class QuadConverter {
|
||||
memPutInt(ptr + 20, base + 3);
|
||||
ptr += 24;
|
||||
}
|
||||
uploadAndDraw(dst, quadCount * 6, GL11.GL_UNSIGNED_INT, 4, primcount, instanced);
|
||||
uploadAndDraw(glCtx, dst, quadCount * 6, GL11.GL_UNSIGNED_INT, 4, primcount, instanced);
|
||||
return;
|
||||
}
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
GLStateManager.preDraw();
|
||||
final int prevEbo = GLStateManager.getBoundEBO();
|
||||
ensureCapacity(first / 4 + quadCount);
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||
// Index offset: first vertex / 4 quads * 6 indices * 4 bytes per int
|
||||
final long indexOffset = (long) (first / 4) * 6 * 4;
|
||||
if (instanced) {
|
||||
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset, primcount);
|
||||
} else {
|
||||
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset);
|
||||
}
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
GLStateManager.preDraw(glCtx, GL11.GL_TRIANGLES);
|
||||
final int prevEbo = glCtx.vaos.boundEBO;
|
||||
ensureCapacity(first / 4 + quadCount);
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||
// Index offset: first vertex / 4 quads * 6 indices * 4 bytes per int
|
||||
final long indexOffset = (long) (first / 4) * 6 * 4;
|
||||
if (instanced) {
|
||||
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset, primcount);
|
||||
} else {
|
||||
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, quadCount * 6, INDEX_TYPE, indexOffset);
|
||||
}
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,42 +138,37 @@ public final class QuadConverter {
|
||||
* @param indexType GL_UNSIGNED_INT or GL_UNSIGNED_SHORT
|
||||
* @param bytesPerIndex bytes per index element
|
||||
*/
|
||||
private static void uploadAndDraw(ByteBuffer dst, int triIndexCount, int indexType, int bytesPerIndex) {
|
||||
uploadAndDraw(dst, triIndexCount, indexType, bytesPerIndex, 1, false);
|
||||
private static void uploadAndDraw(GLContextState glCtx, ByteBuffer dst, int triIndexCount, int indexType, int bytesPerIndex) {
|
||||
uploadAndDraw(glCtx, dst, triIndexCount, indexType, bytesPerIndex, 1, false);
|
||||
}
|
||||
|
||||
private static void uploadAndDraw(ByteBuffer dst, int triIndexCount, int indexType, int bytesPerIndex, int primcount, boolean instanced) {
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
GLStateManager.preDraw();
|
||||
final int needed = triIndexCount * bytesPerIndex;
|
||||
final int prevEbo = GLStateManager.getBoundEBO();
|
||||
private static void uploadAndDraw(GLContextState glCtx, ByteBuffer dst, int triIndexCount, int indexType, int bytesPerIndex, int primcount, boolean instanced) {
|
||||
GLStateManager.preDraw(glCtx, GL11.GL_TRIANGLES);
|
||||
final int needed = triIndexCount * bytesPerIndex;
|
||||
final int prevEbo = glCtx.vaos.boundEBO;
|
||||
|
||||
if (scratchEboId == 0) {
|
||||
scratchEboId = RENDER_BACKEND.genBuffers();
|
||||
}
|
||||
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, scratchEboId);
|
||||
|
||||
if (needed > scratchEboCapacity) {
|
||||
// Power-of-2 growth -- allocate full capacity, upload actual data
|
||||
int newCap = Math.max(4096, scratchEboCapacity);
|
||||
while (newCap < needed) newCap *= 2;
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, newCap, GL15.GL_STREAM_DRAW);
|
||||
scratchEboCapacity = newCap;
|
||||
}
|
||||
RENDER_BACKEND.bufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, 0, dst);
|
||||
|
||||
if (instanced) {
|
||||
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L, primcount);
|
||||
} else {
|
||||
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L);
|
||||
}
|
||||
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
if (scratchEboId == 0) {
|
||||
scratchEboId = RENDER_BACKEND.genBuffers();
|
||||
}
|
||||
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, scratchEboId);
|
||||
|
||||
if (needed > scratchEboCapacity) {
|
||||
// Power-of-2 growth -- allocate full capacity, upload actual data
|
||||
int newCap = Math.max(4096, scratchEboCapacity);
|
||||
while (newCap < needed) newCap *= 2;
|
||||
RENDER_BACKEND.bufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, newCap, GL15.GL_STREAM_DRAW);
|
||||
scratchEboCapacity = newCap;
|
||||
}
|
||||
RENDER_BACKEND.bufferSubData(GL15.GL_ELEMENT_ARRAY_BUFFER, 0, dst);
|
||||
|
||||
if (instanced) {
|
||||
RENDER_BACKEND.drawElementsInstanced(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L, primcount);
|
||||
} else {
|
||||
RENDER_BACKEND.drawElements(GL11.GL_TRIANGLES, triIndexCount, indexType, 0L);
|
||||
}
|
||||
|
||||
RENDER_BACKEND.bindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
|
||||
memFree(dst);
|
||||
}
|
||||
|
||||
@@ -190,15 +180,15 @@ public final class QuadConverter {
|
||||
* @param type GL_UNSIGNED_INT, GL_UNSIGNED_SHORT, or GL_UNSIGNED_BYTE
|
||||
* @param offset byte offset into the currently bound EBO
|
||||
*/
|
||||
public static void drawQuadElementsAsTriangles(int indexCount, int type, long offset) {
|
||||
drawQuadElementsFromEbo(indexCount, type, offset, 1, false);
|
||||
public static void drawQuadElementsAsTriangles(GLContextState glCtx, int indexCount, int type, long offset) {
|
||||
drawQuadElementsFromEbo(glCtx, indexCount, type, offset, 1, false);
|
||||
}
|
||||
|
||||
public static void drawQuadElementsAsTrianglesInstanced(int indexCount, int type, long offset, int primcount) {
|
||||
drawQuadElementsFromEbo(indexCount, type, offset, primcount, true);
|
||||
public static void drawQuadElementsAsTrianglesInstanced(GLContextState glCtx, int indexCount, int type, long offset, int primcount) {
|
||||
drawQuadElementsFromEbo(glCtx, indexCount, type, offset, primcount, true);
|
||||
}
|
||||
|
||||
private static void drawQuadElementsFromEbo(int indexCount, int type, long offset, int primcount, boolean instanced) {
|
||||
private static void drawQuadElementsFromEbo(GLContextState glCtx, int indexCount, int type, long offset, int primcount, boolean instanced) {
|
||||
if (indexCount == 0) return;
|
||||
assert indexCount % 4 == 0 : "QuadConverter: indexCount must be multiple of 4";
|
||||
final int quadCount = indexCount / 4;
|
||||
@@ -213,13 +203,13 @@ public final class QuadConverter {
|
||||
triangulateQuads(memAddress0(src), type, memAddress0(dst), GL11.GL_UNSIGNED_INT, quadCount);
|
||||
|
||||
memFree(src);
|
||||
uploadAndDraw(dst, triIndexCount, GL11.GL_UNSIGNED_INT, 4, primcount, instanced);
|
||||
uploadAndDraw(glCtx, dst, triIndexCount, GL11.GL_UNSIGNED_INT, 4, primcount, instanced);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a client-side IntBuffer of quad indices to triangles.
|
||||
*/
|
||||
public static void drawQuadElementsAsTriangles(IntBuffer indices) {
|
||||
public static void drawQuadElementsAsTriangles(GLContextState glCtx, IntBuffer indices) {
|
||||
final int indexCount = indices.remaining();
|
||||
if (indexCount == 0) return;
|
||||
assert indexCount % 4 == 0;
|
||||
@@ -230,13 +220,13 @@ public final class QuadConverter {
|
||||
final long srcAddr = memAddress0(indices) + (long) indices.position() * 4;
|
||||
triangulateQuads(srcAddr, GL11.GL_UNSIGNED_INT, memAddress0(dst), GL11.GL_UNSIGNED_INT, quadCount);
|
||||
|
||||
uploadAndDraw(dst, triIndexCount, GL11.GL_UNSIGNED_INT, 4);
|
||||
uploadAndDraw(glCtx, dst, triIndexCount, GL11.GL_UNSIGNED_INT, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a client-side ShortBuffer of quad indices to triangles.
|
||||
*/
|
||||
public static void drawQuadElementsAsTriangles(ShortBuffer indices) {
|
||||
public static void drawQuadElementsAsTriangles(GLContextState glCtx, ShortBuffer indices) {
|
||||
final int indexCount = indices.remaining();
|
||||
if (indexCount == 0) return;
|
||||
assert indexCount % 4 == 0;
|
||||
@@ -247,13 +237,13 @@ public final class QuadConverter {
|
||||
final long srcAddr = memAddress0(indices) + (long) indices.position() * 2;
|
||||
triangulateQuads(srcAddr, GL11.GL_UNSIGNED_SHORT, memAddress0(dst), GL11.GL_UNSIGNED_SHORT, quadCount);
|
||||
|
||||
uploadAndDraw(dst, triIndexCount, GL11.GL_UNSIGNED_SHORT, 2);
|
||||
uploadAndDraw(glCtx, dst, triIndexCount, GL11.GL_UNSIGNED_SHORT, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a client-side ByteBuffer of quad indices (type-agnostic) to triangles.
|
||||
*/
|
||||
public static void drawQuadElementsAsTriangles(int count, int type, ByteBuffer indices) {
|
||||
public static void drawQuadElementsAsTriangles(GLContextState glCtx, int count, int type, ByteBuffer indices) {
|
||||
if (count == 0) return;
|
||||
assert count % 4 == 0;
|
||||
final int quadCount = count / 4;
|
||||
@@ -263,23 +253,7 @@ public final class QuadConverter {
|
||||
final ByteBuffer dst = memAlloc(triIndexCount * 4);
|
||||
triangulateQuads(srcAddr, type, memAddress0(dst), GL11.GL_UNSIGNED_INT, quadCount);
|
||||
|
||||
uploadAndDraw(dst, triIndexCount, GL11.GL_UNSIGNED_INT, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the shared EBO.
|
||||
*/
|
||||
public static void destroy() {
|
||||
if (eboId != 0) {
|
||||
GLStateManager.glDeleteBuffers(eboId);
|
||||
eboId = 0;
|
||||
maxQuads = 0;
|
||||
}
|
||||
if (scratchEboId != 0) {
|
||||
GLStateManager.glDeleteBuffers(scratchEboId);
|
||||
scratchEboId = 0;
|
||||
scratchEboCapacity = 0;
|
||||
}
|
||||
uploadAndDraw(glCtx, dst, triIndexCount, GL11.GL_UNSIGNED_INT, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -169,13 +169,13 @@ public class RenderSystem {
|
||||
|
||||
if (isGLES) {
|
||||
GLStateManager.LOGGER.info("GL ES context detected, enabling shader transformer.");
|
||||
ShaderManager.getInstance().enable();
|
||||
ShaderManager.enable();
|
||||
} else if (GLStateManager.capabilities.OpenGL32) {
|
||||
final int profileMask = RENDER_BACKEND.getInteger(GL32.GL_CONTEXT_PROFILE_MASK);
|
||||
if ((profileMask & GL32.GL_CONTEXT_CORE_PROFILE_BIT) != 0) {
|
||||
coreProfile = true;
|
||||
GLStateManager.LOGGER.info("GL 3.3 core profile detected, enabling FFP shader emulation.");
|
||||
ShaderManager.getInstance().enable();
|
||||
ShaderManager.enable();
|
||||
} else {
|
||||
throw new IllegalStateException("Non-core GL context (profile mask 0x" + Integer.toHexString(profileMask)
|
||||
+ "); FFP emulation requires a core profile and nothing would render. Context creation should have rejected this.");
|
||||
@@ -503,13 +503,12 @@ public class RenderSystem {
|
||||
dsaState.bindTextureToUnit(target, unit, texture);
|
||||
}
|
||||
|
||||
public static final FloatBuffer PROJECTION_MATRIX_BUFFER = BufferUtils.createFloatBuffer(16);
|
||||
|
||||
public static void setupProjectionMatrix(Matrix4f matrix) {
|
||||
final FloatBuffer projectionMatrixBuffer = GLStateManager.ctx().projectionMatrixBuffer;
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glPushMatrix();
|
||||
matrix.get(0, PROJECTION_MATRIX_BUFFER);
|
||||
GLStateManager.glLoadMatrix(PROJECTION_MATRIX_BUFFER);
|
||||
matrix.get(0, projectionMatrixBuffer);
|
||||
GLStateManager.glLoadMatrix(projectionMatrixBuffer);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.cel.api.util.NormI8;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
@@ -59,7 +60,6 @@ public final class FfpExtendedAttribs {
|
||||
|
||||
private static final HashMap<Key, ExtBuffer> cache = new HashMap<>();
|
||||
private static final Key lookup = new Key();
|
||||
private static int internalGlDepth = 0;
|
||||
|
||||
private static final HashMap<EboKey, Boolean> eboPatternCache = new HashMap<>();
|
||||
private static final EboKey eboLookup = new EboKey();
|
||||
@@ -79,15 +79,19 @@ public final class FfpExtendedAttribs {
|
||||
return cache.isEmpty() && eboPatternCache.isEmpty();
|
||||
}
|
||||
|
||||
private static int internalDrawDepth = 0;
|
||||
public static void beginInternalDraw() { GLStateManager.ctx().internalDrawDepth++; }
|
||||
|
||||
public static void beginInternalDraw() { internalDrawDepth++; }
|
||||
public static void endInternalDraw() {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
if (glCtx.internalDrawDepth > 0) glCtx.internalDrawDepth--;
|
||||
}
|
||||
|
||||
public static void endInternalDraw() { if (internalDrawDepth > 0) internalDrawDepth--; }
|
||||
private static void beginInternalGl() { GLStateManager.ctx().internalGlDepth++; }
|
||||
|
||||
private static void beginInternalGl() { internalGlDepth++; }
|
||||
|
||||
private static void endInternalGl() { if (internalGlDepth > 0) internalGlDepth--; }
|
||||
private static void endInternalGl() {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
if (glCtx.internalGlDepth > 0) glCtx.internalGlDepth--;
|
||||
}
|
||||
|
||||
private static boolean isNotConventionalTexturedArray(VAOManager.Attrib a) {
|
||||
return a == null || !a.enabled || a.genericPointer || a.vboId == 0 || a.size < 2;
|
||||
@@ -104,20 +108,20 @@ public final class FfpExtendedAttribs {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean maybeBind(int mode, int first, int count) {
|
||||
public static boolean maybeBind(GLContextState glCtx, int mode, int first, int count) {
|
||||
final ImmediateExtendedAttribHandler h = GLSMHooks.immediateExtendedHandler;
|
||||
if (h == null) return false;
|
||||
if (internalDrawDepth > 0) return false;
|
||||
if (glCtx.internalDrawDepth > 0) return false;
|
||||
final int extPrim = ImmediateExtendedAttribHandler.extPrimVerts(mode, count);
|
||||
if (extPrim == 0 || first < 0) return false;
|
||||
if (!h.wantsExtended()) return false;
|
||||
|
||||
final VAOManager.Attrib pos = VAOManager.get(POSITION_LOC);
|
||||
final VAOManager.Attrib uv = VAOManager.get(UV_LOC);
|
||||
final VAOManager.Attrib pos = glCtx.vaos.attrib(POSITION_LOC);
|
||||
final VAOManager.Attrib uv = glCtx.vaos.attrib(UV_LOC);
|
||||
if (isNotConventionalTexturedArray(pos) || isNotConventionalTexturedArray(uv)) return false;
|
||||
if (isStreaming(pos.vboId) || isStreaming(uv.vboId)) return false;
|
||||
|
||||
VAOManager.Attrib normal = extPrim == 3 ? VAOManager.get(NORMAL_LOC) : null;
|
||||
VAOManager.Attrib normal = extPrim == 3 ? glCtx.vaos.attrib(NORMAL_LOC) : null;
|
||||
if (normal != null && (!normal.enabled || normal.genericPointer || normal.vboId == 0 || normal.size < 3
|
||||
|| isStreaming(normal.vboId))) {
|
||||
normal = null;
|
||||
@@ -126,17 +130,17 @@ public final class FfpExtendedAttribs {
|
||||
return bindExt(h, mode, first, count, extPrim, pos, uv, normal);
|
||||
}
|
||||
|
||||
public static boolean maybeBindIndexed(int mode, int indexCount, int indexType, long indicesOffset) {
|
||||
public static boolean maybeBindIndexed(GLContextState glCtx, int mode, int indexCount, int indexType, long indicesOffset) {
|
||||
final ImmediateExtendedAttribHandler h = GLSMHooks.immediateExtendedHandler;
|
||||
if (h == null) return false;
|
||||
if (internalDrawDepth > 0) return false;
|
||||
if (glCtx.internalDrawDepth > 0) return false;
|
||||
if (mode != GL11.GL_TRIANGLES || indexCount <= 0 || indexCount % 6 != 0) return false;
|
||||
if (!h.wantsExtended()) return false;
|
||||
|
||||
final VAOManager.Attrib pos = VAOManager.get(POSITION_LOC);
|
||||
final VAOManager.Attrib uv = VAOManager.get(UV_LOC);
|
||||
final VAOManager.Attrib pos = glCtx.vaos.attrib(POSITION_LOC);
|
||||
final VAOManager.Attrib uv = glCtx.vaos.attrib(UV_LOC);
|
||||
if (isNotConventionalTexturedArray(pos) || isNotConventionalTexturedArray(uv)) return false;
|
||||
if (isStreaming(pos.vboId) || isStreaming(uv.vboId) || isStreaming(VAOManager.boundEBO)) return false;
|
||||
if (isStreaming(pos.vboId) || isStreaming(uv.vboId) || isStreaming(glCtx.vaos.boundEBO)) return false;
|
||||
|
||||
final int indexSize = indexTypeBytes(indexType);
|
||||
if (indexSize == 0) return false;
|
||||
@@ -144,7 +148,7 @@ public final class FfpExtendedAttribs {
|
||||
final int first = (int) (indicesOffset / indexSize) / 6 * 4;
|
||||
final int count = indexCount / 6 * 4;
|
||||
|
||||
if (!isQuadPatternEbo(VAOManager.boundEBO, indicesOffset, indexCount, indexType, indexSize, first)) return false;
|
||||
if (!isQuadPatternEbo(glCtx.vaos.boundEBO, indicesOffset, indexCount, indexType, indexSize, first)) return false;
|
||||
|
||||
// Build/bind as GL_QUADS (the real primitive): face-normal tangents, 4-vertex tiles, no per-vertex normal.
|
||||
return bindExt(h, GL11.GL_QUADS, first, count, 4, pos, uv, null);
|
||||
@@ -226,8 +230,8 @@ public final class FfpExtendedAttribs {
|
||||
GLStateManager.glVertexAttrib4f(ImmediateExtendedAttribHandler.LOC_TANGENT, 1.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
public static void onDeleteBuffer(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
public static void onDeleteBuffer(GLContextState glCtx, int vboId) {
|
||||
if (glCtx.internalGlDepth > 0 || vboId == 0) return;
|
||||
dropDerivedFrom(vboId);
|
||||
respecCounts.remove(vboId);
|
||||
respecFrames.remove(vboId);
|
||||
@@ -236,8 +240,8 @@ public final class FfpExtendedAttribs {
|
||||
persistentSources.remove(vboId);
|
||||
}
|
||||
|
||||
public static void onBufferRespecified(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
public static void onBufferRespecified(GLContextState glCtx, int vboId) {
|
||||
if (glCtx.internalGlDepth > 0 || vboId == 0) return;
|
||||
if (streamingSources.contains(vboId)) {
|
||||
respecFrames.put(vboId, frame);
|
||||
return;
|
||||
@@ -268,16 +272,15 @@ public final class FfpExtendedAttribs {
|
||||
respecFrames.put(vboId, frame);
|
||||
}
|
||||
|
||||
public static void markStreaming(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
private static void markStreaming(int vboId) {
|
||||
respecCounts.remove(vboId);
|
||||
respecRunStart.remove(vboId);
|
||||
respecFrames.put(vboId, frame);
|
||||
if (streamingSources.add(vboId)) dropDerivedFrom(vboId);
|
||||
}
|
||||
|
||||
public static void markPersistent(int vboId) {
|
||||
if (internalGlDepth > 0 || vboId == 0) return;
|
||||
public static void markPersistent(GLContextState glCtx, int vboId) {
|
||||
if (glCtx.internalGlDepth > 0 || vboId == 0) return;
|
||||
markStreaming(vboId);
|
||||
persistentSources.add(vboId);
|
||||
}
|
||||
|
||||
@@ -134,13 +134,13 @@ public final class FragmentKey {
|
||||
}
|
||||
|
||||
// Damage overlay
|
||||
if (GLStateManager.ffpInstancing.hasInstanceHead()) {
|
||||
if (glCtx.ffpInstancing.hasInstanceHead()) {
|
||||
global |= (1L << BIT_OVERLAY_INSTANCED);
|
||||
} else if (glCtx.overlayA != 0.0f) {
|
||||
global |= (1L << BIT_OVERLAY_ENABLED);
|
||||
}
|
||||
|
||||
if (GLStateManager.lineStippleActive) {
|
||||
if (glCtx.lineStippleActive) {
|
||||
global |= (1L << BIT_LINE_STIPPLE);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.IdentityHashMap;
|
||||
|
||||
|
||||
@@ -271,25 +270,6 @@ public class ShaderCache {
|
||||
}
|
||||
}
|
||||
|
||||
/** Destroy all cached programs and clear caches. */
|
||||
public void destroy() {
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
if (programs[i] != null) {
|
||||
programs[i].destroy();
|
||||
}
|
||||
}
|
||||
Arrays.fill(keys, 0);
|
||||
Arrays.fill(programs, null);
|
||||
Arrays.fill(fragSources, null);
|
||||
count = 0;
|
||||
uniqueFragCount = 0;
|
||||
vertexSourceCache.clear();
|
||||
geometrySourceCache.clear();
|
||||
lastProgram = null;
|
||||
lastVK = Long.MIN_VALUE;
|
||||
GLStateManager.LOGGER.debug("FFP shader cache cleared");
|
||||
}
|
||||
|
||||
public int getProgramCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.QuadConverter;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StackIdAllocator;
|
||||
@@ -28,45 +27,42 @@ import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACK
|
||||
*/
|
||||
public final class ShaderManager {
|
||||
|
||||
private static final class Holder {
|
||||
|
||||
static final ShaderManager INSTANCE = new ShaderManager();
|
||||
}
|
||||
|
||||
private final ShaderCache cache = new ShaderCache();
|
||||
private static final ShaderCache cache = new ShaderCache();
|
||||
private final Uniforms uniforms = new Uniforms();
|
||||
|
||||
@Getter
|
||||
private boolean active = false;
|
||||
private boolean active;
|
||||
private Program currentProgram = null;
|
||||
private int lastBoundProgramId = -1;
|
||||
private int lastBoundProgramId;
|
||||
private long currentVertexKeyPacked = Long.MIN_VALUE;
|
||||
private final long[] currentFKScratch = new long[FragmentKey.MAX_UNITS];
|
||||
private final long[] currentFKPacked = new long[FragmentKey.MAX_UNITS];
|
||||
private int currentFKLen = 0;
|
||||
|
||||
@Getter private static final Vector3f currentNormal = new Vector3f(0.0f, 0.0f, 1.0f);
|
||||
private static final Vector4f[] currentTexCoords = {
|
||||
public final Vector3f currentNormal = new Vector3f(0.0f, 0.0f, 1.0f);
|
||||
public final Vector4f[] currentTexCoords = {
|
||||
new Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
new Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
new Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
new Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||
};
|
||||
@Getter private static final Vec3fStack normalStack = new Vec3fStack(currentNormal, StackIdAllocator.nextStaticId());
|
||||
@Getter private static final Vec4fStack texCoordStack = new Vec4fStack(currentTexCoords[0], StackIdAllocator.nextStaticId());
|
||||
@Getter private static int normalGeneration;
|
||||
@Getter private static int texCoordGeneration;
|
||||
public final Vec3fStack normalStack = new Vec3fStack(currentNormal, StackIdAllocator.nextId());
|
||||
public final Vec4fStack texCoordStack = new Vec4fStack(currentTexCoords[0], StackIdAllocator.nextId());
|
||||
public int normalGeneration;
|
||||
public int texCoordGeneration;
|
||||
|
||||
private int preDrawCalls;
|
||||
private int lastFramePreDrawCalls;
|
||||
|
||||
public static long variantSwitches;
|
||||
|
||||
public static Vector4f getCurrentTexCoord() { return currentTexCoords[0]; }
|
||||
public static Vector4f getCurrentTexCoord(int unit) { return currentTexCoords[unit]; }
|
||||
@Getter private boolean enabled = false;
|
||||
public static Vector3f getCurrentNormal() { return GLStateManager.ctx().ffp.currentNormal; }
|
||||
public static Vector4f getCurrentTexCoord() { return GLStateManager.ctx().ffp.currentTexCoords[0]; }
|
||||
public static Vec3fStack getNormalStack() { return GLStateManager.ctx().ffp.normalStack; }
|
||||
public static Vec4fStack getTexCoordStack() { return GLStateManager.ctx().ffp.texCoordStack; }
|
||||
@Getter private static boolean enabled = false;
|
||||
|
||||
private ShaderManager() {
|
||||
static {
|
||||
cache.setDumpDir(SystemProperties.shaderDumpDir("ffp"));
|
||||
VertexFormat.registerSetupBufferStateOverride((format, offset) -> {
|
||||
VAOManager.setCurrentVertexFlags(format.getVertexFlags());
|
||||
@@ -74,18 +70,20 @@ public final class ShaderManager {
|
||||
});
|
||||
}
|
||||
|
||||
public static ShaderManager getInstance() {
|
||||
return Holder.INSTANCE;
|
||||
public ShaderManager() {
|
||||
active = enabled;
|
||||
lastBoundProgramId = enabled ? 0 : -1;
|
||||
}
|
||||
|
||||
public void enable() {
|
||||
public static ShaderManager getInstance() {
|
||||
return GLStateManager.ctx().ffp;
|
||||
}
|
||||
|
||||
public static void enable() {
|
||||
warmUp();
|
||||
enabled = true;
|
||||
|
||||
if (GLStateManager.getActiveProgram() == 0) {
|
||||
activate();
|
||||
}
|
||||
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
if (glCtx.activeProgram == 0) glCtx.ffp.activate();
|
||||
GLStateManager.LOGGER.info("FFP shader emulation enabled");
|
||||
}
|
||||
|
||||
@@ -112,7 +110,7 @@ public final class ShaderManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
public static void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
@@ -121,10 +119,11 @@ public final class ShaderManager {
|
||||
lastBoundProgramId = GLStateManager.getActiveProgram();
|
||||
}
|
||||
|
||||
public static void invalidateBoundProgram() {
|
||||
final ShaderManager sm = Holder.INSTANCE;
|
||||
sm.lastBoundProgramId = -1;
|
||||
sm.currentProgram = null;
|
||||
public void invalidateProgram() {
|
||||
lastBoundProgramId = -1;
|
||||
currentProgram = null;
|
||||
currentVertexKeyPacked = Long.MIN_VALUE;
|
||||
uniforms.invalidateBinding();
|
||||
}
|
||||
|
||||
public void deactivate() {
|
||||
@@ -136,30 +135,29 @@ public final class ShaderManager {
|
||||
GLStateManager.forceAttribDefaultsDirty();
|
||||
}
|
||||
|
||||
public void preDraw() {
|
||||
public void preDraw(GLContextState glCtx) {
|
||||
GLSMHooks.resolvePendingProgram();
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null) bh.flushDeferredBlend();
|
||||
|
||||
// Handle FFP & Iris uniforms
|
||||
final int currentProgramId = GLStateManager.getActiveProgram();
|
||||
final int currentProgramId = glCtx.activeProgram;
|
||||
if (currentProgramId != 0) {
|
||||
if (!CompatUniformManager.refreshCompatUniforms(currentProgramId)) {
|
||||
if (!CompatUniformManager.refreshCompatUniforms(currentProgramId, glCtx)) {
|
||||
return; // Don't emulate FFP on non-iris core shaders
|
||||
}
|
||||
}
|
||||
|
||||
final int vertexFlags = VAOManager.getCurrentVertexFlags();
|
||||
final int vertexFlags = glCtx.vaos.getVertexFlags();
|
||||
final boolean hasColor = (vertexFlags & VertexFlags.COLOR_BIT) != 0;
|
||||
final boolean hasNormal = (vertexFlags & VertexFlags.NORMAL_BIT) != 0;
|
||||
final boolean hasTexCoord = (vertexFlags & VertexFlags.TEXTURE_BIT) != 0;
|
||||
final boolean hasLightmap = (vertexFlags & VertexFlags.BRIGHTNESS_BIT) != 0;
|
||||
GLStateManager.flushDeferredVertexAttribs(hasColor, hasNormal, hasTexCoord, hasLightmap);
|
||||
GLStateManager.flushDeferredVertexAttribs(glCtx, hasColor, hasNormal, hasTexCoord, hasLightmap);
|
||||
|
||||
if (!active) return;
|
||||
|
||||
preDrawCalls++;
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final int fkLen = FragmentKey.packFromState(currentFKScratch, glCtx);
|
||||
final int fragMask = FragmentKey.unitMaskFromPacked(currentFKScratch, fkLen);
|
||||
final long vkPacked = VertexKey.packFromState(hasColor, hasNormal, hasTexCoord, hasLightmap, fragMask, glCtx);
|
||||
@@ -191,44 +189,31 @@ public final class ShaderManager {
|
||||
}
|
||||
|
||||
public static void endFrame() {
|
||||
final ShaderManager sm = Holder.INSTANCE;
|
||||
final ShaderManager sm = GLStateManager.ctx().ffp;
|
||||
sm.lastFramePreDrawCalls = sm.preDrawCalls;
|
||||
sm.preDrawCalls = 0;
|
||||
sm.uniforms.endFrame();
|
||||
FfpExtendedAttribs.endFrame();
|
||||
}
|
||||
|
||||
public static void setCurrentNormal(float x, float y, float z) {
|
||||
public void setNormal(float x, float y, float z) {
|
||||
normalStack.beforeModify();
|
||||
currentNormal.set(x, y, z);
|
||||
normalGeneration++;
|
||||
}
|
||||
|
||||
public static void setCurrentTexCoord(float s, float t, float r, float q) {
|
||||
public void setTexCoord(float s, float t, float r, float q) {
|
||||
texCoordStack.beforeModify();
|
||||
currentTexCoords[0].set(s, t, r, q);
|
||||
texCoordGeneration++;
|
||||
}
|
||||
|
||||
public static void setCurrentTexCoord(int unit, float s, float t, float r, float q) {
|
||||
public void setTexCoord(int unit, float s, float t, float r, float q) {
|
||||
if (unit == 0) texCoordStack.beforeModify();
|
||||
currentTexCoords[unit].set(s, t, r, q);
|
||||
texCoordGeneration++;
|
||||
}
|
||||
|
||||
public static void bumpNormalGeneration() { normalGeneration++; }
|
||||
public static void bumpTexCoordGeneration() { texCoordGeneration++; }
|
||||
|
||||
public void destroy() {
|
||||
cache.destroy();
|
||||
uniforms.destroy();
|
||||
final GLSMInitConfig config = GLStateManager.getInitConfig();
|
||||
if (config != null && config.getStreamingDrawerDestroy() != null) config.getStreamingDrawerDestroy().run();
|
||||
QuadConverter.destroy();
|
||||
active = false;
|
||||
currentProgram = null;
|
||||
}
|
||||
|
||||
public String getDebugInfo() {
|
||||
return String.format("FFP: %d programs (%d vert, %d frag variants) | UBO: %d/%d draws wrote (mat %d, light %d, frag %d, misc %d)",
|
||||
cache.getProgramCount(), cache.getVertexVariantCount(), cache.getFragmentVariantCount(),
|
||||
|
||||
@@ -11,7 +11,6 @@ import com.gtnewhorizons.angelica.glsm.states.TexGenState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ViewportState;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.UniformRingBuffer;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import org.joml.Math;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
@@ -125,25 +124,26 @@ public class Uniforms {
|
||||
stagedColor++;
|
||||
dirty = true;
|
||||
}
|
||||
final int normG = ShaderManager.getNormalGeneration();
|
||||
final ShaderManager ffp = glCtx.ffp;
|
||||
final int normG = ffp.normalGeneration;
|
||||
if (normG != normalGen) {
|
||||
final Vector3f normal = ShaderManager.getCurrentNormal();
|
||||
final Vector3f normal = ffp.currentNormal;
|
||||
putVec3(FFPUniformBlock.CURRENT_NORMAL, normal.x, normal.y, normal.z);
|
||||
normalGen = normG;
|
||||
stagedNormal++;
|
||||
dirty = true;
|
||||
}
|
||||
final int tcG = ShaderManager.getTexCoordGeneration();
|
||||
final int tcG = ffp.texCoordGeneration;
|
||||
if (tcG != texCoordGen) {
|
||||
putVec4(FFPUniformBlock.CURRENT_TEX_COORD_0, ShaderManager.getCurrentTexCoord(0));
|
||||
putVec4(FFPUniformBlock.CURRENT_TEX_COORD_2, ShaderManager.getCurrentTexCoord(2));
|
||||
putVec4(FFPUniformBlock.CURRENT_TEX_COORD_3, ShaderManager.getCurrentTexCoord(3));
|
||||
putVec4(FFPUniformBlock.CURRENT_TEX_COORD_0, ffp.currentTexCoords[0]);
|
||||
putVec4(FFPUniformBlock.CURRENT_TEX_COORD_2, ffp.currentTexCoords[2]);
|
||||
putVec4(FFPUniformBlock.CURRENT_TEX_COORD_3, ffp.currentTexCoords[3]);
|
||||
texCoordGen = tcG;
|
||||
stagedTexCoord++;
|
||||
dirty = true;
|
||||
}
|
||||
final float brightX = GLSMConfig.lastBrightnessX;
|
||||
final float brightY = GLSMConfig.lastBrightnessY;
|
||||
final float brightX = glCtx.lastBrightnessX;
|
||||
final float brightY = glCtx.lastBrightnessY;
|
||||
if (brightX != lightmapX || brightY != lightmapY) {
|
||||
putFloat(FFPUniformBlock.CURRENT_LIGHTMAP_COORD, brightX);
|
||||
putFloat(FFPUniformBlock.CURRENT_LIGHTMAP_COORD + 4, brightY);
|
||||
@@ -446,6 +446,10 @@ public class Uniforms {
|
||||
stagedMisc = 0;
|
||||
}
|
||||
|
||||
void invalidateBinding() {
|
||||
bound = false;
|
||||
}
|
||||
|
||||
UniformRingBuffer getRing() {
|
||||
return ring;
|
||||
}
|
||||
|
||||
@@ -14,30 +14,34 @@ import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACK
|
||||
public final class VAOManager {
|
||||
public static final int MAX_ATTRIBS = 16;
|
||||
|
||||
public static final Int2ObjectOpenHashMap<VAOData> vaoMap = new Int2ObjectOpenHashMap<>();
|
||||
public final Int2ObjectOpenHashMap<VAOData> vaoMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
static final Int2ObjectOpenHashMap<VAOData> eboOwners = new Int2ObjectOpenHashMap<>();
|
||||
final Int2ObjectOpenHashMap<VAOData> eboOwners = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
private static VAOData current = null;
|
||||
private static int currentVertexFlags = 0;
|
||||
private static Attrib[] currentAttribs = null;
|
||||
public static int boundEBO = 0;
|
||||
private VAOData current = null;
|
||||
private int currentVertexFlags = 0;
|
||||
private Attrib[] currentAttribs = null;
|
||||
public int boundEBO = 0;
|
||||
|
||||
private static int clientSideEnabledCount = 0;
|
||||
private int clientSideEnabledCount = 0;
|
||||
|
||||
|
||||
public static void init(int defaultVAO) {
|
||||
public void init(int defaultVAO) {
|
||||
final VAOData data = new VAOData();
|
||||
current = data;
|
||||
currentAttribs = data.attribs;
|
||||
vaoMap.put(defaultVAO, data);
|
||||
}
|
||||
|
||||
public static void onBindEBO(int ebo) {
|
||||
public boolean isInitialized() {
|
||||
return current != null;
|
||||
}
|
||||
|
||||
public void onBindEBO(int ebo) {
|
||||
boundEBO = ebo;
|
||||
}
|
||||
|
||||
public static void onBindVertexArrayPre(int vaoId) {
|
||||
public void onBindVertexArrayPre(int vaoId) {
|
||||
// Save old VAO data
|
||||
if (current != null) {
|
||||
current.vertexFlags = currentVertexFlags;
|
||||
@@ -62,14 +66,14 @@ public final class VAOManager {
|
||||
clientSideEnabledCount = data.clientSideEnabledCount;
|
||||
}
|
||||
|
||||
public static void onDeleteVertexArray(int vaoId) {
|
||||
public void onDeleteVertexArray(int vaoId) {
|
||||
final VAOData data = vaoMap.remove(vaoId);
|
||||
if (data == null) return;
|
||||
removeEboOwner(data.ebo, data);
|
||||
if (data == current) current = null;
|
||||
}
|
||||
|
||||
public static void onDeleteBuffer(int buffer) {
|
||||
public void onDeleteBuffer(int buffer) {
|
||||
if (buffer == 0) return;
|
||||
VAOData owner = eboOwners.remove(buffer);
|
||||
while (owner != null) {
|
||||
@@ -80,12 +84,12 @@ public final class VAOManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static void addEboOwner(int ebo, VAOData data) {
|
||||
private void addEboOwner(int ebo, VAOData data) {
|
||||
if (ebo == 0) return;
|
||||
data.nextEboOwner = eboOwners.put(ebo, data);
|
||||
}
|
||||
|
||||
private static void removeEboOwner(int ebo, VAOData data) {
|
||||
private void removeEboOwner(int ebo, VAOData data) {
|
||||
if (ebo == 0) return;
|
||||
VAOData node = eboOwners.get(ebo);
|
||||
if (node == data) {
|
||||
@@ -105,10 +109,11 @@ public final class VAOManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void enableClientVertexFlag(int flag) { currentVertexFlags |= flag; }
|
||||
public static void disableClientVertexFlag(int flag) { currentVertexFlags &= ~flag; }
|
||||
public static int getCurrentVertexFlags() { return currentVertexFlags; }
|
||||
public static void setCurrentVertexFlags(int flags) { currentVertexFlags = flags; }
|
||||
public void enableClientVertexFlag(int flag) { currentVertexFlags |= flag; }
|
||||
public void disableClientVertexFlag(int flag) { currentVertexFlags &= ~flag; }
|
||||
public int getVertexFlags() { return currentVertexFlags; }
|
||||
public void setVertexFlags(int flags) { currentVertexFlags = flags; }
|
||||
public static void setCurrentVertexFlags(int flags) { GLStateManager.ctx().vaos.currentVertexFlags = flags; }
|
||||
|
||||
public static final class VAOData {
|
||||
public Attrib[] attribs;
|
||||
@@ -122,7 +127,7 @@ public final class VAOManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAttribute(int index, int size, int type, boolean normalized, int stride, long offset, int vboId) {
|
||||
public void setAttribute(int index, int size, int type, boolean normalized, int stride, long offset, int vboId) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return;
|
||||
final Attrib a = getAttrib(index);
|
||||
final boolean wasClient = a.clientPointer != null;
|
||||
@@ -139,7 +144,7 @@ public final class VAOManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAttribute(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
|
||||
public void setAttribute(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return;
|
||||
final Attrib a = getAttrib(index);
|
||||
final boolean wasClient = a.clientPointer != null;
|
||||
@@ -156,19 +161,19 @@ public final class VAOManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isGenericPointerEnabled(int index) {
|
||||
public boolean isGenericPointerEnabled(int index) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return false;
|
||||
final Attrib a = currentAttribs[index];
|
||||
return a != null && a.genericPointer && a.enabled;
|
||||
}
|
||||
|
||||
public static void markConventional(int index) {
|
||||
public void markConventional(int index) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return;
|
||||
final Attrib a = currentAttribs[index];
|
||||
if (a != null) a.genericPointer = false;
|
||||
}
|
||||
|
||||
public static void setEnabled(int index, boolean enabled) {
|
||||
public void setEnabled(int index, boolean enabled) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return;
|
||||
final Attrib a = getAttrib(index);
|
||||
if (a.clientPointer != null && a.enabled != enabled) {
|
||||
@@ -177,7 +182,7 @@ public final class VAOManager {
|
||||
a.enabled = enabled;
|
||||
}
|
||||
|
||||
public static void enableAttribute(int index) {
|
||||
public void enableAttribute(int index) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return;
|
||||
final Attrib a = getAttrib(index);
|
||||
if (a.clientPointer != null && !a.enabled) {
|
||||
@@ -186,7 +191,7 @@ public final class VAOManager {
|
||||
a.enabled = true;
|
||||
}
|
||||
|
||||
public static void disableAttribute(int index) {
|
||||
public void disableAttribute(int index) {
|
||||
if (index < 0 || index >= MAX_ATTRIBS) return;
|
||||
final Attrib a = getAttrib(index);
|
||||
if (a.clientPointer != null && a.enabled) {
|
||||
@@ -196,7 +201,7 @@ public final class VAOManager {
|
||||
}
|
||||
|
||||
// Lazily allocate each attrib. Most VAOs don't even have 4 attribs, let alone 16.
|
||||
private static Attrib getAttrib(int index) {
|
||||
private Attrib getAttrib(int index) {
|
||||
Attrib a = currentAttribs[index];
|
||||
if (a == null) {
|
||||
a = new Attrib();
|
||||
@@ -205,28 +210,32 @@ public final class VAOManager {
|
||||
return a;
|
||||
}
|
||||
|
||||
public static boolean isClientSideAttrib(int index) {
|
||||
public boolean isClientSideAttrib(int index) {
|
||||
final Attrib attrib = currentAttribs[index];
|
||||
return attrib != null && attrib.isClientSide();
|
||||
}
|
||||
|
||||
public static int getAttribEffectiveStride(int index) {
|
||||
public Attrib attrib(int index) {
|
||||
return currentAttribs[index];
|
||||
}
|
||||
|
||||
public int attribEffectiveStride(int index) {
|
||||
final Attrib attrib = currentAttribs[index];
|
||||
return attrib != null ? attrib.effectiveStride() : 0;
|
||||
}
|
||||
|
||||
public static long getAttribOffset(int index) {
|
||||
public long attribOffset(int index) {
|
||||
final Attrib attrib = currentAttribs[index];
|
||||
return attrib != null ? attrib.offset : 0;
|
||||
}
|
||||
|
||||
public static int getAttribVBO(int index) {
|
||||
public int attribVBO(int index) {
|
||||
final Attrib attrib = currentAttribs[index];
|
||||
return attrib != null ? attrib.vboId : 0;
|
||||
}
|
||||
|
||||
public static @Nullable Attrib get(int index) {
|
||||
return currentAttribs[index];
|
||||
return GLStateManager.ctx().vaos.attrib(index);
|
||||
}
|
||||
|
||||
|
||||
@@ -236,7 +245,7 @@ public final class VAOManager {
|
||||
* (i.e. as a client-side pointer). In core profile, such attribs are treated as null
|
||||
* offsets into a non-existent buffer, causing a native crash at draw time.
|
||||
*/
|
||||
public static boolean hasAnyClientSideEnabledAttrib() {
|
||||
public boolean hasAnyClientSideEnabledAttrib() {
|
||||
return clientSideEnabledCount > 0;
|
||||
}
|
||||
|
||||
@@ -244,15 +253,15 @@ public final class VAOManager {
|
||||
* If any enabled vertex attribute uses a client-side pointer (no VBO), upload all such
|
||||
* attribs into a shared stream VBO so the draw succeeds under core profile.
|
||||
*/
|
||||
private static int clientArraysVBO = 0;
|
||||
private static int clientArraysVBOCapacity = 0;
|
||||
private static final int[] clientArraysVBOOffsets = new int[VAOManager.MAX_ATTRIBS];
|
||||
private int clientArraysVBO = 0;
|
||||
private int clientArraysVBOCapacity = 0;
|
||||
private final int[] clientArraysVBOOffsets = new int[VAOManager.MAX_ATTRIBS];
|
||||
|
||||
public static void uploadClientArraysToVBO() {
|
||||
public void uploadClientArraysToVBO() {
|
||||
int totalBytes = 0;
|
||||
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
|
||||
clientArraysVBOOffsets[i] = -1;
|
||||
final VAOManager.Attrib a = VAOManager.get(i);
|
||||
final VAOManager.Attrib a = currentAttribs[i];
|
||||
if (a == null || !a.enabled || a.clientPointer == null) continue;
|
||||
clientArraysVBOOffsets[i] = totalBytes;
|
||||
totalBytes += a.clientPointer.remaining();
|
||||
@@ -275,7 +284,7 @@ public final class VAOManager {
|
||||
|
||||
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
|
||||
if (clientArraysVBOOffsets[i] < 0) continue;
|
||||
final VAOManager.Attrib a = VAOManager.get(i);
|
||||
final VAOManager.Attrib a = currentAttribs[i];
|
||||
RENDER_BACKEND.bufferSubData(GL15.GL_ARRAY_BUFFER, clientArraysVBOOffsets[i], a.clientPointer.duplicate());
|
||||
RENDER_BACKEND.vertexAttribPointer(i, a.size, a.type, a.normalized, a.stride, (long) clientArraysVBOOffsets[i]);
|
||||
}
|
||||
|
||||
@@ -238,15 +238,15 @@ public final class VertexKey {
|
||||
bits |= (1L << BIT_CLIP_PLANES);
|
||||
}
|
||||
|
||||
if (GLStateManager.wideLineEmulationActive) {
|
||||
if (glCtx.wideLineEmulationActive) {
|
||||
bits |= (1L << BIT_WIDE_LINE);
|
||||
}
|
||||
|
||||
if (GLStateManager.lineStippleActive) {
|
||||
if (glCtx.lineStippleActive) {
|
||||
bits |= (1L << BIT_LINE_STIPPLE);
|
||||
}
|
||||
|
||||
bits |= (long) GLStateManager.ffpInstancing.ordinal() << BIT_INSTANCING;
|
||||
bits |= (long) glCtx.ffpInstancing.ordinal() << BIT_INSTANCING;
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
@@ -41,10 +41,6 @@ public final class FrameHooks {
|
||||
BackendManager.shutdown();
|
||||
}
|
||||
|
||||
public static void bindSplashVao() {
|
||||
GLStateManager.glBindVertexArray(GLStateManager.glGenVertexArrays());
|
||||
}
|
||||
|
||||
public static void splashFinished() {
|
||||
GLStateManager.markSplashComplete("SplashProgress.finish");
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public final class GLSMConfig {
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
|
||||
// Lightmap (replaces OpenGlHelper.lastBrightnessX/Y)
|
||||
public static float lastBrightnessX;
|
||||
public static float lastBrightnessY;
|
||||
public final class GLSMConfig {
|
||||
|
||||
public static int packBrightness(float x, float y) {
|
||||
return ((int) y << 16) | ((int) x & 0xFFFF);
|
||||
}
|
||||
|
||||
public static int packedLastBrightness() {
|
||||
return packBrightness(lastBrightnessX, lastBrightnessY);
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
return packBrightness(glCtx.lastBrightnessX, glCtx.lastBrightnessY);
|
||||
}
|
||||
|
||||
public static boolean hudCacheOverride;
|
||||
|
||||
@@ -12,7 +12,6 @@ public final class GLSMInitConfig {
|
||||
@Getter private final boolean lwjglDebug;
|
||||
@Getter private final StreamingUploader.UploadStrategy streamingUploadStrategy;
|
||||
@Getter private final Consumer<DirectTessellator> directDrawer;
|
||||
@Getter private final Runnable streamingDrawerDestroy;
|
||||
@Getter private final int displayWidth;
|
||||
@Getter private final int displayHeight;
|
||||
@Getter private final Runnable postInitCallback;
|
||||
@@ -24,7 +23,6 @@ public final class GLSMInitConfig {
|
||||
this.streamingUploadStrategy = builder.streamingUploadStrategy;
|
||||
this.noErrorChecks = builder.noErrorChecks;
|
||||
this.directDrawer = builder.directDrawer;
|
||||
this.streamingDrawerDestroy = builder.streamingDrawerDestroy;
|
||||
this.displayWidth = builder.displayWidth;
|
||||
this.displayHeight = builder.displayHeight;
|
||||
this.postInitCallback = builder.postInitCallback;
|
||||
@@ -43,7 +41,6 @@ public final class GLSMInitConfig {
|
||||
private boolean lwjglDebug = SystemProperties.LWJGL_DEBUG;
|
||||
private StreamingUploader.UploadStrategy streamingUploadStrategy = StreamingUploader.UploadStrategy.BUFFER_DATA;
|
||||
private Consumer<DirectTessellator> directDrawer = TessellatorStreamingDrawer::drawDirect;
|
||||
private Runnable streamingDrawerDestroy = TessellatorStreamingDrawer::destroy;
|
||||
private int displayWidth = 0;
|
||||
private int displayHeight = 0;
|
||||
private Runnable postInitCallback = null;
|
||||
@@ -72,11 +69,6 @@ public final class GLSMInitConfig {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder streamingDrawerDestroy(Runnable streamingDrawerDestroy) {
|
||||
this.streamingDrawerDestroy = streamingDrawerDestroy;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder displaySize(int width, int height) {
|
||||
this.displayWidth = width;
|
||||
this.displayHeight = height;
|
||||
|
||||
@@ -11,22 +11,20 @@ public final class TextureUtilHooks {
|
||||
|
||||
private TextureUtilHooks() {}
|
||||
|
||||
private static int savedFilterTextureId;
|
||||
|
||||
public static void setMaxLevel(int mipmapLevels) {
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, mipmapLevels);
|
||||
}
|
||||
|
||||
public static void captureBoundFilterTexture() {
|
||||
savedFilterTextureId = GLStateManager.getBoundTextureForServerState();
|
||||
GLStateManager.ctx().savedFilterTextureId = GLStateManager.getBoundTextureForServerState();
|
||||
}
|
||||
|
||||
public static void clearSavedFilterTexture() {
|
||||
savedFilterTextureId = 0;
|
||||
GLStateManager.ctx().savedFilterTextureId = 0;
|
||||
}
|
||||
|
||||
public static void restoreFilter(int min, int mag) {
|
||||
final int id = savedFilterTextureId;
|
||||
final int id = GLStateManager.ctx().savedFilterTextureId;
|
||||
if (id != 0 && DisplayListManager.getRecordMode() == DisplayListManager.RecordMode.NONE) {
|
||||
RenderSystem.texParameteri(id, GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, min);
|
||||
RenderSystem.texParameteri(id, GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, mag);
|
||||
@@ -37,7 +35,7 @@ public final class TextureUtilHooks {
|
||||
}
|
||||
|
||||
public static void restoreAniso(float aniso) {
|
||||
final int id = savedFilterTextureId;
|
||||
final int id = GLStateManager.ctx().savedFilterTextureId;
|
||||
if (id != 0 && DisplayListManager.getRecordMode() == DisplayListManager.RecordMode.NONE) {
|
||||
RenderSystem.texParameterf(id, GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
|
||||
} else {
|
||||
|
||||
@@ -68,6 +68,7 @@ public final class AttribSnapshot {
|
||||
final AttribDesc[] out = new AttribDesc[VAOManager.MAX_ATTRIBS];
|
||||
final List<ByteBuffer> allocated = new ArrayList<>();
|
||||
final int prevVBO = GLStateManager.getBoundVBO();
|
||||
final VAOManager vaos = GLStateManager.ctx().vaos;
|
||||
boolean success = false;
|
||||
|
||||
// Pass 1 — unioned [start,end] per vboId. Hot path: one source VBO, zero allocations.
|
||||
@@ -78,7 +79,7 @@ public final class AttribSnapshot {
|
||||
|
||||
final long lastVtx = (long) firstVertex + vertexCount - 1;
|
||||
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
|
||||
final VAOManager.Attrib a = VAOManager.get(i);
|
||||
final VAOManager.Attrib a = vaos.attrib(i);
|
||||
if (a == null || !a.enabled || a.vboId == 0) continue;
|
||||
final int stride = a.effectiveStride();
|
||||
final long typeBytes = (long) a.size * GLTypes.sizeBytes(a.type);
|
||||
@@ -114,7 +115,7 @@ public final class AttribSnapshot {
|
||||
|
||||
try {
|
||||
for (int i = 0; i < VAOManager.MAX_ATTRIBS; i++) {
|
||||
final VAOManager.Attrib a = VAOManager.get(i);
|
||||
final VAOManager.Attrib a = vaos.attrib(i);
|
||||
if (a == null || !a.enabled) continue;
|
||||
|
||||
if (a.vboId != 0) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaBooleanLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BooleanState;
|
||||
@@ -89,7 +90,8 @@ public class BooleanStateStack extends BooleanState implements CowStateStack<Boo
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
if (GLStateManager.getAttribDepth() > 0
|
||||
final GLContextState o = cow.owner;
|
||||
if (o != null && o.attribDepth > 0
|
||||
&& (stateUnknown || !GLStateManager.isCachingEnabled() || enabled != vanillaValue())) {
|
||||
beforeModify();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
|
||||
/**
|
||||
@@ -14,6 +15,7 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
|
||||
public final class CowDepths {
|
||||
public int id = -1;
|
||||
public GLContextState owner;
|
||||
public final int restoreBit;
|
||||
public final int restoreUnit;
|
||||
private final int[] slotDepth = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
|
||||
/**
|
||||
@@ -41,15 +42,19 @@ public interface CowStateStack<T> extends IStateStack<T> {
|
||||
|
||||
@Override
|
||||
default int pushDepth() {
|
||||
return GLStateManager.getAttribDepth();
|
||||
final GLContextState o = cowDepths().owner;
|
||||
return o == null ? 0 : o.attribDepth;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void beforeModify() {
|
||||
final int s = cowDepths().claim(GLStateManager.getAttribDepth());
|
||||
final CowDepths d = cowDepths();
|
||||
final GLContextState o = d.owner;
|
||||
if (o == null) return;
|
||||
final int s = d.claim(o.attribDepth);
|
||||
if (s >= 0) {
|
||||
captureSlot(s);
|
||||
GLStateManager.registerModifiedState(stackId());
|
||||
GLStateManager.registerModifiedState(o, d.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,8 @@ public final class StackIdAllocator {
|
||||
|
||||
private StackIdAllocator() {}
|
||||
|
||||
private static final int STATIC_BASE = 4096;
|
||||
private static final int STATIC_CAPACITY = 16;
|
||||
|
||||
private static int next = -1;
|
||||
private static int perContext = -1;
|
||||
private static int staticNext;
|
||||
|
||||
public static void beginContext() {
|
||||
next = 0;
|
||||
@@ -28,14 +24,7 @@ public final class StackIdAllocator {
|
||||
return next >= 0 ? next++ : -1;
|
||||
}
|
||||
|
||||
public static int nextStaticId() {
|
||||
if (staticNext >= STATIC_CAPACITY) {
|
||||
throw new IllegalStateException("static stack id capacity exceeded");
|
||||
}
|
||||
return STATIC_BASE + staticNext++;
|
||||
}
|
||||
|
||||
public static int capacity() {
|
||||
return STATIC_BASE + STATIC_CAPACITY;
|
||||
public static int allocated() {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
|
||||
public final class StreamingVaos {
|
||||
private static final int FORMAT_COUNT = VertexFlags.BITSET_SIZE;
|
||||
|
||||
final int[] persistentVAOs = new int[FORMAT_COUNT];
|
||||
final int[] orphanVAOs = new int[FORMAT_COUNT];
|
||||
|
||||
final int[] extendedPersistentVAOs = new int[FORMAT_COUNT];
|
||||
final int[] extendedOrphanVAOs = new int[FORMAT_COUNT];
|
||||
}
|
||||
+35
-56
@@ -6,6 +6,7 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.QuadConverter;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
@@ -47,11 +48,6 @@ public class TessellatorStreamingDrawer {
|
||||
private static PersistentStreamingBuffer persistentBuffer;
|
||||
private static final OrphanStreamingBuffer[] orphanBuffers = new OrphanStreamingBuffer[FORMAT_COUNT];
|
||||
|
||||
private static final int[] persistentVAOs = new int[FORMAT_COUNT];
|
||||
private static final int[] orphanVAOs = new int[FORMAT_COUNT];
|
||||
|
||||
private static final int[] extendedPersistentVAOs = new int[FORMAT_COUNT];
|
||||
private static final int[] extendedOrphanVAOs = new int[FORMAT_COUNT];
|
||||
private static ByteBuffer extScratch;
|
||||
private static long extScratchAddress;
|
||||
private static int extScratchCapacity;
|
||||
@@ -243,13 +239,18 @@ public class TessellatorStreamingDrawer {
|
||||
final int normalOffset = ImmediateExtendedAttribHandler.normalOffset(format);
|
||||
final int combinedStride = vertexSize + ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int combinedBytes = vertexCount * combinedStride;
|
||||
ensureExtScratch(combinedBytes);
|
||||
interleaveBase(srcBase, extScratchAddress, vertexCount, vertexSize, combinedStride);
|
||||
extHandler.buildPacked(srcBase, vertexSize, 0, texOffset, normalOffset, vertexCount, extPrim, extScratchAddress + vertexSize, combinedStride);
|
||||
extScratch.position(0);
|
||||
extScratch.limit(combinedBytes);
|
||||
uploadAndDrawExtended(extScratch, flags, format, combinedStride, drawMode, vertexCount);
|
||||
shrinkExtScratchIfOversized(combinedBytes);
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
ensureExtScratch(combinedBytes);
|
||||
interleaveBase(srcBase, extScratchAddress, vertexCount, vertexSize, combinedStride);
|
||||
extHandler.buildPacked(srcBase, vertexSize, 0, texOffset, normalOffset, vertexCount, extPrim, extScratchAddress + vertexSize, combinedStride);
|
||||
extScratch.position(0);
|
||||
extScratch.limit(combinedBytes);
|
||||
uploadAndDrawExtended(extScratch, flags, format, combinedStride, drawMode, vertexCount);
|
||||
shrinkExtScratchIfOversized(combinedBytes);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -311,7 +312,9 @@ public class TessellatorStreamingDrawer {
|
||||
Tracy.beginZone(Z_SDL_STREAM_DRAW);
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
ensureVAO(flags, format);
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final StreamingVaos vaos = glCtx.streamingVaos;
|
||||
ensureVAO(vaos, flags, format);
|
||||
|
||||
if (Tracy.ENABLED) {
|
||||
streamedBytes += packed.remaining();
|
||||
@@ -326,10 +329,10 @@ public class TessellatorStreamingDrawer {
|
||||
|
||||
final boolean fromRing = firstVertex >= 0;
|
||||
if (fromRing) {
|
||||
GLStateManager.glBindVertexArray(persistentVAOs[flags]);
|
||||
GLStateManager.glBindVertexArray(vaos.persistentVAOs[flags]);
|
||||
} else {
|
||||
if (Tracy.ENABLED && persistentBuffer != null) orphanFallbacks++;
|
||||
GLStateManager.glBindVertexArray(orphanVAOs[flags]);
|
||||
GLStateManager.glBindVertexArray(vaos.orphanVAOs[flags]);
|
||||
orphanBuffers[flags].upload(packed);
|
||||
firstVertex = 0;
|
||||
}
|
||||
@@ -340,7 +343,7 @@ public class TessellatorStreamingDrawer {
|
||||
lastStreamDrawMode = drawMode;
|
||||
}
|
||||
|
||||
drawWithQuadConversion(drawMode, firstVertex, vertexCount);
|
||||
drawWithQuadConversion(glCtx, drawMode, firstVertex, vertexCount);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
@@ -352,11 +355,11 @@ public class TessellatorStreamingDrawer {
|
||||
return persistentBuffer == null ? 0L : persistentBuffer.getWraps();
|
||||
}
|
||||
|
||||
private static void drawWithQuadConversion(int drawMode, int firstVertex, int vertexCount) {
|
||||
private static void drawWithQuadConversion(GLContextState glCtx, int drawMode, int firstVertex, int vertexCount) {
|
||||
FfpExtendedAttribs.beginInternalDraw();
|
||||
try {
|
||||
if (drawMode == GL11.GL_QUADS) {
|
||||
QuadConverter.drawQuadsAsTriangles(firstVertex, vertexCount);
|
||||
QuadConverter.drawQuadsAsTriangles(glCtx, firstVertex, vertexCount);
|
||||
} else {
|
||||
GLStateManager.glDrawArrays(drawMode, firstVertex, vertexCount);
|
||||
}
|
||||
@@ -366,8 +369,10 @@ public class TessellatorStreamingDrawer {
|
||||
}
|
||||
|
||||
private static void uploadAndDrawExtended(ByteBuffer combined, int flags, VertexFormat format, int combinedStride, int drawMode, int vertexCount) {
|
||||
ensureVAO(flags, format);
|
||||
ensureExtendedVAOs(flags, format);
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final StreamingVaos vaos = glCtx.streamingVaos;
|
||||
ensureVAO(vaos, flags, format);
|
||||
ensureExtendedVAOs(vaos, flags, format);
|
||||
|
||||
int firstVertex = -1;
|
||||
|
||||
@@ -376,18 +381,20 @@ public class TessellatorStreamingDrawer {
|
||||
}
|
||||
|
||||
if (firstVertex >= 0) {
|
||||
GLStateManager.glBindVertexArray(extendedPersistentVAOs[flags]);
|
||||
GLStateManager.glBindVertexArray(vaos.extendedPersistentVAOs[flags]);
|
||||
} else {
|
||||
GLStateManager.glBindVertexArray(extendedOrphanVAOs[flags]);
|
||||
GLStateManager.glBindVertexArray(vaos.extendedOrphanVAOs[flags]);
|
||||
orphanBuffers[flags].upload(combined);
|
||||
firstVertex = 0;
|
||||
}
|
||||
|
||||
drawWithQuadConversion(drawMode, firstVertex, vertexCount);
|
||||
drawWithQuadConversion(glCtx, drawMode, firstVertex, vertexCount);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
private static void ensureExtendedVAOs(int flags, VertexFormat format) {
|
||||
private static void ensureExtendedVAOs(StreamingVaos vaos, int flags, VertexFormat format) {
|
||||
final int[] extendedOrphanVAOs = vaos.extendedOrphanVAOs;
|
||||
final int[] extendedPersistentVAOs = vaos.extendedPersistentVAOs;
|
||||
if (extendedOrphanVAOs[flags] == 0) {
|
||||
extendedOrphanVAOs[flags] = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(extendedOrphanVAOs[flags]);
|
||||
@@ -467,12 +474,13 @@ public class TessellatorStreamingDrawer {
|
||||
return repackBuffer;
|
||||
}
|
||||
|
||||
private static void ensureVAO(int flags, VertexFormat format) {
|
||||
private static void ensureVAO(StreamingVaos vaos, int flags, VertexFormat format) {
|
||||
init();
|
||||
if (orphanBuffers[flags] == null) orphanBuffers[flags] = new OrphanStreamingBuffer();
|
||||
final int[] orphanVAOs = vaos.orphanVAOs;
|
||||
final int[] persistentVAOs = vaos.persistentVAOs;
|
||||
|
||||
if (orphanVAOs[flags] == 0) {
|
||||
orphanBuffers[flags] = new OrphanStreamingBuffer();
|
||||
|
||||
orphanVAOs[flags] = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(orphanVAOs[flags]);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, orphanBuffers[flags].getBufferId());
|
||||
@@ -489,33 +497,4 @@ public class TessellatorStreamingDrawer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up all VAOs, streaming buffers, and the repack buffer.
|
||||
*/
|
||||
public static void destroy() {
|
||||
for (int i = 0; i < FORMAT_COUNT; i++) {
|
||||
if (persistentVAOs[i] != 0) { GLStateManager.glDeleteVertexArrays(persistentVAOs[i]); persistentVAOs[i] = 0; }
|
||||
if (orphanVAOs[i] != 0) { GLStateManager.glDeleteVertexArrays(orphanVAOs[i]); orphanVAOs[i] = 0; }
|
||||
if (extendedPersistentVAOs[i] != 0) { GLStateManager.glDeleteVertexArrays(extendedPersistentVAOs[i]); extendedPersistentVAOs[i] = 0; }
|
||||
if (extendedOrphanVAOs[i] != 0) { GLStateManager.glDeleteVertexArrays(extendedOrphanVAOs[i]); extendedOrphanVAOs[i] = 0; }
|
||||
if (orphanBuffers[i] != null) { orphanBuffers[i].destroy(); orphanBuffers[i] = null; }
|
||||
}
|
||||
if (extScratch != null) {
|
||||
memFree(extScratch);
|
||||
extScratch = null;
|
||||
extScratchAddress = 0;
|
||||
extScratchCapacity = 0;
|
||||
}
|
||||
if (persistentBuffer != null) {
|
||||
persistentBuffer.destroy();
|
||||
persistentBuffer = null;
|
||||
}
|
||||
if (repackBuffer != null) {
|
||||
memFree(repackBuffer);
|
||||
repackBuffer = null;
|
||||
repackAddress = 0;
|
||||
repackCapacity = 0;
|
||||
}
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ class CompatUniformManagerTest {
|
||||
|
||||
// Use program and upload — should not error
|
||||
GL20.glUseProgram(program);
|
||||
CompatUniformManager.refreshCompatUniforms(program);
|
||||
CompatUniformManager.refreshCompatUniforms(program, GLStateManager.ctx());
|
||||
|
||||
// Verify no GL errors
|
||||
assertEquals(0, GL11.glGetError(), "GL error after uniform upload");
|
||||
@@ -146,7 +146,7 @@ class CompatUniformManagerTest {
|
||||
|
||||
// Use program and upload — should not error
|
||||
GL20.glUseProgram(program);
|
||||
CompatUniformManager.refreshCompatUniforms(program);
|
||||
CompatUniformManager.refreshCompatUniforms(program, GLStateManager.ctx());
|
||||
|
||||
assertEquals(0, GL11.glGetError(), "GL error after fog uniform upload");
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.TestThreads;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -12,6 +16,28 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class DisplayListIdAllocatorTest {
|
||||
|
||||
@Test
|
||||
void concurrentAllocationsRemainUnique() throws Exception {
|
||||
final DisplayListIDAllocator alloc = new DisplayListIDAllocator();
|
||||
final int[][] ids = new int[2][500];
|
||||
final CountDownLatch start = new CountDownLatch(1);
|
||||
final TestThreads.Worker[] workers = new TestThreads.Worker[2];
|
||||
for (int t = 0; t < workers.length; t++) {
|
||||
final int[] lane = ids[t];
|
||||
workers[t] = TestThreads.start("DisplayListIdAllocator-" + t, () -> {
|
||||
if (!start.await(5, TimeUnit.SECONDS)) throw new AssertionError("start timed out");
|
||||
for (int i = 0; i < lane.length; i++) lane[i] = alloc.allocRange(1);
|
||||
});
|
||||
}
|
||||
start.countDown();
|
||||
for (TestThreads.Worker worker : workers) worker.join();
|
||||
final Set<Integer> unique = new HashSet<>();
|
||||
for (int[] lane : ids) {
|
||||
for (int id : lane) assertTrue(unique.add(id));
|
||||
}
|
||||
assertEquals(1000, unique.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void zeroNeverReturned() {
|
||||
DisplayListIDAllocator alloc = new DisplayListIDAllocator(1);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.TestThreads;
|
||||
import org.lwjgl.opengl.SharedDrawable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
final class DrawableContexts {
|
||||
|
||||
private DrawableContexts() {}
|
||||
|
||||
static TestThreads.Worker start(SharedDrawable drawable, String name, TestThreads.Body body) {
|
||||
return TestThreads.start(name, () -> {
|
||||
GLStateManager.makeCurrent(drawable);
|
||||
try {
|
||||
body.run();
|
||||
} finally {
|
||||
GLStateManager.releaseContext(drawable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void run(SharedDrawable drawable, String name, TestThreads.Body body) throws InterruptedException {
|
||||
start(drawable, name, body).join();
|
||||
}
|
||||
|
||||
static void destroy(SharedDrawable drawable) {
|
||||
final Map<Object, GLContextState> contexts = Reflect.getStatic(GLStateManager.class, "drawableContexts");
|
||||
final GLContextState ctx = contexts.remove(drawable);
|
||||
if (ctx != null) {
|
||||
final GLContextState[] states = Reflect.getStatic(GLStateManager.class, "drawableContextStates");
|
||||
Reflect.setStatic(GLStateManager.class, "drawableContextStates", Arrays.stream(states).filter(s -> s != ctx).toArray(GLContextState[]::new));
|
||||
}
|
||||
drawable.destroy();
|
||||
}
|
||||
}
|
||||
+40
-22
@@ -15,6 +15,7 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
@@ -37,7 +38,10 @@ public class GLSM_ServerSideState_UnitTest {
|
||||
private Runnable pendingCommand;
|
||||
private Throwable backgroundError;
|
||||
private volatile boolean shutdownRequested;
|
||||
private volatile GLContextState workerContext;
|
||||
private volatile boolean workerCaching;
|
||||
private boolean originalSplashComplete;
|
||||
private Thread originalDrawableGLHolder;
|
||||
|
||||
private static void setSplashComplete(boolean value) {
|
||||
SplashWindow.setSplashComplete(value);
|
||||
@@ -50,6 +54,7 @@ public class GLSM_ServerSideState_UnitTest {
|
||||
@BeforeEach
|
||||
void setupSharedDrawable() throws Exception {
|
||||
originalSplashComplete = getSplashComplete();
|
||||
originalDrawableGLHolder = GLStateManager.getDrawableGLHolder();
|
||||
|
||||
// Reset splash state to simulate splash screen scenario
|
||||
setSplashComplete(false);
|
||||
@@ -67,6 +72,8 @@ public class GLSM_ServerSideState_UnitTest {
|
||||
|
||||
shutdownRequested = false;
|
||||
backgroundError = null;
|
||||
workerContext = null;
|
||||
workerCaching = false;
|
||||
initLatch = new CountDownLatch(1);
|
||||
commandLatch = new CountDownLatch(1);
|
||||
completeLatch = new CountDownLatch(1);
|
||||
@@ -75,10 +82,8 @@ public class GLSM_ServerSideState_UnitTest {
|
||||
backgroundThread = new Thread(() -> {
|
||||
try {
|
||||
GLStateManager.makeCurrent(sharedDrawable);
|
||||
|
||||
if (GLStateManager.isCachingEnabled()) {
|
||||
throw new AssertionError("SharedDrawable thread should have caching DISABLED");
|
||||
}
|
||||
workerContext = GLStateManager.ctx();
|
||||
workerCaching = GLStateManager.isCachingEnabled();
|
||||
|
||||
// Signal that initialization is complete
|
||||
initLatch.countDown();
|
||||
@@ -102,11 +107,12 @@ public class GLSM_ServerSideState_UnitTest {
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// Normal shutdown
|
||||
} catch (LWJGLException e) {
|
||||
backgroundError = e;
|
||||
} catch (Throwable t) {
|
||||
backgroundError = t;
|
||||
initLatch.countDown();
|
||||
} finally {
|
||||
try {
|
||||
sharedDrawable.releaseContext();
|
||||
GLStateManager.releaseContext(sharedDrawable);
|
||||
} catch (LWJGLException e) {
|
||||
// Ignore on cleanup
|
||||
}
|
||||
@@ -116,6 +122,11 @@ public class GLSM_ServerSideState_UnitTest {
|
||||
|
||||
// Wait for background thread to complete initialization
|
||||
assertTrue(initLatch.await(5, TimeUnit.SECONDS), "Background thread failed to initialize");
|
||||
if (backgroundError != null) {
|
||||
fail("Background thread failed to make SharedDrawable current", backgroundError);
|
||||
}
|
||||
assertTrue(workerCaching, "SharedDrawable thread should cache into its own context");
|
||||
assertNotSame(GLStateManager.ctx(), workerContext, "SharedDrawable thread must not share the main thread's context");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@@ -132,11 +143,12 @@ public class GLSM_ServerSideState_UnitTest {
|
||||
}
|
||||
|
||||
if (sharedDrawable != null) {
|
||||
sharedDrawable.destroy();
|
||||
DrawableContexts.destroy(sharedDrawable);
|
||||
}
|
||||
|
||||
// Restore original state
|
||||
GLStateManager.setDrawableGL(null);
|
||||
GLStateManager.setDrawableGLHolder(originalDrawableGLHolder);
|
||||
setSplashComplete(originalSplashComplete);
|
||||
if (originalSplashComplete) {
|
||||
GLStateManager.markSplashComplete("test");
|
||||
@@ -328,7 +340,11 @@ public class GLSM_ServerSideState_UnitTest {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId1);
|
||||
assertEquals(texId1, GLStateManager.getBoundTextureForServerState(), "Main thread cache should show texId1 bound");
|
||||
|
||||
executeOnSharedDrawable(() -> GL11.glBindTexture(GL11.GL_TEXTURE_2D, texId2));
|
||||
executeOnSharedDrawable(() -> {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId2);
|
||||
assertEquals(texId2, GLStateManager.getBoundTextureForServerState(), "SharedDrawable cache should show texId2 bound");
|
||||
assertEquals(texId2, GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D), "SharedDrawable actual GL binding should be texId2");
|
||||
});
|
||||
|
||||
assertEquals(texId1, GLStateManager.getBoundTextureForServerState(), "Main thread cache should still show texId1 (client-side state is per-context)");
|
||||
|
||||
@@ -436,38 +452,40 @@ public class GLSM_ServerSideState_UnitTest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test glDeleteTextures: SharedDrawable deletes texture, verify cache cleared.
|
||||
*/
|
||||
@Test
|
||||
void testDeleteTexturesFromSharedDrawable() throws InterruptedException {
|
||||
int texId = GL11.glGenTextures();
|
||||
final int texId = GLStateManager.glGenTextures();
|
||||
int flushGen = 0;
|
||||
|
||||
try {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, 32, 32, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
|
||||
int cachedFilter = GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
|
||||
assertEquals(GL11.GL_LINEAR, cachedFilter, "Cache should have LINEAR before delete");
|
||||
|
||||
assertEquals(GL11.GL_LINEAR, GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER), "Cache should have LINEAR before delete");
|
||||
assertTrue(GL11.glIsTexture(texId), "Texture should exist before delete");
|
||||
|
||||
// SharedDrawable: delete the texture (deferred — name stays valid)
|
||||
executeOnSharedDrawable(() -> {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
|
||||
GLStateManager.glDeleteTextures(texId);
|
||||
assertEquals(0, GLStateManager.getBoundTextureForServerState(), "Delete should unbind from the deleting context");
|
||||
assertTrue(GL11.glIsTexture(texId), "Texture name should still be valid (deferred delete)");
|
||||
});
|
||||
|
||||
assertTrue(GL11.glIsTexture(texId), "Texture name should still be valid on main thread (deferred delete)");
|
||||
assertEquals(texId, GLStateManager.getBoundTextureForServerState(), "Main thread binding should survive until the flush");
|
||||
|
||||
// glGenTextures flushes deferred deletes
|
||||
int newTexId = GLStateManager.glGenTextures();
|
||||
assertFalse(GL11.glIsTexture(texId), "Texture should be gone after flush via glGenTextures");
|
||||
GL11.glDeleteTextures(newTexId);
|
||||
final int deferredGen = GLStateManager.glGenTextures();
|
||||
GL11.glDeleteTextures(deferredGen);
|
||||
assertTrue(GL11.glIsTexture(texId), "Delete should stay deferred while the splash is not complete");
|
||||
|
||||
setSplashComplete(true);
|
||||
flushGen = GLStateManager.glGenTextures();
|
||||
assertFalse(GL11.glIsTexture(texId), "Texture should be gone after the post-splash flush");
|
||||
assertEquals(0, GLStateManager.getBoundTextureForServerState(), "Flush should scrub the main thread binding");
|
||||
|
||||
} finally {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
if (flushGen != 0) GL11.glDeleteTextures(flushGen);
|
||||
if (GL11.glIsTexture(texId)) {
|
||||
GL11.glDeleteTextures(texId);
|
||||
}
|
||||
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
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.testutil.TestThreads;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.SharedDrawable;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
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.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;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_SharedDrawableContext_GLTest {
|
||||
|
||||
private SharedDrawable sharedDrawable;
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
if (sharedDrawable != null) {
|
||||
DrawableContexts.destroy(sharedDrawable);
|
||||
sharedDrawable = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void sharedDrawableBindsItsOwnContext() throws Exception {
|
||||
final GLContextState primary = GLStateManager.ctx();
|
||||
sharedDrawable = new SharedDrawable(Display.getDrawable());
|
||||
|
||||
TestThreads.run("SharedDrawableContext-Bind-Thread", () -> {
|
||||
GLStateManager.makeCurrent(sharedDrawable);
|
||||
try {
|
||||
final GLContextState workerCtx = GLStateManager.ctx();
|
||||
assertNotSame(primary, workerCtx);
|
||||
assertNotEquals(0, workerCtx.defaultVAO);
|
||||
assertEquals(workerCtx.defaultVAO, GL11.glGetInteger(GL30.GL_VERTEX_ARRAY_BINDING));
|
||||
|
||||
final IntBuffer viewport = BufferUtils.createIntBuffer(16);
|
||||
GL11.glGetInteger(GL11.GL_VIEWPORT, viewport);
|
||||
assertEquals(0, viewport.get(0));
|
||||
assertEquals(0, viewport.get(1));
|
||||
assertEquals(Display.getWidth(), viewport.get(2));
|
||||
assertEquals(Display.getHeight(), viewport.get(3));
|
||||
} finally {
|
||||
GLStateManager.releaseContext(sharedDrawable);
|
||||
}
|
||||
assertSame(primary, GLStateManager.ctx());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void failedContextSwitchRestoresPreviousBinding() throws Exception {
|
||||
final GLContextState primary = GLStateManager.ctx();
|
||||
sharedDrawable = new SharedDrawable(Display.getDrawable());
|
||||
final CountDownLatch acquired = new CountDownLatch(1);
|
||||
final CountDownLatch release = new CountDownLatch(1);
|
||||
final TestThreads.Worker worker = DrawableContexts.start(sharedDrawable, "SharedDrawableContext-Owner-Thread", () -> {
|
||||
acquired.countDown();
|
||||
if (!release.await(5, TimeUnit.SECONDS)) throw new AssertionError("release timed out");
|
||||
});
|
||||
try {
|
||||
assertTrue(acquired.await(5, TimeUnit.SECONDS));
|
||||
assertThrows(IllegalStateException.class, () -> GLStateManager.makeCurrent(sharedDrawable));
|
||||
assertSame(primary, GLStateManager.ctx());
|
||||
assertEquals(primary.defaultVAO, GL11.glGetInteger(GL30.GL_VERTEX_ARRAY_BINDING));
|
||||
} finally {
|
||||
release.countDown();
|
||||
worker.join();
|
||||
}
|
||||
GLStateManager.makeCurrent(sharedDrawable);
|
||||
assertNotSame(primary, GLStateManager.ctx());
|
||||
GLStateManager.releaseContext(sharedDrawable);
|
||||
GLStateManager.makeCurrent(Display.getDrawable());
|
||||
assertSame(primary, GLStateManager.ctx());
|
||||
}
|
||||
|
||||
@Test
|
||||
void foreignThreadCannotFinishOrRecordIntoActiveList() throws Exception {
|
||||
sharedDrawable = new SharedDrawable(Display.getDrawable());
|
||||
final int existing = GLStateManager.glGenLists(1);
|
||||
final int active = GLStateManager.glGenLists(1);
|
||||
final int rejected = GLStateManager.glGenLists(1);
|
||||
GLStateManager.glNewList(existing, GL11.GL_COMPILE);
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.glEndList();
|
||||
GLStateManager.disableBlend();
|
||||
|
||||
final CountDownLatch started = new CountDownLatch(1);
|
||||
final CountDownLatch finish = new CountDownLatch(1);
|
||||
final TestThreads.Worker worker = DrawableContexts.start(sharedDrawable, "SharedDrawableContext-List-Thread", () -> {
|
||||
GLStateManager.glNewList(active, GL11.GL_COMPILE);
|
||||
started.countDown();
|
||||
if (!finish.await(5, TimeUnit.SECONDS)) throw new AssertionError("finish timed out");
|
||||
GLStateManager.glEndList();
|
||||
});
|
||||
try {
|
||||
try {
|
||||
assertTrue(started.await(5, TimeUnit.SECONDS));
|
||||
assertThrows(IllegalStateException.class, () -> GLStateManager.glNewList(rejected, GL11.GL_COMPILE));
|
||||
assertThrows(IllegalStateException.class, GLStateManager::glEndList);
|
||||
assertEquals(0, GLStateManager.getListMode());
|
||||
DisplayListManager.abortIfLeaked();
|
||||
assertThrows(IllegalStateException.class, DisplayListManager::abortCompilation);
|
||||
GLStateManager.glCallList(existing);
|
||||
assertTrue(GLStateManager.glIsEnabled(GL11.GL_BLEND));
|
||||
assertEquals(0, GLStateManager.getListMode());
|
||||
} finally {
|
||||
finish.countDown();
|
||||
worker.join();
|
||||
}
|
||||
assertTrue(DisplayListManager.displayListExists(active));
|
||||
GLStateManager.glCallList(active);
|
||||
} finally {
|
||||
GLStateManager.glDeleteLists(existing, 1);
|
||||
GLStateManager.glDeleteLists(active, 1);
|
||||
GLStateManager.glDeleteLists(rejected, 1);
|
||||
GLStateManager.disableBlend();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void simultaneousListStartsClaimOneOwner() throws Exception {
|
||||
sharedDrawable = new SharedDrawable(Display.getDrawable());
|
||||
final int first = GLStateManager.glGenLists(1);
|
||||
final int second = GLStateManager.glGenLists(1);
|
||||
final CyclicBarrier start = new CyclicBarrier(2);
|
||||
final CountDownLatch attempted = new CountDownLatch(2);
|
||||
final AtomicInteger completed = new AtomicInteger();
|
||||
final AtomicInteger rejected = new AtomicInteger();
|
||||
final TestThreads.Worker worker = DrawableContexts.start(sharedDrawable, "SharedDrawableContext-List-Race-Thread", () -> {
|
||||
start.await();
|
||||
try {
|
||||
GLStateManager.glNewList(second, GL11.GL_COMPILE);
|
||||
} catch (IllegalStateException expected) {
|
||||
rejected.incrementAndGet();
|
||||
attempted.countDown();
|
||||
return;
|
||||
}
|
||||
attempted.countDown();
|
||||
if (!attempted.await(5, TimeUnit.SECONDS)) throw new AssertionError("attempts timed out");
|
||||
GLStateManager.glEndList();
|
||||
completed.incrementAndGet();
|
||||
});
|
||||
try {
|
||||
start.await();
|
||||
try {
|
||||
GLStateManager.glNewList(first, GL11.GL_COMPILE);
|
||||
} catch (IllegalStateException expected) {
|
||||
rejected.incrementAndGet();
|
||||
attempted.countDown();
|
||||
}
|
||||
if (GLStateManager.getListMode() != 0) {
|
||||
attempted.countDown();
|
||||
assertTrue(attempted.await(5, TimeUnit.SECONDS));
|
||||
GLStateManager.glEndList();
|
||||
completed.incrementAndGet();
|
||||
}
|
||||
worker.join();
|
||||
} finally {
|
||||
GLStateManager.glDeleteLists(first, 1);
|
||||
GLStateManager.glDeleteLists(second, 1);
|
||||
}
|
||||
assertEquals(1, completed.get());
|
||||
assertEquals(1, rejected.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void reenteredContextRebindsFfpProgram() throws Exception {
|
||||
sharedDrawable = new SharedDrawable(Display.getDrawable());
|
||||
|
||||
DrawableContexts.run(sharedDrawable, "SharedDrawableContext-Reentry-Thread", () -> {
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
final boolean wasEnabled = ShaderManager.isEnabled();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
try {
|
||||
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 int vbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, vertices, GL15.GL_STATIC_DRAW);
|
||||
FfpFixture.attrib(0, 3, GL11.GL_FLOAT, false, 12, 0);
|
||||
VAOManager.setCurrentVertexFlags(0);
|
||||
|
||||
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
|
||||
|
||||
GLStateManager.releaseContext(sharedDrawable);
|
||||
GLStateManager.makeCurrent(sharedDrawable);
|
||||
|
||||
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
|
||||
|
||||
assertNotEquals(0, GL11.glGetInteger(GL20.GL_CURRENT_PROGRAM), "invalidateProgram must force a rebind after replay clears the program to 0");
|
||||
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
} finally {
|
||||
sm.deactivate();
|
||||
if (!wasEnabled) ShaderManager.disable();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void deferredTextureDeletesWaitForSplashAndScrubDrawableContexts() throws Exception {
|
||||
sharedDrawable = new SharedDrawable(Display.getDrawable());
|
||||
final int t = GLStateManager.glGenTextures();
|
||||
|
||||
final AtomicReference<GLContextState> workerCtxRef = new AtomicReference<>();
|
||||
DrawableContexts.run(sharedDrawable, "SharedDrawableContext-DeferredDelete-Thread", () -> {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, t);
|
||||
workerCtxRef.set(GLStateManager.ctx());
|
||||
});
|
||||
|
||||
final GLContextState workerCtx = workerCtxRef.get();
|
||||
int deferredGen = 0;
|
||||
int flushGen = 0;
|
||||
|
||||
try {
|
||||
SplashWindow.setSplashComplete(false);
|
||||
GLStateManager.glDeleteTextures(t);
|
||||
deferredGen = GLStateManager.glGenTextures();
|
||||
|
||||
assertTrue(GL11.glIsTexture(t), "delete must stay deferred while the splash is not complete");
|
||||
assertEquals(t, workerCtx.textures.getTextureUnitBindings(0).getBinding(), "worker ctx binding must survive until the flush");
|
||||
|
||||
SplashWindow.setSplashComplete(true);
|
||||
flushGen = GLStateManager.glGenTextures();
|
||||
|
||||
assertFalse(GL11.glIsTexture(t), "the flush must delete the texture once the splash is complete");
|
||||
assertEquals(0, workerCtx.textures.getTextureUnitBindings(0).getBinding(), "the flush must scrub the drawable context's binding");
|
||||
} finally {
|
||||
SplashWindow.setSplashComplete(true);
|
||||
if (deferredGen != 0) GL11.glDeleteTextures(deferredGen);
|
||||
if (flushGen != 0) GL11.glDeleteTextures(flushGen);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void concurrentContextsDoNotShareAttribOrCurrentState() throws Exception {
|
||||
final GLContextState primary = GLStateManager.ctx();
|
||||
sharedDrawable = new SharedDrawable(Display.getDrawable());
|
||||
|
||||
final CyclicBarrier barrier = new CyclicBarrier(2);
|
||||
final AtomicReference<GLContextState> workerCtxRef = new AtomicReference<>();
|
||||
final TestThreads.Worker worker = DrawableContexts.start(sharedDrawable, "SharedDrawableContext-Concurrent-Thread", () -> {
|
||||
workerCtxRef.set(GLStateManager.ctx());
|
||||
barrier.await();
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
final int t = GLStateManager.pushState(StateSet.FONT);
|
||||
GLStateManager.glNormal3f(0.1f, 0.2f, 0.3f);
|
||||
GLStateManager.popStateTo(t);
|
||||
}
|
||||
GLStateManager.glNormal3f(0.1f, 0.2f, 0.3f);
|
||||
});
|
||||
|
||||
try {
|
||||
barrier.await();
|
||||
for (int i = 0; i < 20000; i++) {
|
||||
final int t = GLStateManager.pushState(StateSet.FONT);
|
||||
GLStateManager.glNormal3f(0.25f, 0.5f, 0.75f);
|
||||
GLStateManager.popStateTo(t);
|
||||
}
|
||||
GLStateManager.glNormal3f(0.25f, 0.5f, 0.75f);
|
||||
worker.join();
|
||||
|
||||
final GLContextState workerCtx = workerCtxRef.get();
|
||||
assertNotSame(primary, workerCtx);
|
||||
assertEquals(0, primary.attribDepth);
|
||||
assertEquals(0, workerCtx.attribDepth);
|
||||
assertEquals(0.25f, primary.ffp.currentNormal.x, 0.0f);
|
||||
assertEquals(0.5f, primary.ffp.currentNormal.y, 0.0f);
|
||||
assertEquals(0.75f, primary.ffp.currentNormal.z, 0.0f);
|
||||
assertEquals(0.1f, workerCtx.ffp.currentNormal.x, 0.0f);
|
||||
assertEquals(0.2f, workerCtx.ffp.currentNormal.y, 0.0f);
|
||||
assertEquals(0.3f, workerCtx.ffp.currentNormal.z, 0.0f);
|
||||
} finally {
|
||||
GLStateManager.glNormal3f(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -1,9 +1,14 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
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.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.Display;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -63,4 +68,37 @@ public class GLSM_SplashCompleteLatch_UnitTest {
|
||||
t.join();
|
||||
assertTrue(cachingOffThread.get(), "after the latch caching is global - this is what the latch buys");
|
||||
}
|
||||
|
||||
@Test
|
||||
void pausedSplashThreadDoesNotCompleteUntilItExits() throws Exception {
|
||||
final CountDownLatch released = new CountDownLatch(1);
|
||||
final CountDownLatch resume = new CountDownLatch(1);
|
||||
|
||||
GLStateManager.releaseContext(Display.getDrawable());
|
||||
|
||||
try {
|
||||
final TestThreads.Worker worker = TestThreads.start("SplashPause-Worker-Thread", () -> {
|
||||
GLStateManager.makeCurrent(Display.getDrawable());
|
||||
GLStateManager.releaseContext(Display.getDrawable());
|
||||
released.countDown();
|
||||
resume.await();
|
||||
});
|
||||
assertTrue(released.await(10, TimeUnit.SECONDS), "worker did not release the Display drawable in time");
|
||||
|
||||
GLStateManager.makeCurrent(Display.getDrawable());
|
||||
assertFalse(GLStateManager.isSplashComplete());
|
||||
|
||||
resume.countDown();
|
||||
worker.join();
|
||||
|
||||
GLStateManager.releaseContext(Display.getDrawable());
|
||||
GLStateManager.makeCurrent(Display.getDrawable());
|
||||
assertTrue(GLStateManager.isSplashComplete());
|
||||
} finally {
|
||||
resume.countDown();
|
||||
Reflect.setStatic(GLStateManager.class, "stateSeedPending", false);
|
||||
Reflect.setStatic(GLStateManager.class, "splashDisplayReleaser", null);
|
||||
GLStateManager.makeCurrent(Display.getDrawable());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ public class GLSM_VAO_UnitTest {
|
||||
|
||||
@Test
|
||||
void testDefaultStates() {
|
||||
verifyState(GL30.GL_VERTEX_ARRAY_BINDING, GLStateManager.getDefaultVAO(), "GL_VERTEX_ARRAY_BINDING Initial State");
|
||||
verifyState(GL30.GL_VERTEX_ARRAY_BINDING, GLStateManager.ctx().defaultVAO, "GL_VERTEX_ARRAY_BINDING Initial State");
|
||||
verifyState(GL13.GL_CLIENT_ACTIVE_TEXTURE, GL13.GL_TEXTURE0, "GL_CLIENT_ACTIVE_TEXTURE Initial State");
|
||||
testClientStates();
|
||||
}
|
||||
@@ -84,7 +84,7 @@ public class GLSM_VAO_UnitTest {
|
||||
|
||||
// Check if everything got reset to default
|
||||
glBindVertexArray(0);
|
||||
verifyState(GL30.GL_VERTEX_ARRAY_BINDING, GLStateManager.getDefaultVAO(), "GL_VERTEX_ARRAY_BINDING - Unbound");
|
||||
verifyState(GL30.GL_VERTEX_ARRAY_BINDING, GLStateManager.ctx().defaultVAO, "GL_VERTEX_ARRAY_BINDING - Unbound");
|
||||
verifyState(GL15.GL_ARRAY_BUFFER_BINDING, 0, "GL_ARRAY_BUFFER_BINDING - Unbound");
|
||||
|
||||
verifyState(GL11.GL_VERTEX_ARRAY, defaultVertexEnabled, "GL_VERTEX_ARRAY - Reset");
|
||||
@@ -192,9 +192,10 @@ public class GLSM_VAO_UnitTest {
|
||||
}
|
||||
|
||||
private static void glBindVertexArray(int array) {
|
||||
if (array == 0) array = GLStateManager.getDefaultVAO();
|
||||
GLStateManager.ctx().boundVAO = array;
|
||||
VAOManager.onBindVertexArrayPre(array);
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
if (array == 0) array = glCtx.defaultVAO;
|
||||
glCtx.boundVAO = array;
|
||||
glCtx.vaos.onBindVertexArrayPre(array);
|
||||
UniversalVAO.bindVertexArray(array);
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -28,7 +28,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
@Test
|
||||
void testGlColor4fSetsVertexAttrib() {
|
||||
GLStateManager.glColor4f(0.5f, 0.25f, 0.75f, 0.8f);
|
||||
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
|
||||
GLStateManager.flushDeferredVertexAttribs(GLStateManager.ctx(), false, false, false, false);
|
||||
|
||||
float[] color = getVertexAttrib4f(COLOR_ATTRIB);
|
||||
assertEquals(0.5f, color[0], EPSILON, "color.r");
|
||||
@@ -40,7 +40,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
@Test
|
||||
void testGlNormal3fSetsVertexAttrib() {
|
||||
GLStateManager.glNormal3f(0.0f, 1.0f, 0.0f);
|
||||
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
|
||||
GLStateManager.flushDeferredVertexAttribs(GLStateManager.ctx(), false, false, false, false);
|
||||
|
||||
float[] normal = getVertexAttrib4f(NORMAL_ATTRIB);
|
||||
assertEquals(0.0f, normal[0], EPSILON, "normal.x");
|
||||
@@ -51,7 +51,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
@Test
|
||||
void testSetLightmapTextureCoordsOnUnit1SetsVertexAttrib() {
|
||||
GLStateManager.setLightmapTextureCoords(GL13.GL_TEXTURE1, 240.0f, 128.0f);
|
||||
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
|
||||
GLStateManager.flushDeferredVertexAttribs(GLStateManager.ctx(), false, false, false, false);
|
||||
|
||||
float[] lm = getVertexAttrib4f(SECONDARY_UV_ATTRIB);
|
||||
assertEquals(240.0f, lm[0], EPSILON, "lightmap.s");
|
||||
@@ -64,7 +64,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
void testGlTexCoord2fSetsVertexAttrib() {
|
||||
// glTexCoord2f outside recording → sets default vertex attrib 2 (PRIMARY_UV)
|
||||
GLStateManager.glTexCoord2f(0.5f, 0.75f);
|
||||
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
|
||||
GLStateManager.flushDeferredVertexAttribs(GLStateManager.ctx(), false, false, false, false);
|
||||
|
||||
float[] uv = getVertexAttrib4f(PRIMARY_UV_ATTRIB);
|
||||
assertEquals(0.5f, uv[0], EPSILON, "texcoord.s");
|
||||
@@ -77,7 +77,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
// Second call is a cache hit — changeColor not called.
|
||||
// Vertex attrib should still hold the correct value from the first call.
|
||||
GLStateManager.glColor4f(0.3f, 0.6f, 0.9f, 1.0f);
|
||||
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
|
||||
GLStateManager.flushDeferredVertexAttribs(GLStateManager.ctx(), false, false, false, false);
|
||||
|
||||
float[] color = getVertexAttrib4f(COLOR_ATTRIB);
|
||||
assertEquals(0.3f, color[0], EPSILON, "color.r after dedup");
|
||||
@@ -89,7 +89,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
@Test
|
||||
void testCompileMode_DoesNotSetVertexAttrib() {
|
||||
GLStateManager.glColor4f(0.1f, 0.2f, 0.3f, 1.0f);
|
||||
GLStateManager.flushDeferredVertexAttribs(false, false, false, false);
|
||||
GLStateManager.flushDeferredVertexAttribs(GLStateManager.ctx(), false, false, false, false);
|
||||
|
||||
int listId = GLStateManager.glGenLists(1);
|
||||
GLStateManager.glNewList(listId, GL11.GL_COMPILE);
|
||||
|
||||
@@ -245,7 +245,7 @@ class FFPCombineShaderTest {
|
||||
@Test
|
||||
void testMultiTexCoordRoutesToShaderManager() {
|
||||
// Not in immediate mode or display list recording, so should go to ShaderManager.currentTexCoord
|
||||
ShaderManager.setCurrentTexCoord(0.0f, 0.0f, 0.0f, 1.0f); // reset
|
||||
GLStateManager.ctx().ffp.setTexCoord(0.0f, 0.0f, 0.0f, 1.0f); // reset
|
||||
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0, 0.5f, 0.75f);
|
||||
|
||||
var texCoord = ShaderManager.getCurrentTexCoord();
|
||||
@@ -255,7 +255,7 @@ class FFPCombineShaderTest {
|
||||
|
||||
@Test
|
||||
void testMultiTexCoord2dDelegatesToFloat() {
|
||||
ShaderManager.setCurrentTexCoord(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
GLStateManager.ctx().ffp.setTexCoord(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
GLStateManager.glMultiTexCoord2d(GL13.GL_TEXTURE0, 0.5, 0.75);
|
||||
|
||||
var texCoord = ShaderManager.getCurrentTexCoord();
|
||||
|
||||
@@ -69,7 +69,7 @@ class FFPVertexLightingGLTest {
|
||||
GLStateManager.glDisable(GL11.GL_COLOR_MATERIAL);
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
if (sm.isActive()) sm.deactivate();
|
||||
sm.disable();
|
||||
ShaderManager.disable();
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
if (vbo != 0) {
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
@@ -148,7 +148,7 @@ class FFPVertexLightingGLTest {
|
||||
private float[] drawAndReadCenter() {
|
||||
buildTriangle();
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
|
||||
GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
+3
-4
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
@@ -50,8 +49,8 @@ class FfpUniformBlockDedupTest {
|
||||
GLStateManager.glAlphaFunc(GL11.GL_ALWAYS, 0.0f);
|
||||
GLStateManager.glNormal3f(0.0f, 0.0f, 1.0f);
|
||||
GLStateManager.glTexCoord2f(0.0f, 0.0f);
|
||||
GLSMConfig.lastBrightnessX = 0.0f;
|
||||
GLSMConfig.lastBrightnessY = 0.0f;
|
||||
GLStateManager.ctx().lastBrightnessX = 0.0f;
|
||||
GLStateManager.ctx().lastBrightnessY = 0.0f;
|
||||
GLStateManager.glFog(GL11.GL_FOG_COLOR, floats(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
GLStateManager.glFogf(GL11.GL_FOG_DENSITY, 1.0f);
|
||||
GLStateManager.glTexEnv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, floats(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
@@ -128,7 +127,7 @@ class FfpUniformBlockDedupTest {
|
||||
addCategory(tests, "currentColor", () -> GLStateManager.glColor4f(0.25f, 0.5f, 0.75f, 1.0f));
|
||||
addCategory(tests, "currentNormal", () -> GLStateManager.glNormal3f(0.0f, 1.0f, 0.0f));
|
||||
addCategory(tests, "currentTexCoord", () -> GLStateManager.glTexCoord2f(0.125f, 0.375f));
|
||||
addCategory(tests, "lightmapCoord", () -> GLSMConfig.lastBrightnessX = 96.0f);
|
||||
addCategory(tests, "lightmapCoord", () -> GLStateManager.ctx().lastBrightnessX = 96.0f);
|
||||
addCategory(tests, "texGenPlane", () -> GLStateManager.glTexGen(GL11.GL_S, GL11.GL_OBJECT_PLANE, floats(0.5f, 0.0f, 0.0f, 0.25f)));
|
||||
addCategory(tests, "clipPlane", () -> GLStateManager.glClipPlane(GL11.GL_CLIP_PLANE0, doubles(0.0, 1.0, 0.0, 7.0)));
|
||||
addCategory(tests, "alphaRef", () -> GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.35f));
|
||||
|
||||
+10
-10
@@ -147,7 +147,7 @@ class InstancedFFPDrawGLTest {
|
||||
CubeParityFixture.setupScene(lightmap, lit);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
|
||||
final CubeParityFixture.Spec[] specs = CubeParityFixture.specs();
|
||||
@@ -172,7 +172,7 @@ class InstancedFFPDrawGLTest {
|
||||
ParticleParityFixture.setupScene(true);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
|
||||
final ParticleParityFixture.Particle[] particles = ParticleParityFixture.particles();
|
||||
@@ -222,9 +222,9 @@ class InstancedFFPDrawGLTest {
|
||||
|
||||
@Test
|
||||
void instancedSourceUsesInstanceAttribs() {
|
||||
GLStateManager.ffpInstancing = Instancing.TEMPLATE;
|
||||
GLStateManager.setFfpInstancing(Instancing.TEMPLATE);
|
||||
final VertexKey key = VertexKey.fromState(true, true, false, false, 0b10);
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
|
||||
final String source = VertexShaderGenerator.generate(key);
|
||||
assertTrue(source.contains("a_InstRow0"), "instance matrix attribs declared");
|
||||
@@ -248,7 +248,7 @@ class InstancedFFPDrawGLTest {
|
||||
uploadInstances(0xFF0000FF, 0, 0xFF00FF00, 0);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
@@ -278,7 +278,7 @@ class InstancedFFPDrawGLTest {
|
||||
uploadInstances(0xFF0000FF, 0, 0x0D0000FF, 0);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
GLStateManager.enableAlphaTest();
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1f);
|
||||
@@ -311,7 +311,7 @@ class InstancedFFPDrawGLTest {
|
||||
uploadInstances(0xFF0000FF, 0, 0xFF00FF00, 0);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
@@ -351,7 +351,7 @@ class InstancedFFPDrawGLTest {
|
||||
uploadInstances(0xFFFFFFFF, 0, 0xFFFFFFFF, 0xFF0000FF);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
sm.enable();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
@@ -430,8 +430,8 @@ class InstancedFFPDrawGLTest {
|
||||
}
|
||||
|
||||
private static void drawInstances() {
|
||||
GLStateManager.ffpInstancing = Instancing.TEMPLATE;
|
||||
GLStateManager.setFfpInstancing(Instancing.TEMPLATE);
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 2);
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
}
|
||||
}
|
||||
+22
-21
@@ -17,6 +17,7 @@ public class VAOManagerEboIndexTest {
|
||||
private static final int DEFAULT_VAO = 0;
|
||||
private static final int NO_VAO = -1;
|
||||
|
||||
private final VAOManager vaos = new VAOManager();
|
||||
private final Int2IntOpenHashMap expectedEbo = new Int2IntOpenHashMap();
|
||||
private int currentVao;
|
||||
|
||||
@@ -24,7 +25,7 @@ public class VAOManagerEboIndexTest {
|
||||
void reset() {
|
||||
drainManagerState();
|
||||
expectedEbo.clear();
|
||||
VAOManager.init(DEFAULT_VAO);
|
||||
vaos.init(DEFAULT_VAO);
|
||||
expectedEbo.put(DEFAULT_VAO, 0);
|
||||
currentVao = DEFAULT_VAO;
|
||||
}
|
||||
@@ -34,23 +35,23 @@ public class VAOManagerEboIndexTest {
|
||||
drainManagerState();
|
||||
}
|
||||
|
||||
private static void drainManagerState() {
|
||||
for (int vao : VAOManager.vaoMap.keySet().toIntArray()) {
|
||||
VAOManager.onDeleteVertexArray(vao);
|
||||
private void drainManagerState() {
|
||||
for (int vao : vaos.vaoMap.keySet().toIntArray()) {
|
||||
vaos.onDeleteVertexArray(vao);
|
||||
}
|
||||
VAOManager.boundEBO = 0;
|
||||
assertEquals(0, VAOManager.eboOwners.size(), "draining every VAO left entries in the index");
|
||||
vaos.boundEBO = 0;
|
||||
assertEquals(0, vaos.eboOwners.size(), "draining every VAO left entries in the index");
|
||||
}
|
||||
|
||||
private void bindVao(int vao) {
|
||||
if (currentVao != NO_VAO) expectedEbo.put(currentVao, VAOManager.boundEBO);
|
||||
VAOManager.onBindVertexArrayPre(vao);
|
||||
if (currentVao != NO_VAO) expectedEbo.put(currentVao, vaos.boundEBO);
|
||||
vaos.onBindVertexArrayPre(vao);
|
||||
expectedEbo.putIfAbsent(vao, 0);
|
||||
currentVao = vao;
|
||||
}
|
||||
|
||||
private void bindEbo(int ebo) {
|
||||
VAOManager.onBindEBO(ebo);
|
||||
vaos.onBindEBO(ebo);
|
||||
}
|
||||
|
||||
private int countOwners(int buffer) {
|
||||
@@ -62,20 +63,20 @@ public class VAOManagerEboIndexTest {
|
||||
}
|
||||
|
||||
private void deleteBuffer(int buffer) {
|
||||
VAOManager.onDeleteBuffer(buffer);
|
||||
vaos.onDeleteBuffer(buffer);
|
||||
for (int vao : expectedEbo.keySet().toIntArray()) {
|
||||
if (expectedEbo.get(vao) == buffer) expectedEbo.put(vao, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteVao(int vao) {
|
||||
VAOManager.onDeleteVertexArray(vao);
|
||||
vaos.onDeleteVertexArray(vao);
|
||||
expectedEbo.remove(vao);
|
||||
if (vao == currentVao) currentVao = NO_VAO;
|
||||
}
|
||||
|
||||
private void assertExpectedEbos(int op) {
|
||||
for (var entry : VAOManager.vaoMap.int2ObjectEntrySet()) {
|
||||
for (var entry : vaos.vaoMap.int2ObjectEntrySet()) {
|
||||
final int vao = entry.getIntKey();
|
||||
if (vao == currentVao) continue;
|
||||
assertEquals(expectedEbo.get(vao), entry.getValue().ebo, () -> "op " + op + ": vao " + vao + " ebo mismatch");
|
||||
@@ -140,10 +141,10 @@ public class VAOManagerEboIndexTest {
|
||||
|
||||
deleteBuffer(ebo);
|
||||
|
||||
assertEquals(0, VAOManager.vaoMap.get(1).ebo, "vao 1 kept a freed shared EBO");
|
||||
assertEquals(0, VAOManager.vaoMap.get(2).ebo, "vao 2 kept a freed shared EBO");
|
||||
assertEquals(0, VAOManager.vaoMap.get(3).ebo, "vao 3 kept a freed shared EBO");
|
||||
assertFalse(VAOManager.eboOwners.containsKey(ebo), "index kept owners for a freed EBO");
|
||||
assertEquals(0, vaos.vaoMap.get(1).ebo, "vao 1 kept a freed shared EBO");
|
||||
assertEquals(0, vaos.vaoMap.get(2).ebo, "vao 2 kept a freed shared EBO");
|
||||
assertEquals(0, vaos.vaoMap.get(3).ebo, "vao 3 kept a freed shared EBO");
|
||||
assertFalse(vaos.eboOwners.containsKey(ebo), "index kept owners for a freed EBO");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -156,9 +157,9 @@ public class VAOManagerEboIndexTest {
|
||||
bindVao(DEFAULT_VAO);
|
||||
|
||||
deleteVao(1);
|
||||
assertTrue(VAOManager.eboOwners.containsKey(ebo), "index dropped a still-live owner");
|
||||
assertTrue(vaos.eboOwners.containsKey(ebo), "index dropped a still-live owner");
|
||||
deleteVao(2);
|
||||
assertFalse(VAOManager.eboOwners.containsKey(ebo), "index kept an entry after its last owner went away");
|
||||
assertFalse(vaos.eboOwners.containsKey(ebo), "index kept an entry after its last owner went away");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -170,8 +171,8 @@ public class VAOManagerEboIndexTest {
|
||||
deleteVao(1);
|
||||
bindVao(DEFAULT_VAO);
|
||||
|
||||
assertFalse(VAOManager.eboOwners.containsKey(ebo), "deleting the bound VAO left an unreachable owner in the index");
|
||||
assertEquals(0, VAOManager.eboOwners.size(), "index leaked an entry for a deleted VAO");
|
||||
assertFalse(vaos.eboOwners.containsKey(ebo), "deleting the bound VAO left an unreachable owner in the index");
|
||||
assertEquals(0, vaos.eboOwners.size(), "index leaked an entry for a deleted VAO");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -184,6 +185,6 @@ public class VAOManagerEboIndexTest {
|
||||
for (int vao = 1; vao <= 64; vao++) {
|
||||
deleteVao(vao);
|
||||
}
|
||||
assertEquals(0, VAOManager.eboOwners.size(), "index leaked entries after every VAO was deleted");
|
||||
assertEquals(0, vaos.eboOwners.size(), "index leaked entries after every VAO was deleted");
|
||||
}
|
||||
}
|
||||
+8
-6
@@ -314,10 +314,11 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
final ByteBuffer colorSlice = clientData.duplicate().order(ByteOrder.nativeOrder());
|
||||
colorSlice.position(8);
|
||||
VAOManager.setAttribute(0, 2, GL11.GL_FLOAT, false, POS_COLOR_STRIDE, clientData);
|
||||
VAOManager.enableAttribute(0);
|
||||
VAOManager.setAttribute(1, 4, GL11.GL_UNSIGNED_BYTE, true, POS_COLOR_STRIDE, colorSlice);
|
||||
VAOManager.enableAttribute(1);
|
||||
final VAOManager vaos = GLStateManager.ctx().vaos;
|
||||
vaos.setAttribute(0, 2, GL11.GL_FLOAT, false, POS_COLOR_STRIDE, clientData);
|
||||
vaos.enableAttribute(0);
|
||||
vaos.setAttribute(1, 4, GL11.GL_UNSIGNED_BYTE, true, POS_COLOR_STRIDE, colorSlice);
|
||||
vaos.enableAttribute(1);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
|
||||
final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2);
|
||||
@@ -593,8 +594,9 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
VAOManager.setAttribute(0, 3, GL11.GL_FLOAT, false, 0, clientVerts);
|
||||
VAOManager.enableAttribute(0);
|
||||
final VAOManager vaos = GLStateManager.ctx().vaos;
|
||||
vaos.setAttribute(0, 3, GL11.GL_FLOAT, false, 0, clientVerts);
|
||||
vaos.enableAttribute(0);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, indices);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
@@ -42,7 +42,6 @@ public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback,
|
||||
GLStateManager.initialize(GLSMInitConfig.builder()
|
||||
.displaySize(800, 600)
|
||||
.directDrawer(t -> {})
|
||||
.streamingDrawerDestroy(() -> {})
|
||||
.enableDSA(false)
|
||||
.build());
|
||||
GLStateManager.setRunningSplash(false);
|
||||
|
||||
@@ -70,7 +70,6 @@ public class GLSMExtension implements BeforeAllCallback, BeforeEachCallback, Aft
|
||||
final GLSMInitConfig config = GLSMInitConfig.builder()
|
||||
.displaySize(displayMode.getWidth(), displayMode.getHeight())
|
||||
.directDrawer(t -> {})
|
||||
.streamingDrawerDestroy(() -> {})
|
||||
.enableDSA(false)
|
||||
.build();
|
||||
setMainThread(Thread.currentThread());
|
||||
@@ -144,7 +143,7 @@ public class GLSMExtension implements BeforeAllCallback, BeforeEachCallback, Aft
|
||||
if (ffp.isActive()) {
|
||||
ffp.deactivate();
|
||||
}
|
||||
ffp.disable();
|
||||
ShaderManager.disable();
|
||||
|
||||
// Force-sync VAO binding. Tests may unbind via raw GL, desynchronizing GLSM's cache.
|
||||
// Invalidate the cache so glBindVertexArray(0) actually issues the GL call.
|
||||
|
||||
+2
-2
@@ -272,9 +272,9 @@ public final class CubeParityFixture {
|
||||
CubeInstancedAttribs.pointInstanceAttribs(0L);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
GLStateManager.ffpInstancing = Instancing.CUBE;
|
||||
GLStateManager.setFfpInstancing(Instancing.CUBE);
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, UnitCubeMesh.VERTEX_COUNT, specs.length);
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -244,10 +244,10 @@ public final class FfpFixture {
|
||||
}
|
||||
|
||||
public static void resetFfpState() {
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
if (sm.isActive()) sm.deactivate();
|
||||
sm.disable();
|
||||
ShaderManager.disable();
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
|
||||
+2
-2
@@ -184,9 +184,9 @@ public final class ParticleParityFixture {
|
||||
ParticleInstancedAttribs.pointInstanceAttribs(0L);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
GLStateManager.ffpInstancing = Instancing.PARTICLE;
|
||||
GLStateManager.setFfpInstancing(Instancing.PARTICLE);
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, ParticleQuadMesh.VERTEX_COUNT, particles.length);
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.gtnewhorizons.angelica.glsm.testutil;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public final class TestThreads {
|
||||
|
||||
private static final long JOIN_TIMEOUT_MS = 10_000;
|
||||
|
||||
private TestThreads() {}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Body {
|
||||
void run() throws Throwable;
|
||||
}
|
||||
|
||||
public static final class Worker {
|
||||
private final Thread thread;
|
||||
private final AtomicReference<Throwable> error = new AtomicReference<>();
|
||||
|
||||
private Worker(String name, Body body) {
|
||||
thread = new Thread(() -> {
|
||||
try {
|
||||
body.run();
|
||||
} catch (Throwable t) {
|
||||
error.set(t);
|
||||
}
|
||||
}, name);
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public Throwable error() {
|
||||
return error.get();
|
||||
}
|
||||
|
||||
public void join() throws InterruptedException {
|
||||
thread.join(JOIN_TIMEOUT_MS);
|
||||
assertFalse(thread.isAlive(), thread.getName() + " did not finish");
|
||||
if (error.get() != null) fail(error.get());
|
||||
}
|
||||
}
|
||||
|
||||
public static Worker start(String name, Body body) {
|
||||
return new Worker(name, body);
|
||||
}
|
||||
|
||||
public static void run(String name, Body body) throws InterruptedException {
|
||||
start(name, body).join();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.compute.VoxelizationDispatcher;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.CaptureGate;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.GLDebugMessageListener;
|
||||
@@ -555,14 +556,34 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
@Override public boolean handleMakeCurrent(Object drawable) {
|
||||
if (!isSDLManagedDrawable(drawable)) return false;
|
||||
Lwjgl3GLCapabilitiesShim.installOnCurrentThread(advertisedCapabilities());
|
||||
final OffscreenTarget splash = splashTarget;
|
||||
if (splash != null && Thread.currentThread() != GLStateManager.getMainThread()) {
|
||||
s().boundFboId = splash.fboId();
|
||||
frameManager.frame().swapchainUnavailable = true;
|
||||
}
|
||||
enterContext(drawable instanceof SDLDrawable);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void enterContext(boolean displayDrawable) {
|
||||
final ContextState st = s();
|
||||
final GLContextState ctx = GLStateManager.ctx();
|
||||
if (st.mirroredContext != ctx) {
|
||||
st.invalidateMirror();
|
||||
st.mirroredContext = ctx;
|
||||
}
|
||||
final OffscreenTarget splash = splashTarget;
|
||||
final boolean splashContext = displayDrawable && splash != null && Thread.currentThread() != GLStateManager.getMainThread();
|
||||
st.defaultFboId = splashContext ? splash.fboId() : 0;
|
||||
if (splashContext && !frameManager.isFrameActive()) {
|
||||
beginSplashFrame();
|
||||
} else if (st.needsSeed) {
|
||||
st.needsSeed = false;
|
||||
GLStateManager.replayStateToBackend();
|
||||
}
|
||||
}
|
||||
|
||||
private void beginSplashFrame() {
|
||||
frameManager.beginFrame();
|
||||
beginFrameInit();
|
||||
frameManager.frame().swapchainUnavailable = true;
|
||||
}
|
||||
|
||||
@Override public boolean handleReleaseContext(Object drawable) {
|
||||
if (!isSDLManagedDrawable(drawable)) return false;
|
||||
if (Thread.currentThread() == GLStateManager.getMainThread()) return true;
|
||||
@@ -583,9 +604,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
endFrameUploadFlush();
|
||||
frameManager.endFrame();
|
||||
SplashDispatcher.signalFrameReady((int) s().viewportW, (int) s().viewportH);
|
||||
frameManager.beginFrame();
|
||||
beginFrameInit();
|
||||
frameManager.frame().swapchainUnavailable = true;
|
||||
beginSplashFrame();
|
||||
return true;
|
||||
}
|
||||
frameManager.presentFrame();
|
||||
@@ -1726,13 +1745,16 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
|
||||
@Override public void deleteFramebuffers(int framebuffer) {
|
||||
final ContextState cs = s();
|
||||
final boolean drawBound = cs.boundFboId == framebuffer;
|
||||
final boolean readBound = cs.boundReadFboId == framebuffer;
|
||||
resourceManager.deleteFbo(framebuffer);
|
||||
if (cs.boundFboId == framebuffer) cs.boundFboId = 0;
|
||||
if (cs.boundReadFboId == framebuffer) cs.boundReadFboId = 0;
|
||||
if (drawBound) bindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, 0);
|
||||
if (readBound) bindFramebuffer(GL30.GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
@Override public void bindFramebuffer(int target, int framebuffer) {
|
||||
final ContextState cs = s();
|
||||
if (framebuffer == 0) framebuffer = cs.defaultFboId;
|
||||
final boolean isRead = target == GL30.GL_READ_FRAMEBUFFER;
|
||||
final boolean isDraw = target == GL30.GL_DRAW_FRAMEBUFFER;
|
||||
final boolean isBoth = !isRead && !isDraw;
|
||||
@@ -1771,7 +1793,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
|
||||
@Override public void framebufferTexture2D(int target, int attachment, int textarget, int texture, int level) {
|
||||
final ContextState cs = s();
|
||||
if (cs.boundFboId == 0) return;
|
||||
if (cs.boundFboId == cs.defaultFboId) return;
|
||||
final FboState fbo = resourceManager.getFbo(cs.boundFboId);
|
||||
if (fbo == null) return;
|
||||
|
||||
@@ -1852,7 +1874,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
|
||||
@Override public void drawBuffers(int buffer) {
|
||||
final ContextState cs = s();
|
||||
if (cs.boundFboId == 0) return;
|
||||
if (cs.boundFboId == cs.defaultFboId) return;
|
||||
final FboState fbo = resourceManager.getFbo(cs.boundFboId);
|
||||
if (fbo == null) return;
|
||||
|
||||
@@ -1875,7 +1897,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
@Override public void drawBuffers(IntBuffer bufs) {
|
||||
final ContextState cs = s();
|
||||
if (cs.boundFboId == 0) return;
|
||||
if (cs.boundFboId == cs.defaultFboId) return;
|
||||
final FboState fbo = resourceManager.getFbo(cs.boundFboId);
|
||||
if (fbo == null) return;
|
||||
applyDrawBuffersFromIntBuffer(fbo, bufs, true);
|
||||
@@ -1911,8 +1933,9 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
}
|
||||
@Override public void readBuffer(int mode) {
|
||||
final int fboId = s().boundReadFboId;
|
||||
if (fboId == 0) return;
|
||||
final ContextState cs = s();
|
||||
final int fboId = cs.boundReadFboId;
|
||||
if (fboId == cs.defaultFboId) return;
|
||||
final FboState fbo = resourceManager.getFbo(fboId);
|
||||
if (fbo == null) return;
|
||||
if (mode >= GL30.GL_COLOR_ATTACHMENT0 && mode < GL30.GL_COLOR_ATTACHMENT0 + ContextState.MAX_COLOR_ATTACHMENTS) {
|
||||
@@ -2047,6 +2070,10 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
final int colorFormat = cachedSwapchainFormatArray != null ? cachedSwapchainFormatArray[0] : 0;
|
||||
return defaultFramebufferAttachmentParameteri(attachment, pname, colorFormat, resourceManager.getSwapchainDepthStencilFormat());
|
||||
}
|
||||
if (fboId == cs.defaultFboId) {
|
||||
final FboState fbo = resourceManager.getFbo(fboId);
|
||||
return defaultFramebufferAttachmentParameteri(attachment, pname, fbo.colorFormats[0], fbo.depthFormat);
|
||||
}
|
||||
return fboAttachmentParameteri(resourceManager.getFbo(fboId), attachment, pname);
|
||||
}
|
||||
|
||||
@@ -3720,8 +3747,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
case GL43.GL_MAX_DEBUG_GROUP_STACK_DEPTH -> 64;
|
||||
case GL43.GL_MAX_LABEL_LENGTH -> 256;
|
||||
case GL43.GL_MAX_DEBUG_MESSAGE_LENGTH -> 1024;
|
||||
case GL11.GL_DRAW_BUFFER -> drawBufferEnum(cs.boundFboId);
|
||||
case GL11.GL_READ_BUFFER -> readBufferEnum(cs.boundReadFboId);
|
||||
case GL11.GL_DRAW_BUFFER -> drawBufferEnum(cs.boundFboId, cs.defaultFboId);
|
||||
case GL11.GL_READ_BUFFER -> readBufferEnum(cs.boundReadFboId, cs.defaultFboId);
|
||||
case GL11.GL_TEXTURE_BINDING_2D -> boundTextureOf(cs.activeTextureUnit, cs.boundTextures);
|
||||
case GL11.GL_DEPTH_BITS -> getFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL11.GL_DEPTH, GL30.GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE);
|
||||
case GL11.GL_STENCIL_BITS -> getFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL11.GL_STENCIL, GL30.GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE);
|
||||
@@ -3738,15 +3765,15 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
return GL30.GL_COLOR_ATTACHMENT0 + attachmentIndex;
|
||||
}
|
||||
|
||||
private int drawBufferEnum(int fboId) {
|
||||
if (fboId == 0) return bufferEnum(0, 0);
|
||||
private int drawBufferEnum(int fboId, int defaultFboId) {
|
||||
if (fboId == defaultFboId) return bufferEnum(0, 0);
|
||||
final FboState fbo = resourceManager.getFbo(fboId);
|
||||
final int idx = (fbo == null || fbo.drawBuffers.length == 0) ? -1 : fbo.drawBuffers[0];
|
||||
return bufferEnum(fboId, idx);
|
||||
}
|
||||
|
||||
private int readBufferEnum(int fboId) {
|
||||
if (fboId == 0) return bufferEnum(0, 0);
|
||||
private int readBufferEnum(int fboId, int defaultFboId) {
|
||||
if (fboId == defaultFboId) return bufferEnum(0, 0);
|
||||
final FboState fbo = resourceManager.getFbo(fboId);
|
||||
return bufferEnum(fboId, fbo == null ? -1 : fbo.readBufferIndex);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.frame;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SDLGPURenderBackend;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineCache;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
|
||||
@@ -328,6 +329,8 @@ public final class ContextState {
|
||||
|
||||
public int boundFboId;
|
||||
public int boundReadFboId;
|
||||
public int defaultFboId;
|
||||
public GLContextState mirroredContext;
|
||||
|
||||
public final LongOpenHashSet clearedTexturesThisFrame = new LongOpenHashSet();
|
||||
public final LongOpenHashSet clearedStencilTexturesThisFrame = new LongOpenHashSet();
|
||||
|
||||
@@ -53,7 +53,7 @@ public final class OffscreenTarget {
|
||||
public void markContent() { hasContent = true; }
|
||||
|
||||
public boolean isFor(ContextState st) {
|
||||
return fboGlId != 0 && st.boundFboId == fboGlId;
|
||||
return fboGlId != 0 && st.defaultFboId == fboGlId;
|
||||
}
|
||||
|
||||
public void destroy(ResourceManager rm) {
|
||||
|
||||
@@ -47,7 +47,7 @@ class GlsmSdlFfpTest {
|
||||
|
||||
@AfterEach
|
||||
void cleanupInstancing() {
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
CubeParityFixture.deleteResources();
|
||||
ParticleParityFixture.deleteResources();
|
||||
FfpFixture.IntegerInstances.delete();
|
||||
|
||||
+1
-2
@@ -51,7 +51,6 @@ public final class GlsmSdlHeadlessRig {
|
||||
GLStateManager.initialize(GLSMInitConfig.builder()
|
||||
.displaySize(SIZE, SIZE)
|
||||
.directDrawer(TessellatorStreamingDrawer::drawDirect)
|
||||
.streamingDrawerDestroy(TessellatorStreamingDrawer::destroy)
|
||||
.build());
|
||||
GLStateManager.setRunningSplash(false);
|
||||
GLStateManager.markSplashComplete("glsmSdlHeadlessRig");
|
||||
@@ -92,7 +91,7 @@ public final class GlsmSdlHeadlessRig {
|
||||
return bound.get(target);
|
||||
}
|
||||
|
||||
private static int colorTargetSdlFormat() {
|
||||
static int colorTargetSdlFormat() {
|
||||
final ResourceManager rm = Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager");
|
||||
final ResourceManager.TextureMeta meta = rm.getTextureMeta(colorTexture);
|
||||
return meta != null ? meta.sdlFormat() : 0;
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.TestThreads;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SDLGPURenderBackend;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.device.Device;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.OffscreenTarget;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.splash.SplashDispatcher;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
@Tag("glsm-sdl")
|
||||
@ExtendWith(GlsmSdlFrameExtension.class)
|
||||
class GlsmSdlSplashTargetTest {
|
||||
|
||||
@BeforeAll
|
||||
static void boot() {
|
||||
GlsmSdlHeadlessRig.boot();
|
||||
}
|
||||
|
||||
@Test
|
||||
void seededSplashContextDrawsIntoSplashTarget() throws Exception {
|
||||
final SDLGPURenderBackend backend = (SDLGPURenderBackend) BackendManager.RENDER_BACKEND;
|
||||
final OffscreenTarget target = createSplashTarget(backend);
|
||||
Reflect.set(backend, "splashTarget", target);
|
||||
try {
|
||||
TestThreads.run("SplashTarget-Seed-Thread", () -> {
|
||||
Reflect.invoke(backend, "enterContext", new Class<?>[]{boolean.class}, true);
|
||||
GlsmSdlHeadlessRig.solidQuad(0f, 1f, 0f);
|
||||
backend.handleSwapBuffers();
|
||||
backend.onRenderThreadReleased(Thread.currentThread());
|
||||
});
|
||||
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, target.fboId());
|
||||
GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTarget(), 0xFF00FF00, "splash target");
|
||||
} finally {
|
||||
releaseSplashTarget(backend, target);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindZeroOnSplashThreadTargetsSplash() throws Exception {
|
||||
final SDLGPURenderBackend backend = (SDLGPURenderBackend) BackendManager.RENDER_BACKEND;
|
||||
final OffscreenTarget target = createSplashTarget(backend);
|
||||
Reflect.set(backend, "splashTarget", target);
|
||||
try {
|
||||
GlsmSdlHeadlessRig.bindTarget();
|
||||
GlsmSdlHeadlessRig.clearTo(0f, 0f, 1f, 1f);
|
||||
TestThreads.run("SplashTarget-BindZero-Thread", () -> {
|
||||
Reflect.invoke(backend, "enterContext", new Class<?>[]{boolean.class}, true);
|
||||
GlsmSdlHeadlessRig.bindTarget();
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
GlsmSdlHeadlessRig.clearTo(1f, 0f, 0f, 1f);
|
||||
GlsmSdlHeadlessRig.solidQuad(0f, 1f, 0f);
|
||||
backend.handleSwapBuffers();
|
||||
backend.onRenderThreadReleased(Thread.currentThread());
|
||||
});
|
||||
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, target.fboId());
|
||||
GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTarget(), 0xFF00FF00, "splash target");
|
||||
|
||||
GlsmSdlHeadlessRig.bindTarget();
|
||||
GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTarget(), 0xFF0000FF, "rig target");
|
||||
} finally {
|
||||
releaseSplashTarget(backend, target);
|
||||
}
|
||||
}
|
||||
|
||||
private static OffscreenTarget createSplashTarget(SDLGPURenderBackend backend) {
|
||||
final Device device = Reflect.get(backend, "device");
|
||||
final ResourceManager resourceManager = Reflect.get(backend, "resourceManager");
|
||||
final int sdlFormat = GlsmSdlHeadlessRig.colorTargetSdlFormat();
|
||||
final OffscreenTarget target = new OffscreenTarget();
|
||||
target.create(device, resourceManager, GlsmSdlHeadlessRig.SIZE, GlsmSdlHeadlessRig.SIZE, sdlFormat);
|
||||
return target;
|
||||
}
|
||||
|
||||
private static void releaseSplashTarget(SDLGPURenderBackend backend, OffscreenTarget target) {
|
||||
Reflect.set(backend, "splashTarget", null);
|
||||
SplashDispatcher.reset();
|
||||
GlsmSdlHeadlessRig.bindTarget();
|
||||
final ResourceManager resourceManager = Reflect.get(backend, "resourceManager");
|
||||
target.destroy(resourceManager);
|
||||
}
|
||||
}
|
||||
-2
@@ -1,6 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.pipeline;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.FboState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PixelOps;
|
||||
@@ -35,7 +34,6 @@ class AttachmentClearTest {
|
||||
rig = SdlTestRig.acquireRealDevice();
|
||||
rig.resourceManager.cachePreferredDepthFormats();
|
||||
sm = new ShaderManager(rig.device);
|
||||
VAOManager.init(0);
|
||||
store = new PipelineStore(rig.device);
|
||||
attachmentClear = new AttachmentClear(rig.frameManager, store, sm);
|
||||
|
||||
|
||||
-2
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.sdlgpu.pipeline;
|
||||
|
||||
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -17,7 +16,6 @@ class PipelineCacheKeyTest {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
VAOManager.init(0);
|
||||
PipelineCache.setSwapchainFormats(new int[]{SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM});
|
||||
}
|
||||
|
||||
|
||||
-2
@@ -1,7 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.pipeline;
|
||||
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -31,7 +30,6 @@ class PipelineCacheVertexInputTest {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
VAOManager.init(0);
|
||||
PipelineCache.setSwapchainFormats(new int[]{SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM});
|
||||
store = new PipelineStore(null);
|
||||
}
|
||||
|
||||
-2
@@ -1,6 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.pipeline;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PixelOps;
|
||||
@@ -69,7 +68,6 @@ class StencilMaskGpuTest {
|
||||
rig = SdlTestRig.acquireRealDevice();
|
||||
rig.resourceManager.cachePreferredDepthFormats();
|
||||
sm = new ShaderManager(rig.device);
|
||||
VAOManager.init(0);
|
||||
store = new PipelineStore(rig.device);
|
||||
|
||||
colorTexture = rig.resourceManager.createTexture(9500, GL11.GL_TEXTURE_2D, GL11.GL_RGBA8, SIZE, SIZE, 1, 1);
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.gtnewhorizon.gtnhlib.util.font.FontRendering;
|
||||
import com.gtnewhorizon.gtnhlib.util.font.GlyphReplacements;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FFPVertexLighting;
|
||||
@@ -159,7 +160,6 @@ public class BatchingFontRenderer {
|
||||
|
||||
|
||||
// OpenGL objects (static, can be used between multiple BatchingFontRenderer)
|
||||
private static int fontVAO = 0;
|
||||
private static int vbo;
|
||||
private static IndexBuffer ebo;
|
||||
private static int lightmapSamplerProgram;
|
||||
@@ -169,6 +169,7 @@ public class BatchingFontRenderer {
|
||||
private static boolean streamingInitialized;
|
||||
private static int pendingBuffer;
|
||||
private static int pendingBase;
|
||||
private static GLContextState vaoContext;
|
||||
private static int vaoBuffer = -1;
|
||||
private static int vaoBase = -1;
|
||||
|
||||
@@ -219,7 +220,8 @@ public class BatchingFontRenderer {
|
||||
float v = 0.0f;
|
||||
int texture = 0;
|
||||
if (active) {
|
||||
scratchLightmapUv.set(GLSMConfig.lastBrightnessX, GLSMConfig.lastBrightnessY, 0.0f, 1.0f).mul(GLStateManager.getTextures().getTextureUnitMatrix(LIGHTMAP_TEX_UNIT));
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
scratchLightmapUv.set(glCtx.lastBrightnessX, glCtx.lastBrightnessY, 0.0f, 1.0f).mul(GLStateManager.getTextures().getTextureUnitMatrix(LIGHTMAP_TEX_UNIT));
|
||||
u = scratchLightmapUv.x;
|
||||
v = scratchLightmapUv.y;
|
||||
texture = unitBinding;
|
||||
@@ -620,11 +622,16 @@ public class BatchingFontRenderer {
|
||||
return;
|
||||
}
|
||||
|
||||
GLStateManager.beginForeignDraw();
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
flushDeferredTextInner();
|
||||
GLStateManager.beginForeignDraw();
|
||||
try {
|
||||
flushDeferredTextInner();
|
||||
} finally {
|
||||
GLStateManager.endForeignDraw();
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.endForeignDraw();
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -859,8 +866,15 @@ public class BatchingFontRenderer {
|
||||
lightmapSamplerProgram = fontShaderId;
|
||||
}
|
||||
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
if (vaoContext != glCtx) {
|
||||
vaoContext = glCtx;
|
||||
vaoBuffer = -1;
|
||||
}
|
||||
int fontVAO = glCtx.fontVao;
|
||||
if (fontVAO == 0) {
|
||||
fontVAO = GLStateManager.glGenVertexArrays();
|
||||
glCtx.fontVao = fontVAO;
|
||||
|
||||
GLStateManager.glBindVertexArray(fontVAO);
|
||||
ebo.bind();
|
||||
|
||||
@@ -308,9 +308,9 @@ public final class WeatherRenderer {
|
||||
WeatherInstancedAttribs.pointInstanceAttribs((long) first * WeatherInstancedAttribs.STRIDE);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
VAOManager.setCurrentVertexFlags(WeatherQuadMesh.VERTEX_FLAGS);
|
||||
if (deferred == null) GLStateManager.ffpInstancing = Instancing.WEATHER;
|
||||
if (deferred == null) GLStateManager.setFfpInstancing(Instancing.WEATHER);
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLE_FAN, 0, WeatherQuadMesh.VERTEX_COUNT, count);
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
|
||||
if (deferred != null) deferred.rebindCurrentPass();
|
||||
|
||||
@@ -333,7 +333,7 @@ public final class PlayerReflectionCapture {
|
||||
GLStateManager.glColorMask(false, false, false, false);
|
||||
GLStateManager.glDepthMask(false);
|
||||
try {
|
||||
QuadConverter.drawQuadsAsTriangles(0, VERTEX_COUNT);
|
||||
QuadConverter.drawQuadsAsTriangles(GLStateManager.ctx(), 0, VERTEX_COUNT);
|
||||
} finally {
|
||||
GLStateManager.popStateTo(maskStateDepth);
|
||||
GLStateManager.setModelViewMatrix(savedModelView);
|
||||
|
||||
@@ -345,11 +345,11 @@ public final class ParticleInstancer {
|
||||
ParticleInstancedAttribs.pointInstanceAttribs(base);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
try {
|
||||
if (ffp) GLStateManager.ffpInstancing = Instancing.PARTICLE;
|
||||
if (ffp) GLStateManager.setFfpInstancing(Instancing.PARTICLE);
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, ParticleQuadMesh.VERTEX_COUNT, count);
|
||||
draws++;
|
||||
} finally {
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -112,7 +112,7 @@ final class InstancedTemplateRenderer {
|
||||
int draws = 0;
|
||||
long recordOffset = ringBase;
|
||||
neutralizeExtendedAttribs();
|
||||
GLStateManager.ffpInstancing = Instancing.TEMPLATE;
|
||||
GLStateManager.setFfpInstancing(Instancing.TEMPLATE);
|
||||
for (int b = 0, n = liveBuckets.size(); b < n; b++) {
|
||||
final Bucket bucket = liveBuckets.get(b);
|
||||
final TemplateMesh mesh = bucket.mesh;
|
||||
@@ -130,7 +130,7 @@ final class InstancedTemplateRenderer {
|
||||
bucket.mesh = null;
|
||||
bucketPool.add(bucket);
|
||||
}
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
liveBuckets.clear();
|
||||
return draws;
|
||||
@@ -142,7 +142,7 @@ final class InstancedTemplateRenderer {
|
||||
int redrawTemplates(TexRun base, int colorABGR) {
|
||||
long recordOffset = base.uploadOffset;
|
||||
neutralizeExtendedAttribs();
|
||||
GLStateManager.ffpInstancing = Instancing.TEMPLATE;
|
||||
GLStateManager.setFfpInstancing(Instancing.TEMPLATE);
|
||||
for (int b = 0, n = base.uploadMeshes.size(); b < n; b++) {
|
||||
final TemplateMesh mesh = (TemplateMesh) base.uploadMeshes.get(b);
|
||||
final int count = base.uploadCounts.getInt(b);
|
||||
@@ -154,7 +154,7 @@ final class InstancedTemplateRenderer {
|
||||
GLStateManager.glEnableVertexAttribArray(InstancedAttribs.LOC_COLOR);
|
||||
recordOffset += (long) count * InstancedAttribs.STRIDE;
|
||||
}
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
return base.uploadMeshes.size();
|
||||
}
|
||||
@@ -195,9 +195,9 @@ final class InstancedTemplateRenderer {
|
||||
bindRingTo(UnitCubeMesh.vao(), UnitCubeMesh.VERTEX_FLAGS);
|
||||
CubeInstancedAttribs.pointInstanceAttribs(ringBase);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.ffpInstancing = Instancing.CUBE;
|
||||
GLStateManager.setFfpInstancing(Instancing.CUBE);
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, UnitCubeMesh.VERTEX_COUNT, run.instances);
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
return ringBase;
|
||||
}
|
||||
@@ -220,9 +220,9 @@ final class InstancedTemplateRenderer {
|
||||
CubeInstancedAttribs.pointInstanceAttribs(offset);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
setConstantColor(colorABGR);
|
||||
GLStateManager.ffpInstancing = Instancing.CUBE;
|
||||
GLStateManager.setFfpInstancing(Instancing.CUBE);
|
||||
GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, UnitCubeMesh.VERTEX_COUNT, instances);
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
GLStateManager.glEnableVertexAttribArray(InstancedAttribs.LOC_COLOR);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
-5
@@ -10,11 +10,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
@Mixin(value = cpw.mods.fml.client.SplashProgress.class, remap = false)
|
||||
public class MixinSplashProgressCaching {
|
||||
|
||||
@Inject(method = "start", at = @At("TAIL"))
|
||||
private static void angelica$bindSharedDrawableVAO(CallbackInfo ci) {
|
||||
FrameHooks.bindSplashVao();
|
||||
}
|
||||
|
||||
@Inject(method = "finish", at = @At("RETURN"))
|
||||
private static void angelica$enableCachingOnFinish(CallbackInfo ci) {
|
||||
FrameHooks.splashFinished();
|
||||
|
||||
+2
-2
@@ -149,7 +149,7 @@ class PlayerReflectionSsboMappingTest {
|
||||
|
||||
GLStateManager.glUseProgram(program);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
QuadConverter.drawQuadsAsTriangles(0, PlayerReflectionCapture.VERTEX_COUNT);
|
||||
QuadConverter.drawQuadsAsTriangles(GLStateManager.ctx(), 0, PlayerReflectionCapture.VERTEX_COUNT);
|
||||
|
||||
final ByteBuffer ssboData = readback(ssbo, SSBO_BYTES);
|
||||
final int floats = PlayerReflectionLayoutTest.FLOATS;
|
||||
@@ -176,7 +176,7 @@ class PlayerReflectionSsboMappingTest {
|
||||
|
||||
GLStateManager.glUseProgram(program);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
QuadConverter.drawQuadsAsTriangles(0, PlayerReflectionCapture.VERTEX_COUNT);
|
||||
QuadConverter.drawQuadsAsTriangles(GLStateManager.ctx(), 0, PlayerReflectionCapture.VERTEX_COUNT);
|
||||
|
||||
final ByteBuffer visitedData = readback(visitedSsbo, VISITED_BYTES);
|
||||
for (int vertexId = 0; vertexId < PlayerReflectionCapture.VERTEX_COUNT; vertexId++) {
|
||||
|
||||
+3
-3
@@ -98,7 +98,7 @@ class RetainedTesrGroupsGLTest {
|
||||
GLStateManager.glColor4f(1f, 1f, 1f, 1f);
|
||||
|
||||
final ShaderManager ffp = ShaderManager.getInstance();
|
||||
ffp.enable();
|
||||
ShaderManager.enable();
|
||||
ffp.activate();
|
||||
|
||||
source = new AngelicaBufferSource();
|
||||
@@ -111,7 +111,7 @@ class RetainedTesrGroupsGLTest {
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
skipClientArrays = false;
|
||||
GLStateManager.ffpInstancing = Instancing.NONE;
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
groups.clear();
|
||||
@@ -119,7 +119,7 @@ class RetainedTesrGroupsGLTest {
|
||||
ring.delete();
|
||||
final ShaderManager ffp = ShaderManager.getInstance();
|
||||
if (ffp.isActive()) ffp.deactivate();
|
||||
ffp.disable();
|
||||
ShaderManager.disable();
|
||||
if (baseTexture != 0) GLStateManager.glDeleteTextures(baseTexture);
|
||||
if (glintTexture != 0) GLStateManager.glDeleteTextures(glintTexture);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
|
||||
@@ -58,7 +58,7 @@ class TesrLayerStateTest {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
final ShaderManager ffp = ShaderManager.getInstance();
|
||||
if (ffp.isActive()) ffp.deactivate();
|
||||
ffp.disable();
|
||||
ShaderManager.disable();
|
||||
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.disableAlphaTest();
|
||||
@@ -213,7 +213,7 @@ class TesrLayerStateTest {
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
final ShaderManager ffp = ShaderManager.getInstance();
|
||||
ffp.enable();
|
||||
ShaderManager.enable();
|
||||
ffp.activate();
|
||||
|
||||
assertTrue(unitTexIdentity(), "a freshly loaded identity unit-0 matrix must read as identity");
|
||||
|
||||
@@ -176,7 +176,7 @@ class IrisInstancingGLTest {
|
||||
GLSMHooks.pendingProgramSelection = null;
|
||||
Reflect.setStatic(BatchEligibility.class, "depth", 0);
|
||||
Reflect.setStatic(BatchEligibility.class, "allowed", false);
|
||||
Reflect.setStatic(GLStateManager.class, "foreignDrawDepth", 0);
|
||||
GLStateManager.ctx().foreignDrawDepth = 0;
|
||||
}
|
||||
|
||||
private static DeferredWorldRenderingPipeline newPipeline(WorldRenderingPhase phase, boolean isMainBound) {
|
||||
|
||||
-5
@@ -10,11 +10,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
@Mixin(value = cpw.mods.fml.client.SplashProgress.class, remap = false)
|
||||
public class MixinSplashProgressCaching {
|
||||
|
||||
@Inject(method = "start", at = @At("TAIL"))
|
||||
private static void umbra$bindSharedDrawableVAO(CallbackInfo ci) {
|
||||
FrameHooks.bindSplashVao();
|
||||
}
|
||||
|
||||
@Inject(method = "finish", at = @At("RETURN"))
|
||||
private static void umbra$enableCachingOnFinish(CallbackInfo ci) {
|
||||
FrameHooks.splashFinished();
|
||||
|
||||
Reference in new issue
Block a user