mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa71eda3db | ||
|
|
9705722469 | ||
|
|
977db3d604 | ||
|
|
c10d30b2da | ||
|
|
2f4ac2a9b7 | ||
|
|
9081f4c3e8 | ||
|
|
1f274a64ea | ||
|
|
9814a96b10 | ||
|
|
af599f292e | ||
|
|
7bb5bfd45c | ||
|
|
74ea5c4709 | ||
|
|
233c74e0d7 | ||
|
|
e17b45c842 | ||
|
|
c9d46e70e0 | ||
|
|
a8c29fa3f9 | ||
|
|
7c28a69bfe | ||
|
|
7f3fb35ebb | ||
|
|
44c930f4ab | ||
|
|
b76284048a | ||
|
|
762c5661eb |
No files matched your search
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds the libTracyClient shared library for the tracy-client module.
|
||||
# Builds the libTracyClient and AngelicaTracyCapture shared libraries for the tracy-client module.
|
||||
set -euo pipefail
|
||||
|
||||
TRACY_VERSION="${TRACY_VERSION:-v0.14.0}"
|
||||
TRACY_VERSION="${TRACY_VERSION:-v0.14.1}"
|
||||
TRACY_VERSION_BARE="${TRACY_VERSION#v}"
|
||||
OUT_DIR="${OUT_DIR:-dist/tracy-natives}"
|
||||
TRACY_REPO="https://github.com/wolfpld/tracy"
|
||||
@@ -31,36 +31,95 @@ else
|
||||
git clone --depth 1 --branch "$TRACY_VERSION" "$TRACY_REPO" "$SRC_DIR"
|
||||
fi
|
||||
|
||||
BUILD_DIR="$WORK_DIR/build"
|
||||
export MACOSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET:-11.0}"
|
||||
|
||||
cmake -S "$REPO_ROOT/tracy-client/src/main/native" -B "$BUILD_DIR" \
|
||||
CLIENT_BUILD_DIR="$WORK_DIR/build-client"
|
||||
cmake -S "$REPO_ROOT/tracy-client/src/main/native" -B "$CLIENT_BUILD_DIR" \
|
||||
-DTRACY_SRC_DIR="$SRC_DIR" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DTRACY_STATIC=OFF \
|
||||
-DTRACY_ENABLE=ON \
|
||||
-DTRACY_ON_DEMAND=ON \
|
||||
-DTRACY_DELAYED_INIT=ON \
|
||||
-DTRACY_MANUAL_LIFETIME=ON \
|
||||
-DTRACY_NO_SAMPLING=ON \
|
||||
-DTRACY_NO_SYSTEM_TRACING=ON \
|
||||
-DTRACY_NO_CRASH_HANDLER=ON \
|
||||
-DTRACY_LTO=OFF
|
||||
cmake --build "$BUILD_DIR" --config Release --parallel
|
||||
cmake --build "$CLIENT_BUILD_DIR" --config Release --parallel
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
CPM_CACHE_DIR="$WORK_DIR/cpm"
|
||||
CAPTURE_BUILD_DIR="$WORK_DIR/build-capture"
|
||||
cmake -S "$REPO_ROOT/tracy-client/src/main/native-capture" -B "$CAPTURE_BUILD_DIR" \
|
||||
-DTRACY_SRC_DIR="$SRC_DIR" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCPM_SOURCE_CACHE="$CPM_CACHE_DIR"
|
||||
cmake --build "$CAPTURE_BUILD_DIR" --config Release --parallel
|
||||
|
||||
find_one() {
|
||||
find "$1" -name "$2" 2>/dev/null | head -1
|
||||
}
|
||||
|
||||
CLIENT_LIB_NAME=""
|
||||
CLIENT_LIB=""
|
||||
for NAME in libTracyClient.so libTracyClient.dylib TracyClient.dll; do
|
||||
LIB="$(find "$BUILD_DIR" -name "$NAME" | head -1)"
|
||||
LIB="$(find_one "$CLIENT_BUILD_DIR" "$NAME")"
|
||||
if [[ -n "$LIB" ]]; then
|
||||
cp -L "$LIB" "$OUT_DIR/$NAME"
|
||||
cp "$SRC_DIR/LICENSE" "$OUT_DIR/LICENSE"
|
||||
mkdir -p "$RES_DIR"
|
||||
cp -L "$LIB" "$RES_DIR/$NAME"
|
||||
cp "$SRC_DIR/LICENSE" "$RES_DIR/LICENSE"
|
||||
echo "built: $OUT_DIR/$NAME ($TRACY_VERSION), staged: $RES_DIR/$NAME"
|
||||
exit 0
|
||||
CLIENT_LIB="$LIB"
|
||||
CLIENT_LIB_NAME="$NAME"
|
||||
break
|
||||
fi
|
||||
done
|
||||
echo "error: built library not found under $BUILD_DIR" >&2
|
||||
exit 1
|
||||
if [[ -z "$CLIENT_LIB" ]]; then
|
||||
echo "error: libTracyClient not found under $CLIENT_BUILD_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CAPTURE_LIB_NAME=""
|
||||
CAPTURE_LIB=""
|
||||
for NAME in libAngelicaTracyCapture.so libAngelicaTracyCapture.dylib AngelicaTracyCapture.dll; do
|
||||
LIB="$(find_one "$CAPTURE_BUILD_DIR" "$NAME")"
|
||||
if [[ -n "$LIB" ]]; then
|
||||
CAPTURE_LIB="$LIB"
|
||||
CAPTURE_LIB_NAME="$NAME"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ -z "$CAPTURE_LIB" ]]; then
|
||||
echo "error: AngelicaTracyCapture not found under $CAPTURE_BUILD_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
STRIPPED_CAPTURE_LIB="$WORK_DIR/$CAPTURE_LIB_NAME"
|
||||
cp -L "$CAPTURE_LIB" "$STRIPPED_CAPTURE_LIB"
|
||||
case "$PLAT_OS" in
|
||||
macos) strip -x "$STRIPPED_CAPTURE_LIB" ;;
|
||||
linux) strip --strip-unneeded "$STRIPPED_CAPTURE_LIB" ;;
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
LICENSE_ZSTD="$(find_one "$CPM_CACHE_DIR/zstd" LICENSE)"
|
||||
LICENSE_CAPSTONE="$(find_one "$CPM_CACHE_DIR/capstone" LICENSE.TXT)"
|
||||
LICENSE_PPQSORT="$(find_one "$CPM_CACHE_DIR/ppqsort" LICENSE)"
|
||||
|
||||
LICENSE_NAMES=(LICENSE LICENSE-zstd LICENSE-capstone LICENSE-ppqsort)
|
||||
LICENSE_SRCS=("$SRC_DIR/LICENSE" "$LICENSE_ZSTD" "$LICENSE_CAPSTONE" "$LICENSE_PPQSORT")
|
||||
|
||||
for i in "${!LICENSE_NAMES[@]}"; do
|
||||
if [[ -z "${LICENSE_SRCS[$i]}" || ! -f "${LICENSE_SRCS[$i]}" ]]; then
|
||||
echo "error: ${LICENSE_NAMES[$i]} source not found under $CPM_CACHE_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
mkdir -p "$OUT_DIR" "$RES_DIR"
|
||||
for DEST in "$OUT_DIR" "$RES_DIR"; do
|
||||
cp -L "$CLIENT_LIB" "$DEST/$CLIENT_LIB_NAME"
|
||||
cp -L "$STRIPPED_CAPTURE_LIB" "$DEST/$CAPTURE_LIB_NAME"
|
||||
for i in "${!LICENSE_NAMES[@]}"; do
|
||||
cp "${LICENSE_SRCS[$i]}" "$DEST/${LICENSE_NAMES[$i]}"
|
||||
done
|
||||
done
|
||||
|
||||
echo "built: $OUT_DIR/$CLIENT_LIB_NAME, $OUT_DIR/$CAPTURE_LIB_NAME ($TRACY_VERSION)"
|
||||
echo "staged: $RES_DIR/$CLIENT_LIB_NAME, $RES_DIR/$CAPTURE_LIB_NAME"
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Build libTracyClient
|
||||
- name: Build Tracy natives
|
||||
shell: bash
|
||||
env:
|
||||
OUT_DIR: dist/tracy-natives
|
||||
|
||||
@@ -162,6 +162,7 @@ DependencyHandlerScope.of(dependencies).apply {
|
||||
compileOnly(rfg.deobf("curse.maven:security-craft-64760:2818228"))
|
||||
compileOnly("ganymedes01.etfuturum:Et-Futurum-Requiem:2.6.49-GTNH:dev")
|
||||
compileOnly("com.github.GTNewHorizons:OpenBlocks:1.12.19-GTNH:dev")
|
||||
compileOnly("com.github.GTNewHorizons:Draconic-Evolution:1.5.30-GTNH:dev")
|
||||
compileOnly("com.github.GTNewHorizons:Chisel:2.17.31-GTNH:dev")
|
||||
compileOnly(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
|
||||
|
||||
@@ -201,6 +202,7 @@ afterEvaluate {
|
||||
testImplementation(libs.junit.platform.launcher)
|
||||
|
||||
testImplementation(libs.archunit.junit5)
|
||||
testImplementation(libs.retrofuturabootstrap) { isTransitive = false }
|
||||
|
||||
glsmTestFixtures(testFixtures(project(":glsm")) as ModuleDependency) { isTransitive = false }
|
||||
testCompileOnly(testFixtures(project(":glsm")))
|
||||
|
||||
@@ -40,10 +40,10 @@ public final class SystemProperties {
|
||||
public static final boolean SDL_DISABLE_IN_PASS_CLEAR = Boolean.getBoolean("angelica.sdlgpu.disableInPassClear");
|
||||
|
||||
// Tracy
|
||||
public static final boolean TRACY = Boolean.getBoolean("angelica.tracy");
|
||||
public static final boolean TRACY_FINE_ZONES = Boolean.getBoolean("angelica.tracy.fineZones");
|
||||
public static final Boolean TRACY_OVERRIDE = parseBooleanOverride("angelica.tracy");
|
||||
public static final Boolean TRACY_FINE_ZONES_OVERRIDE = parseBooleanOverride("angelica.tracy.fineZones");
|
||||
public static final String TRACY_DIR = System.getProperty("angelica.tracy.dir", "angelica" + File.separator + "natives" + File.separator + "tracy");
|
||||
public static final int TRACY_MAX_SRC_LOCS = Math.max(16, Integer.getInteger("angelica.tracy.maxSrcLocs", 4096));
|
||||
public static final Integer TRACY_MAX_SRC_LOCS_OVERRIDE = Integer.getInteger("angelica.tracy.maxSrcLocs");
|
||||
|
||||
// Profiling
|
||||
public static final String PROFILE_OPTS = System.getProperty("angelica.profile.opts", "event=wall,interval=5ms,alloc=512k,lock=10ms");
|
||||
@@ -152,6 +152,11 @@ public final class SystemProperties {
|
||||
}
|
||||
}
|
||||
|
||||
private static Boolean parseBooleanOverride(String key) {
|
||||
final String raw = System.getProperty(key);
|
||||
return raw == null ? null : Boolean.parseBoolean(raw);
|
||||
}
|
||||
|
||||
private static double parseDouble(String key) {
|
||||
final String raw = System.getProperty(key);
|
||||
if (raw == null || raw.isEmpty()) return 0.0D;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -86,7 +86,6 @@ public class DisplayListManager {
|
||||
private static List<AccumulatedDraw> accumulatedDraws = null; // Accumulates quad draws for batching
|
||||
private static AccumulatedDraw pendingDraw = null;
|
||||
private static DisplayListCallback transformCallback = null;
|
||||
private static int relativeTransformType;
|
||||
|
||||
private static final int TRANSFORM_SCALE = 0x1; // Can be extracted to a glScale easily
|
||||
private static final int TRANSFORM_TRANSLATE = 0x2; // Can be extracted to a glTranslate easily
|
||||
@@ -95,7 +94,7 @@ public class DisplayListManager {
|
||||
private static StackTraceElement[] compilationStackTrace = null; // For logging: captured at glNewList()
|
||||
|
||||
// Debug logging: track sources of MULT_MATRIX commands and draw origins; only populated when LOG_DISPLAY_LIST_COMPILATION is true
|
||||
private static List<String> pendingTransformOps = null; // Ops accumulated since last flush
|
||||
private static List<List<String>> pendingTransformOps = null; // Ops per matrix slot since its last flush
|
||||
private static List<List<String>> multMatrixSources = null; // Source ops for each MULT_MATRIX in raw buffer
|
||||
private static List<String> drawRangeSources = null; // Source type for each DRAW_RANGE in final buffer
|
||||
|
||||
@@ -119,7 +118,7 @@ public class DisplayListManager {
|
||||
int recordedStatePushes,
|
||||
|
||||
// Debug logging fields (only used when LOG_DISPLAY_LIST_COMPILATION)
|
||||
List<String> pendingOps,
|
||||
List<List<String>> pendingOps,
|
||||
List<List<String>> matrixSources,
|
||||
List<String> drawSources
|
||||
) {}
|
||||
@@ -144,7 +143,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 +152,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;
|
||||
}
|
||||
|
||||
@@ -171,7 +174,9 @@ public class DisplayListManager {
|
||||
|
||||
static void flushAll() {
|
||||
drawBarrier();
|
||||
flushMatrix();
|
||||
for (int slot = 0; slot < DisplayListCallback.SLOT_COUNT; slot++) {
|
||||
flushMatrix(slot);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -186,13 +191,21 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit accumulated transform as MultMatrix if non-identity, then reset.
|
||||
* Emit the current matrix target's accumulated transform as MultMatrix if non-identity, then reset.
|
||||
*/
|
||||
static void flushMatrix() {
|
||||
if (transformCallback.isIdentity()) {
|
||||
flushMatrix(transformCallback.getCurrentSlot());
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit one matrix target's accumulated transform as MultMatrix if non-identity, then reset.
|
||||
*/
|
||||
private static void flushMatrix(int slot) {
|
||||
if (transformCallback.isIdentity(slot)) {
|
||||
// Clear pending ops even if we don't emit - they were no-ops (identity)
|
||||
transformCallback.reset(slot);
|
||||
if (pendingTransformOps != null) {
|
||||
pendingTransformOps.clear();
|
||||
pendingTransformOps.get(slot).clear();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -200,44 +213,60 @@ public class DisplayListManager {
|
||||
|
||||
// Save pending transform ops for logging (before we clear them)
|
||||
if (multMatrixSources != null && pendingTransformOps != null) {
|
||||
if (pendingTransformOps.isEmpty()) {
|
||||
final List<String> ops = pendingTransformOps.get(slot);
|
||||
if (ops.isEmpty()) {
|
||||
GLStateManager.LOGGER.warn("flushMatrix: non-identity transform with no tracked ops");
|
||||
multMatrixSources.add(Collections.singletonList("(unknown source)"));
|
||||
} else {
|
||||
multMatrixSources.add(new ArrayList<>(pendingTransformOps));
|
||||
multMatrixSources.add(new ArrayList<>(ops));
|
||||
}
|
||||
pendingTransformOps.clear();
|
||||
ops.clear();
|
||||
}
|
||||
|
||||
// Record the collapsed MultMatrix command (for playback)
|
||||
// Always put a draw barrier BEFORE flushing the matrix (the transforms are already baked into the current draw)
|
||||
drawBarrier();
|
||||
if (relativeTransformType == TRANSFORM_SCALE) {
|
||||
currentRecorder.writeScale(transformCallback.getScale(transformVector));
|
||||
} else if (relativeTransformType == TRANSFORM_TRANSLATE) {
|
||||
currentRecorder.writeTranslate(transformCallback.getTranslation(transformVector));
|
||||
syncStreamMatrixMode(transformCallback.getSlotMatrixMode(slot));
|
||||
final Matrix4f pending = transformCallback.getReadMatrix(slot);
|
||||
final int type = transformCallback.getTransformType(slot);
|
||||
if (type == TRANSFORM_SCALE) {
|
||||
currentRecorder.writeScale(transformCallback.getScale(slot, transformVector));
|
||||
} else if (type == TRANSFORM_TRANSLATE) {
|
||||
currentRecorder.writeTranslate(transformCallback.getTranslation(slot, transformVector));
|
||||
} else {
|
||||
currentRecorder.writeMultMatrix(transformCallback.getReadMatrix());
|
||||
currentRecorder.writeMultMatrix(pending);
|
||||
}
|
||||
if (glListMode == GL11.GL_COMPILE_AND_EXECUTE) {
|
||||
GLStateManager.applyMultMatrix(transformCallback.getReadMatrix());
|
||||
GLStateManager.applyMultMatrix(pending);
|
||||
}
|
||||
|
||||
|
||||
// Reset to identity - we're now synchronized with GL
|
||||
resetRelativeTransform();
|
||||
transformCallback.reset(slot);
|
||||
}
|
||||
|
||||
private static void syncStreamMatrixMode(int mode) {
|
||||
if (transformCallback.getStreamMatrixMode() == 0 && !transformCallback.explicitMatrixMode) {
|
||||
return;
|
||||
}
|
||||
if (transformCallback.getStreamMatrixMode() == mode) {
|
||||
return;
|
||||
}
|
||||
drawBarrier();
|
||||
transformCallback.setStreamMatrixMode(mode);
|
||||
currentRecorder.writeMatrixMode(mode);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -315,6 +344,11 @@ public class DisplayListManager {
|
||||
currentRecorder.writeSecondaryColor(r, g, b);
|
||||
}
|
||||
|
||||
public static void recordVertexAttrib(int index, float x, float y, float z, float w) {
|
||||
drawBarrier();
|
||||
currentRecorder.writeVertexAttrib(index, x, y, z, w);
|
||||
}
|
||||
|
||||
public static void recordColorMask(boolean r, boolean g, boolean b, boolean a) {
|
||||
drawBarrier();
|
||||
currentRecorder.writeColorMask(r, g, b, a);
|
||||
@@ -371,14 +405,37 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
public static void recordMatrixMode(int mode) {
|
||||
flushMatrix(); // Matrix barrier: flush and reset
|
||||
final int previous = transformCallback.getMatrixMode();
|
||||
if (!transformCallback.explicitMatrixMode) {
|
||||
// Pending transforms still inherit the caller's target until this explicit selection.
|
||||
flushMatrix();
|
||||
}
|
||||
transformCallback.explicitMatrixMode = true;
|
||||
if (previous == mode) {
|
||||
// The initial target and the target after a nested list are unknown at playback.
|
||||
syncStreamMatrixMode(mode);
|
||||
return;
|
||||
}
|
||||
if (glListMode == GL11.GL_COMPILE_AND_EXECUTE) {
|
||||
// Live GL follows the app's matrix mode, so a pending delta can only be replayed onto GL
|
||||
// while its own target is still current.
|
||||
flushAll();
|
||||
} else {
|
||||
// MODELVIEW and PROJECTION share the vertex delta, so a delta belonging to one of them has to
|
||||
// be flushed before the other starts adding to it.
|
||||
final int slot = DisplayListCallback.slotOf(mode);
|
||||
if (transformCallback.getSlotMatrixMode(slot) != mode) {
|
||||
flushMatrix(slot);
|
||||
}
|
||||
}
|
||||
transformCallback.setMatrixMode(mode);
|
||||
currentRecorder.writeMatrixMode(mode);
|
||||
}
|
||||
|
||||
public static void recordPushMatrix() {
|
||||
drawBarrier();
|
||||
// Flush any pending delta, then record push.
|
||||
flushMatrix();
|
||||
syncStreamMatrixMode(transformCallback.getMatrixMode());
|
||||
currentRecorder.writePushMatrix();
|
||||
}
|
||||
|
||||
@@ -386,6 +443,7 @@ public class DisplayListManager {
|
||||
// Discard any transformations & flush any pending draw operations
|
||||
drawBarrier();
|
||||
resetRelativeTransform();
|
||||
syncStreamMatrixMode(transformCallback.getMatrixMode());
|
||||
currentRecorder.writePopMatrix();
|
||||
}
|
||||
|
||||
@@ -435,6 +493,8 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
public static void recordActiveTexture(int texture) {
|
||||
drawBarrier();
|
||||
flushMatrix(DisplayListCallback.SLOT_TEXTURE);
|
||||
currentRecorder.writeActiveTexture(texture);
|
||||
}
|
||||
|
||||
@@ -443,15 +503,31 @@ public class DisplayListManager {
|
||||
currentRecorder.writeUseProgram(program);
|
||||
}
|
||||
|
||||
// PushAttrib saves state but doesn't change it - not a draw barrier
|
||||
private record AttribState(int matrixMode, boolean restoresTexture) {}
|
||||
|
||||
public static void recordPushAttrib(int mask) {
|
||||
final boolean savesMode = (mask & GL11.GL_TRANSFORM_BIT) != 0;
|
||||
if (savesMode) syncStreamMatrixMode(transformCallback.getMatrixMode());
|
||||
transformCallback.attribStates.push(new AttribState(
|
||||
savesMode ? transformCallback.getStreamMatrixMode() : -1, (mask & GL11.GL_TEXTURE_BIT) != 0));
|
||||
currentRecorder.writePushAttrib(mask);
|
||||
}
|
||||
|
||||
// PopAttrib restores potentially any state - draw barrier
|
||||
public static void recordPopAttrib() {
|
||||
drawBarrier();
|
||||
final AttribState restored = transformCallback.attribStates.poll();
|
||||
final int restoredMode = restored == null ? 0 : restored.matrixMode();
|
||||
if (restoredMode != -1) flushAll();
|
||||
else {
|
||||
drawBarrier();
|
||||
if (restored.restoresTexture()) flushMatrix(DisplayListCallback.SLOT_TEXTURE);
|
||||
}
|
||||
currentRecorder.writePopAttrib();
|
||||
if (restoredMode != -1) {
|
||||
transformCallback.setMatrixMode(restoredMode == 0 ? GL11.GL_MODELVIEW : restoredMode);
|
||||
transformCallback.setStreamMatrixMode(restoredMode);
|
||||
transformCallback.explicitMatrixMode = restoredMode != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static int virtualStateDepth(RecordMode mode) {
|
||||
@@ -571,7 +647,10 @@ public class DisplayListManager {
|
||||
public static void recordCallList(int listId) {
|
||||
// Flush all pending draws & transformations
|
||||
flushAll();
|
||||
syncStreamMatrixMode(transformCallback.getMatrixMode());
|
||||
currentRecorder.writeCallList(listId);
|
||||
transformCallback.setStreamMatrixMode(0);
|
||||
transformCallback.explicitMatrixMode = false;
|
||||
}
|
||||
|
||||
public static void recordDrawBuffer(int mode) {
|
||||
@@ -594,6 +673,19 @@ public class DisplayListManager {
|
||||
currentRecorder.writeComplexCommand(cmd);
|
||||
}
|
||||
|
||||
public static void recordStateCommand(DisplayListCommand cmd) {
|
||||
drawBarrier();
|
||||
currentRecorder.writeComplexCommand(cmd);
|
||||
}
|
||||
|
||||
public static void recordStateCommandIfChanged(DisplayListCommand cmd) {
|
||||
if (!currentRecorder.isLastCommand(cmd)) recordStateCommand(cmd);
|
||||
}
|
||||
|
||||
public static void recordStateCommandOnce(DisplayListCommand cmd) {
|
||||
if (currentRecorder.markRecordedOnce(cmd)) recordStateCommand(cmd);
|
||||
}
|
||||
|
||||
public static void recordIndexedDrawCapture(IndexedDrawCapture capture) {
|
||||
flushAll(); //TODO
|
||||
currentRecorder.writeIndexedDrawCapture(capture);
|
||||
@@ -602,12 +694,14 @@ public class DisplayListManager {
|
||||
public static void recordLoadMatrix(Matrix4f matrix) {
|
||||
drawBarrier();
|
||||
resetRelativeTransform();
|
||||
syncStreamMatrixMode(transformCallback.getMatrixMode());
|
||||
currentRecorder.writeLoadMatrix(matrix);
|
||||
}
|
||||
|
||||
public static void recordLoadIdentity() {
|
||||
drawBarrier();
|
||||
resetRelativeTransform();
|
||||
syncStreamMatrixMode(transformCallback.getMatrixMode());
|
||||
currentRecorder.writeLoadIdentity();
|
||||
|
||||
}
|
||||
@@ -645,11 +739,10 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
public static void applyMatrixTranslation(float x, float y, float z) {
|
||||
transformCallback.translate(x, y, z);
|
||||
relativeTransformType |= TRANSFORM_TRANSLATE;
|
||||
transformCallback.translate(x, y, z, TRANSFORM_TRANSLATE);
|
||||
|
||||
if (pendingTransformOps != null) {
|
||||
pendingTransformOps.add(String.format("glTranslatef(%.4f, %.4f, %.4f)", x, y, z));
|
||||
pendingTransformOps.get(transformCallback.getCurrentSlot()).add(String.format("glTranslatef(%.4f, %.4f, %.4f)", x, y, z));
|
||||
}
|
||||
|
||||
if (SystemProperties.DEBUG_DISPLAY_LISTS) {
|
||||
@@ -658,11 +751,10 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
public static void applyMatrixScale(float x, float y, float z) {
|
||||
transformCallback.scale(x, y, z);
|
||||
relativeTransformType |= TRANSFORM_SCALE;
|
||||
transformCallback.scale(x, y, z, TRANSFORM_SCALE);
|
||||
|
||||
if (pendingTransformOps != null) {
|
||||
pendingTransformOps.add(String.format("glScalef(%.4f, %.4f, %.4f)", x, y, z));
|
||||
pendingTransformOps.get(transformCallback.getCurrentSlot()).add(String.format("glScalef(%.4f, %.4f, %.4f)", x, y, z));
|
||||
}
|
||||
|
||||
if (SystemProperties.DEBUG_DISPLAY_LISTS) {
|
||||
@@ -676,11 +768,10 @@ public class DisplayListManager {
|
||||
* Requires the angle to be in radians & the coordinates to be normalized.
|
||||
*/
|
||||
public static void applyMatrixRotation(float rad, float x, float y, float z) {
|
||||
transformCallback.rotate(rad, x, y, z);
|
||||
relativeTransformType |= TRANSFORM_COMPLEX;
|
||||
transformCallback.rotate(rad, x, y, z, TRANSFORM_COMPLEX);
|
||||
|
||||
if (pendingTransformOps != null) {
|
||||
pendingTransformOps.add(String.format("glRotatef(%.4f, %.4f, %.4f, %.4f)", Math.toDegrees(rad), x, y, z));
|
||||
pendingTransformOps.get(transformCallback.getCurrentSlot()).add(String.format("glRotatef(%.4f, %.4f, %.4f, %.4f)", Math.toDegrees(rad), x, y, z));
|
||||
}
|
||||
|
||||
if (SystemProperties.DEBUG_DISPLAY_LISTS) {
|
||||
@@ -698,11 +789,10 @@ public class DisplayListManager {
|
||||
* @param matrix The matrix to multiply
|
||||
*/
|
||||
public static void updateRelativeTransform(Matrix4f matrix) {
|
||||
transformCallback.multMatrix(matrix);
|
||||
relativeTransformType |= TRANSFORM_COMPLEX;
|
||||
transformCallback.multMatrix(matrix, TRANSFORM_COMPLEX);
|
||||
|
||||
if (pendingTransformOps != null) {
|
||||
pendingTransformOps.add("glMultMatrixf(...)");
|
||||
pendingTransformOps.get(transformCallback.getCurrentSlot()).add("glMultMatrixf(...)");
|
||||
}
|
||||
|
||||
if (SystemProperties.DEBUG_DISPLAY_LISTS) {
|
||||
@@ -716,11 +806,10 @@ public class DisplayListManager {
|
||||
public static void updateRelativeTransformOrtho(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
orthoFrustumTemp.identity().ortho((float) left, (float) right, (float) bottom, (float) top, (float) zNear, (float) zFar);
|
||||
|
||||
transformCallback.multMatrix(orthoFrustumTemp);
|
||||
relativeTransformType |= TRANSFORM_COMPLEX;
|
||||
transformCallback.multMatrix(orthoFrustumTemp, TRANSFORM_COMPLEX);
|
||||
|
||||
if (pendingTransformOps != null) {
|
||||
pendingTransformOps.add(String.format("glOrtho(%.4f, %.4f, %.4f, %.4f, %.4f, %.4f)", left, right, bottom, top, zNear, zFar));
|
||||
pendingTransformOps.get(transformCallback.getCurrentSlot()).add(String.format("glOrtho(%.4f, %.4f, %.4f, %.4f, %.4f, %.4f)", left, right, bottom, top, zNear, zFar));
|
||||
}
|
||||
|
||||
if (SystemProperties.DEBUG_DISPLAY_LISTS) {
|
||||
@@ -731,11 +820,10 @@ public class DisplayListManager {
|
||||
public static void updateRelativeTransformFrustum(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
orthoFrustumTemp.identity().frustum((float) left, (float) right, (float) bottom, (float) top, (float) zNear, (float) zFar);
|
||||
|
||||
transformCallback.multMatrix(orthoFrustumTemp);
|
||||
relativeTransformType |= TRANSFORM_COMPLEX;
|
||||
transformCallback.multMatrix(orthoFrustumTemp, TRANSFORM_COMPLEX);
|
||||
|
||||
if (pendingTransformOps != null) {
|
||||
pendingTransformOps.add(String.format("glFrustum(%.4f, %.4f, %.4f, %.4f, %.4f, %.4f)", left, right, bottom, top, zNear, zFar));
|
||||
pendingTransformOps.get(transformCallback.getCurrentSlot()).add(String.format("glFrustum(%.4f, %.4f, %.4f, %.4f, %.4f, %.4f)", left, right, bottom, top, zNear, zFar));
|
||||
}
|
||||
|
||||
if (SystemProperties.DEBUG_DISPLAY_LISTS) {
|
||||
@@ -749,8 +837,8 @@ public class DisplayListManager {
|
||||
* subsequent transforms are relative to that loaded matrix (i.e., start from identity).
|
||||
*/
|
||||
static void resetRelativeTransform() {
|
||||
transformCallback.setIdentity();
|
||||
relativeTransformType = 0;
|
||||
transformCallback.reset(transformCallback.getCurrentSlot());
|
||||
if (pendingTransformOps != null) pendingTransformOps.get(transformCallback.getCurrentSlot()).clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -761,8 +849,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 +869,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 +883,103 @@ 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<>();
|
||||
for (int slot = 0; slot < DisplayListCallback.SLOT_COUNT; slot++) {
|
||||
pendingTransformOps.add(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) {
|
||||
@@ -862,10 +988,8 @@ public class DisplayListManager {
|
||||
|
||||
flushAll();
|
||||
|
||||
// Reset back to MODELVIEW (default state)
|
||||
if (transformCallback.getMatrixMode() != GL11.GL_MODELVIEW) {
|
||||
recordMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
transformCallback.setMatrixMode(GL11.GL_MODELVIEW);
|
||||
syncStreamMatrixMode(GL11.GL_MODELVIEW);
|
||||
|
||||
final CompiledDisplayList compiled;
|
||||
// Create CompiledDisplayList with both unoptimized and optimized versions
|
||||
@@ -966,7 +1090,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 +1100,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 +1134,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 +1170,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) {
|
||||
@@ -1357,12 +1484,32 @@ public class DisplayListManager {
|
||||
|
||||
private static final class DisplayListCallback extends VertexTransformCallback {
|
||||
|
||||
static final int SLOT_VERTEX = 0; // GL_MODELVIEW + GL_PROJECTION, feeds into gl_Position
|
||||
static final int SLOT_TEXTURE = 1;
|
||||
static final int SLOT_COLOR = 2;
|
||||
static final int SLOT_OTHER = 3;
|
||||
static final int SLOT_COUNT = 4;
|
||||
|
||||
static int slotOf(int mode) {
|
||||
return switch (mode) {
|
||||
case GL11.GL_MODELVIEW, GL11.GL_PROJECTION -> SLOT_VERTEX;
|
||||
case GL11.GL_TEXTURE -> SLOT_TEXTURE;
|
||||
case GL11.GL_COLOR -> SLOT_COLOR;
|
||||
default -> SLOT_OTHER;
|
||||
};
|
||||
}
|
||||
|
||||
private int matrixMode = GL11.GL_MODELVIEW;
|
||||
|
||||
private int streamMatrixMode;
|
||||
private boolean explicitMatrixMode;
|
||||
private final Deque<AttribState> attribStates = new ArrayDeque<>();
|
||||
|
||||
private final Matrix3f normalMatrix = new Matrix3f();
|
||||
|
||||
private final Matrix4f transformMatrix = new Matrix4f();
|
||||
private final Matrix4f[] transformMatrices = { new Matrix4f(), new Matrix4f(), new Matrix4f(), new Matrix4f() };
|
||||
private final int[] transformTypes = new int[SLOT_COUNT];
|
||||
private final int[] slotModes = { GL11.GL_MODELVIEW, GL11.GL_TEXTURE, GL11.GL_COLOR, 0 };
|
||||
|
||||
private DirectTessellator bakedOwner;
|
||||
private int bakedVertices;
|
||||
@@ -1374,49 +1521,69 @@ public class DisplayListManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void scale(float x, float y, float z) {
|
||||
transformMatrix.scale(x, y, z);
|
||||
private Matrix4f begin(int typeBits) {
|
||||
final int slot = getCurrentSlot();
|
||||
transformTypes[slot] |= typeBits;
|
||||
slotModes[slot] = matrixMode;
|
||||
return transformMatrices[slot];
|
||||
}
|
||||
|
||||
public void scale(float x, float y, float z, int typeBits) {
|
||||
begin(typeBits).scale(x, y, z);
|
||||
markDirty();
|
||||
}
|
||||
|
||||
public void translate(float x, float y, float z) {
|
||||
transformMatrix.translate(x, y, z);
|
||||
public void translate(float x, float y, float z, int typeBits) {
|
||||
begin(typeBits).translate(x, y, z);
|
||||
markDirty();
|
||||
}
|
||||
|
||||
public void rotate(float rad, float x, float y, float z) {
|
||||
transformMatrix.rotate(rad, x, y, z);
|
||||
public void rotate(float rad, float x, float y, float z, int typeBits) {
|
||||
begin(typeBits).rotate(rad, x, y, z);
|
||||
markDirty();
|
||||
}
|
||||
|
||||
public void multMatrix(Matrix4f matrix4f) {
|
||||
transformMatrix.mul(matrix4f);
|
||||
public void multMatrix(Matrix4f matrix4f, int typeBits) {
|
||||
begin(typeBits).mul(matrix4f);
|
||||
markDirty();
|
||||
}
|
||||
|
||||
public void setIdentity() {
|
||||
transformMatrix.identity();
|
||||
markDirty();
|
||||
public void reset(int slot) {
|
||||
transformMatrices[slot].identity();
|
||||
transformTypes[slot] = 0;
|
||||
if (slot == SLOT_VERTEX) {
|
||||
markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3f getScale(Vector3f dest) {
|
||||
return transformMatrix.getScale(dest);
|
||||
public Vector3f getScale(int slot, Vector3f dest) {
|
||||
final Matrix4f matrix = transformMatrices[slot];
|
||||
return dest.set(matrix.m00(), matrix.m11(), matrix.m22());
|
||||
}
|
||||
|
||||
public Vector3f getTranslation(Vector3f dest) {
|
||||
return transformMatrix.getTranslation(dest);
|
||||
public Vector3f getTranslation(int slot, Vector3f dest) {
|
||||
return transformMatrices[slot].getTranslation(dest);
|
||||
}
|
||||
|
||||
/**
|
||||
* WARNING: If you apply transformations to this matrix, make sure to call markDirty() afterward!
|
||||
*/
|
||||
public Matrix4f getReadMatrix() {
|
||||
return transformMatrix;
|
||||
public Matrix4f getReadMatrix(int slot) {
|
||||
return transformMatrices[slot];
|
||||
}
|
||||
|
||||
public int getTransformType(int slot) {
|
||||
return transformTypes[slot];
|
||||
}
|
||||
|
||||
/** The GL matrix target a slot's pending delta belongs to. */
|
||||
public int getSlotMatrixMode(int slot) {
|
||||
return slotModes[slot];
|
||||
}
|
||||
|
||||
public void markDirty() {
|
||||
if (matrixMode == GL11.GL_MODELVIEW) {
|
||||
NormalHelper.getNormalMatrix(transformMatrix, normalMatrix);
|
||||
NormalHelper.getNormalMatrix(transformMatrices[SLOT_VERTEX], normalMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1425,15 +1592,26 @@ public class DisplayListManager {
|
||||
*/
|
||||
public void setMatrixMode(int mode) {
|
||||
this.matrixMode = mode;
|
||||
markDirty();
|
||||
}
|
||||
|
||||
public int getMatrixMode() {
|
||||
return this.matrixMode;
|
||||
}
|
||||
|
||||
public boolean isIdentity() {
|
||||
return MatrixHelper.isIdentity(transformMatrix);
|
||||
public int getCurrentSlot() {
|
||||
return slotOf(matrixMode);
|
||||
}
|
||||
|
||||
public int getStreamMatrixMode() {
|
||||
return this.streamMatrixMode;
|
||||
}
|
||||
|
||||
public void setStreamMatrixMode(int mode) {
|
||||
this.streamMatrixMode = mode;
|
||||
}
|
||||
|
||||
public boolean isIdentity(int slot) {
|
||||
return MatrixHelper.isIdentity(transformMatrices[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1446,10 +1624,12 @@ public class DisplayListManager {
|
||||
@Override
|
||||
public boolean onDraw(CallbackTessellator tessellator) {
|
||||
if (!tessellator.isEmpty()) {
|
||||
if (!isIdentity()) {
|
||||
if (!isIdentity(SLOT_VERTEX) || !isIdentity(SLOT_TEXTURE) || !isIdentity(SLOT_COLOR)) {
|
||||
final int baked = bakedOwner == tessellator ? bakedVertices : 0;
|
||||
if (bakedVertices == 0) {
|
||||
flushMatrix();
|
||||
flushMatrix(SLOT_VERTEX);
|
||||
flushMatrix(SLOT_TEXTURE);
|
||||
flushMatrix(SLOT_COLOR);
|
||||
} else if (baked != tessellator.vertexCount) {
|
||||
GLStateManager.warnOnce("dl-mixed-baked-raw", "Display list {}: draw of {} vertices carries {} transformed ones - the rest lose the pending transform", glListId, tessellator.vertexCount, baked);
|
||||
}
|
||||
@@ -1473,22 +1653,22 @@ public class DisplayListManager {
|
||||
|
||||
@Override
|
||||
protected Matrix4f getMVPMatrix() {
|
||||
return matrixMode <= GL11.GL_PROJECTION ? transformMatrix : null;
|
||||
return transformTypes[SLOT_VERTEX] != 0 ? transformMatrices[SLOT_VERTEX] : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Matrix4f getTextureMatrix() {
|
||||
return matrixMode == GL11.GL_TEXTURE ? transformMatrix : null;
|
||||
return transformTypes[SLOT_TEXTURE] != 0 ? transformMatrices[SLOT_TEXTURE] : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Matrix4f getColorMatrix() {
|
||||
return matrixMode == GL11.GL_COLOR ? transformMatrix : null;
|
||||
return transformTypes[SLOT_COLOR] != 0 ? transformMatrices[SLOT_COLOR] : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Matrix3f getNormalMatrix() {
|
||||
return matrixMode == GL11.GL_MODELVIEW ? normalMatrix : null; // GL_PROJECTION doesn't transform normals
|
||||
return transformTypes[SLOT_VERTEX] != 0 && slotModes[SLOT_VERTEX] == GL11.GL_MODELVIEW ? normalMatrix : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,7 +302,7 @@ public class Feature {
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_SCISSOR_BIT, ImmutableSet.of(
|
||||
GLStateManager.getScissorTest()
|
||||
// Scissor box
|
||||
, GLStateManager.getScissorState()
|
||||
));
|
||||
attribToFeatures.put(GL11.GL_STENCIL_BUFFER_BIT, ImmutableSet.of(
|
||||
GLStateManager.getStencilTest()
|
||||
|
||||
@@ -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;
|
||||
@@ -48,6 +47,10 @@ public class FeedbackManager {
|
||||
return renderMode == GL11.GL_FEEDBACK;
|
||||
}
|
||||
|
||||
public static int getRenderMode() {
|
||||
return renderMode;
|
||||
}
|
||||
|
||||
public static void glFeedbackBuffer(int type, FloatBuffer buffer) {
|
||||
if (renderMode == GL11.GL_FEEDBACK) {
|
||||
GLStateManager.LOGGER.error("glFeedbackBuffer called while already in feedback mode");
|
||||
@@ -126,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;
|
||||
@@ -165,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);
|
||||
@@ -204,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++) {
|
||||
@@ -213,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);
|
||||
}
|
||||
@@ -245,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;
|
||||
@@ -23,6 +21,7 @@ import com.gtnewhorizons.angelica.glsm.stacks.MaterialStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.MatrixModeStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.PointStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.PolygonStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ScissorStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StackIdAllocator;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ViewPortStateStack;
|
||||
@@ -34,16 +33,24 @@ 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;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
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;
|
||||
|
||||
@@ -100,6 +107,8 @@ public class GLContextState {
|
||||
public int maxBoundImageUnit = 0;
|
||||
public final StateSet[] attribSets = new StateSet[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public int attribDepth = 0;
|
||||
public int batchStateGeneration;
|
||||
public final int[] savedBatchStateGen = new int[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public CowStateStack<?>[] stackById;
|
||||
public CowDepths[] depthsById;
|
||||
public int[] restoreBitById;
|
||||
@@ -125,6 +134,7 @@ public class GLContextState {
|
||||
public final BlendStateStack blendState = member(new BlendStateStack(StackIdAllocator.nextId()));
|
||||
public final BooleanStateStack blendMode = track(new BooleanStateStack(GL11.GL_BLEND));
|
||||
public final BooleanStateStack scissorTest = track(new BooleanStateStack(GL11.GL_SCISSOR_TEST));
|
||||
public final BooleanStateStack rasterizerDiscard = track(new BooleanStateStack(GL30.GL_RASTERIZER_DISCARD));
|
||||
public final DepthStateStack depthState = member(new DepthStateStack(StackIdAllocator.nextId()));
|
||||
public final BooleanStateStack depthTest = track(new BooleanStateStack(GL11.GL_DEPTH_TEST));
|
||||
public final FogStateStack fogState = member(new FogStateStack(StackIdAllocator.nextId()));
|
||||
@@ -200,6 +210,7 @@ public class GLContextState {
|
||||
public final MaterialStateStack frontMaterial = member(new MaterialStateStack(GL11.GL_FRONT, StackIdAllocator.nextId()));
|
||||
public final MaterialStateStack backMaterial = member(new MaterialStateStack(GL11.GL_BACK, StackIdAllocator.nextId()));
|
||||
public final ViewPortStateStack viewportState = member(new ViewPortStateStack(StackIdAllocator.nextId()));
|
||||
public final ScissorStateStack scissorState = member(new ScissorStateStack(StackIdAllocator.nextId()));
|
||||
public int activeProgram = 0;
|
||||
public final IntegerStateStack programStack = member(new IntegerStateStack(0, StackIdAllocator.nextId()));
|
||||
public int listBase = 0;
|
||||
@@ -210,7 +221,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];
|
||||
@@ -223,6 +233,34 @@ 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 int savedFilterMin = -1;
|
||||
public int savedFilterMag = -1;
|
||||
public float savedFilterAniso = -1.0f;
|
||||
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());
|
||||
@@ -238,11 +276,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];
|
||||
@@ -253,6 +291,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
@@ -0,0 +1,26 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VertexKey;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LightModelState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.LightState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public final class LightingSnapshot {
|
||||
final boolean[] lightEnabled = new boolean[VertexKey.FFP_LIGHT_COUNT];
|
||||
final LightState[] lights = new LightState[VertexKey.FFP_LIGHT_COUNT];
|
||||
final LightModelState lightModel = new LightModelState();
|
||||
final MaterialState frontMaterial = new MaterialState(GL11.GL_FRONT);
|
||||
final MaterialState backMaterial = new MaterialState(GL11.GL_BACK);
|
||||
boolean colorMaterial;
|
||||
int colorMaterialFace;
|
||||
int colorMaterialParameter;
|
||||
boolean normalize;
|
||||
boolean rescaleNormal;
|
||||
|
||||
public LightingSnapshot() {
|
||||
for (int i = 0; i < lights.length; i++) {
|
||||
lights[i] = new LightState(GL11.GL_LIGHT0 + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.");
|
||||
@@ -496,20 +496,21 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void bindTextureToUnit(int unit, int texture) {
|
||||
if (unit != 0 && GLStateManager.getBoundTextureForServerState(unit) != texture) GLStateManager.beforeUncapturedStateChange();
|
||||
dsaState.bindTextureToUnit(unit, texture);
|
||||
}
|
||||
|
||||
public static void bindTextureToUnit(int target, int unit, int texture) {
|
||||
if (unit != 0 && GLStateManager.getBoundTextureForServerState(unit) != texture) GLStateManager.beforeUncapturedStateChange();
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ public final class StateSet {
|
||||
public static final int R_TEXTURE = 1 << 12;
|
||||
public static final int R_ACTIVE_UNIT = 1 << 13;
|
||||
public static final int R_PROGRAM = 1 << 14;
|
||||
public static final int R_SCISSOR = 1 << 15;
|
||||
|
||||
public static final int B_FRAG_COLORBUF = 1;
|
||||
public static final int B_FRAG_TEXTURE = 1 << 1;
|
||||
@@ -87,10 +88,15 @@ public final class StateSet {
|
||||
GLStateManager.getActiveTextureUnitStack(),
|
||||
GLStateManager.getBlendMode(), GLStateManager.getAlphaTest(), GLStateManager.getDepthTest(),
|
||||
GLStateManager.getLightingState(), GLStateManager.getCullState(), GLStateManager.getPolygonOffsetFillState(),
|
||||
GLStateManager.getTextures().getTextureUnitStates(0)
|
||||
GLStateManager.getTextures().getTextureUnitStates(0), GLStateManager.getTextures().getTextureUnitStates(1),
|
||||
GLStateManager.getLightStates()[0], GLStateManager.getLightStates()[1],
|
||||
GLStateManager.getLightDataStates()[0], GLStateManager.getLightDataStates()[1],
|
||||
GLStateManager.getColorMaterial(), GLStateManager.getColorMaterialFace(), GLStateManager.getColorMaterialParameter(),
|
||||
GLStateManager.getLightModel(), GLStateManager.getFrontMaterial(), GLStateManager.getBackMaterial(),
|
||||
GLStateManager.getNormalizeState(), GLStateManager.getRescaleNormalState()
|
||||
),
|
||||
R_BLEND | R_DEPTH | R_COLOR_MASK | R_POLYGON | R_TEXTURE | R_ACTIVE_UNIT,
|
||||
B_FRAG_COLORBUF,
|
||||
B_FRAG_COLORBUF | B_LIGHTING,
|
||||
true);
|
||||
}
|
||||
|
||||
@@ -205,6 +211,9 @@ public final class StateSet {
|
||||
if ((mask & GL11.GL_VIEWPORT_BIT) != 0) {
|
||||
restore |= R_VIEWPORT;
|
||||
}
|
||||
if ((mask & GL11.GL_SCISSOR_BIT) != 0) {
|
||||
restore |= R_SCISSOR;
|
||||
}
|
||||
if ((mask & GL11.GL_LINE_BIT) != 0) {
|
||||
restore |= R_LINE;
|
||||
}
|
||||
|
||||
@@ -652,6 +652,11 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
GL11.glReadPixels(x, y, width, height, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readPixels(int x, int y, int width, int height, int format, int type, long pixelBufferOffset) {
|
||||
GL11.glReadPixels(x, y, width, height, format, type, pixelBufferOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getTexImage(int target, int level, int format, int type, ByteBuffer pixels) {
|
||||
GL11.glGetTexImage(target, level, format, type, pixels);
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizons.angelica.glsm.GLESFormatRemap;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureStaging;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -66,6 +70,7 @@ public abstract class RenderBackend {
|
||||
public abstract void flush();
|
||||
public abstract void finish();
|
||||
|
||||
private final ByteBuffer[] clientStagingCache = new ByteBuffer[16];
|
||||
private VSyncMode effectiveVSyncMode = VSyncMode.ON;
|
||||
private VSyncMode preferredTearFreeMode;
|
||||
|
||||
@@ -351,6 +356,71 @@ public abstract class RenderBackend {
|
||||
public void texImage3D(int target, int level, int internalFormat, int width, int height, int depth, int border, int format, int type, IntBuffer pixels) {}
|
||||
public void texSubImage1D(int target, int level, int xoffset, int width, int format, int type, ByteBuffer pixels) {}
|
||||
public void texSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, long pboOffset) {}
|
||||
|
||||
public TextureStaging beginTextureStaging(int level, int x, int y, int width, int height) {
|
||||
final int size = Math.multiplyExact(Math.multiplyExact(width, height), 4);
|
||||
ByteBuffer buffer = null;
|
||||
for (int i = 0; i < clientStagingCache.length; i++) {
|
||||
final ByteBuffer cached = clientStagingCache[i];
|
||||
if (cached != null && cached.capacity() == size) {
|
||||
clientStagingCache[i] = null;
|
||||
buffer = cached;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (buffer == null) buffer = MemoryUtilities.memAlloc(size);
|
||||
buffer.clear();
|
||||
return new ClientTextureStaging(buffer, level, x, y, width, height);
|
||||
}
|
||||
|
||||
public boolean commitTextureStaging(TextureStaging staging) {
|
||||
final int type = RenderSystem.isGLES() ? GLESFormatRemap.remapPixelType(GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV) : GL12.GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
try {
|
||||
final ClientTextureStaging s = (ClientTextureStaging) staging;
|
||||
texSubImage2D(GL11.GL_TEXTURE_2D, s.level(), s.x(), s.y(), s.width(), s.height(), GL12.GL_BGRA, type, s.buffer());
|
||||
} finally {
|
||||
recycleClientStaging(staging.buffer());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void abandonTextureStaging(TextureStaging staging) {
|
||||
recycleClientStaging(staging.buffer());
|
||||
}
|
||||
|
||||
public void trimTextureStaging() {
|
||||
for (int i = 0; i < clientStagingCache.length; i++) {
|
||||
MemoryUtilities.memFree(clientStagingCache[i]);
|
||||
clientStagingCache[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
private record ClientTextureStaging(ByteBuffer buffer, int level, int x, int y, int width, int height) implements TextureStaging {
|
||||
@Override
|
||||
public boolean bgra() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void recycleClientStaging(ByteBuffer buffer) {
|
||||
int victim = -1;
|
||||
for (int i = 0; i < clientStagingCache.length; i++) {
|
||||
final ByteBuffer cached = clientStagingCache[i];
|
||||
if (cached == null) {
|
||||
clientStagingCache[i] = buffer;
|
||||
return;
|
||||
}
|
||||
if (cached.capacity() < buffer.capacity() && (victim < 0 || cached.capacity() < clientStagingCache[victim].capacity())) {
|
||||
victim = i;
|
||||
}
|
||||
}
|
||||
if (victim < 0) {
|
||||
MemoryUtilities.memFree(buffer);
|
||||
return;
|
||||
}
|
||||
MemoryUtilities.memFree(clientStagingCache[victim]);
|
||||
clientStagingCache[victim] = buffer;
|
||||
}
|
||||
public void texSubImage3D(int target, int level, int xoffset, int yoffset, int zoffset, int width, int height, int depth, int format, int type, ByteBuffer pixels) {}
|
||||
public void copyTexImage1D(int target, int level, int internalFormat, int x, int y, int width, int border) {}
|
||||
public void copyTexImage2D(int target, int level, int internalFormat, int x, int y, int width, int height, int border) {}
|
||||
@@ -393,6 +463,7 @@ public abstract class RenderBackend {
|
||||
public abstract void readPixels(int x, int y, int width, int height, int format, int type, ByteBuffer pixels);
|
||||
public abstract void readPixels(int x, int y, int width, int height, int format, int type, FloatBuffer pixels);
|
||||
public abstract void readPixels(int x, int y, int width, int height, int format, int type, IntBuffer pixels);
|
||||
public abstract void readPixels(int x, int y, int width, int height, int format, int type, long pixelBufferOffset);
|
||||
public abstract void getTexImage(int target, int level, int format, int type, ByteBuffer pixels);
|
||||
public abstract void getTexImage(int target, int level, int format, int type, IntBuffer pixels);
|
||||
public abstract void getTexImage(int target, int level, int format, int type, long pixelBufferOffset);
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fc;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
public final class CombinedGlint {
|
||||
private static final Matrix4f savedMatrix = new Matrix4f();
|
||||
private static final Matrix4f inverse = new Matrix4f();
|
||||
private static final Matrix4f relative = new Matrix4f();
|
||||
private static boolean active;
|
||||
private static boolean replaceAlpha;
|
||||
|
||||
private CombinedGlint() {}
|
||||
|
||||
public static boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public static boolean replacesAlpha() {
|
||||
return active && replaceAlpha;
|
||||
}
|
||||
|
||||
public static boolean begin(Matrix4fc secondTextureMatrix) {
|
||||
if (active || GLStateManager.getActiveProgram() != 0 || GLStateManager.getLightingState().isEnabled() || GLStateManager.getColorSumState().isEnabled() || !GLStateManager.glIsEnabled(GL11.GL_BLEND)) return false;
|
||||
final var blend = GLStateManager.getBlendState();
|
||||
final boolean lastAlpha = blend.getSrcAlpha() == GL11.GL_ONE && blend.getDstAlpha() == GL11.GL_ZERO && blend.getEquationAlpha() == GL14.GL_FUNC_ADD;
|
||||
final boolean preserveAlpha = blend.getSrcAlpha() == GL11.GL_ZERO && blend.getDstAlpha() == GL11.GL_ONE && (blend.getEquationAlpha() == GL14.GL_FUNC_ADD || blend.getEquationAlpha() == GL14.GL_FUNC_REVERSE_SUBTRACT);
|
||||
if (blend.getSrcRgb() != GL11.GL_SRC_COLOR || blend.getDstRgb() != GL11.GL_ONE
|
||||
|| (!lastAlpha && !preserveAlpha && (blend.getSrcAlpha() != GL11.GL_SRC_COLOR || blend.getDstAlpha() != GL11.GL_ONE || blend.getEquationAlpha() != blend.getEquationRgb()))
|
||||
|| (blend.getEquationRgb() != GL14.GL_FUNC_ADD && blend.getEquationRgb() != GL14.GL_FUNC_REVERSE_SUBTRACT)) return false;
|
||||
final var textures = GLStateManager.getTextures();
|
||||
if (!textures.getTextureUnitStates(0).isEnabled() || textures.getTextureUnitBindings(0).getBinding() == 0 || textures.getTexEnvState(0).mode != GL11.GL_MODULATE) return false;
|
||||
if (textures.getTextureUnitStates(1).isEnabled() && textures.getTextureUnitBindings(1).getBinding() != 0 && textures.getTexEnvState(1).mode != GL11.GL_MODULATE) return false;
|
||||
for (int unit = 2; unit < 4; unit++) {
|
||||
if (textures.getTextureUnitStates(unit).isEnabled() && textures.getTextureUnitBindings(unit).getBinding() != 0) return false;
|
||||
}
|
||||
inverse.set(textures.getTextureUnitMatrix(0)).invert();
|
||||
relative.set(secondTextureMatrix).mul(inverse);
|
||||
if (!relative.isFinite()) return false;
|
||||
savedMatrix.set(textures.getTextureUnitMatrix(3));
|
||||
GLStateManager.setTextureMatrix(3, relative);
|
||||
replaceAlpha = lastAlpha;
|
||||
active = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void end() {
|
||||
if (!active) return;
|
||||
active = false;
|
||||
GLStateManager.setTextureMatrix(3, savedMatrix);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,9 @@ 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.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
@@ -59,7 +61,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 +80,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 +109,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 +131,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 +149,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);
|
||||
@@ -222,12 +227,17 @@ public final class FfpExtendedAttribs {
|
||||
}
|
||||
|
||||
public static void setNeutralCurrentValues() {
|
||||
GLStateManager.glVertexAttrib2f(ImmediateExtendedAttribHandler.LOC_MID_TEX, 0.5f, 0.5f);
|
||||
GLStateManager.glVertexAttrib4f(ImmediateExtendedAttribHandler.LOC_TANGENT, 1.0f, 0.0f, 0.0f, 1.0f);
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
GLStateManager.glVertexAttrib2f(ImmediateExtendedAttribHandler.LOC_MID_TEX, 0.5f, 0.5f);
|
||||
GLStateManager.glVertexAttrib4f(ImmediateExtendedAttribHandler.LOC_TANGENT, 1.0f, 0.0f, 0.0f, 1.0f);
|
||||
} finally {
|
||||
BatchStateGuard.resume();
|
||||
}
|
||||
}
|
||||
|
||||
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 +246,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 +278,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);
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@ public final class FragmentKey {
|
||||
public static final int FOG_EXP2 = 3;
|
||||
|
||||
private static final int GLOBAL_BITS = 14;
|
||||
private static final int BIT_COMBINED_GLINT = 61;
|
||||
private static final int BIT_GLINT_REPLACE_ALPHA = 62;
|
||||
private static final int BIT_FOG_MODE = 0; // 2 bits
|
||||
private static final int BIT_ALPHA_TEST = 2; // 1 bit
|
||||
private static final int BIT_ALPHA_FUNC = 3; // 3 bits
|
||||
@@ -111,6 +113,8 @@ public final class FragmentKey {
|
||||
|
||||
public static int packFromState(long[] out, GLContextState glCtx) {
|
||||
long global = 0;
|
||||
if (CombinedGlint.isActive()) global |= 1L << BIT_COMBINED_GLINT;
|
||||
if (CombinedGlint.replacesAlpha()) global |= 1L << BIT_GLINT_REPLACE_ALPHA;
|
||||
|
||||
// Fog
|
||||
if (glCtx.fogMode.isEnabled()) {
|
||||
@@ -130,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);
|
||||
}
|
||||
|
||||
@@ -248,6 +252,8 @@ public final class FragmentKey {
|
||||
}
|
||||
|
||||
public int fogMode() { return (int) (packed[0] & 0x3); }
|
||||
public boolean combinedGlint() { return (packed[0] & (1L << BIT_COMBINED_GLINT)) != 0; }
|
||||
public boolean glintReplaceAlpha() { return (packed[0] & (1L << BIT_GLINT_REPLACE_ALPHA)) != 0; }
|
||||
public boolean alphaTestEnabled() { return ((packed[0] >> BIT_ALPHA_TEST) & 1) != 0; }
|
||||
public int alphaTestFunc() { return (int) ((packed[0] >> BIT_ALPHA_FUNC) & 0x7); }
|
||||
public boolean separateSpecular() { return ((packed[0] >> BIT_SEPARATE_SPECULAR) & 1) != 0; }
|
||||
@@ -389,7 +395,9 @@ public final class FragmentKey {
|
||||
default -> "?";
|
||||
};
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b colorSum=%b overlay=%b units=%d", fogName, alphaTestEnabled(), alphaTestEnabled() ? String.format("0x%04X", decodeAlphaFunc(alphaTestFunc())) : "-", separateSpecular(), colorSum(), overlayEnabled() || overlayInstanced(), nrEnabledUnits()));
|
||||
sb.append(String.format("FFPFragmentKey[fog=%s alpha=%b(%s) specSep=%b colorSum=%b overlay=%b units=%d", fogName, alphaTestEnabled(),
|
||||
alphaTestEnabled() ? String.format("0x%04X", decodeAlphaFunc(alphaTestFunc())) : "-", separateSpecular(), colorSum(),
|
||||
overlayEnabled() || overlayInstanced(), nrEnabledUnits()));
|
||||
for (int i = 0; i < nrEnabledUnits(); i++) {
|
||||
if (!unitEnabled(i)) {
|
||||
sb.append(String.format(" u%d=OFF", i));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
|
||||
|
||||
/**
|
||||
* Generates GLSL 330 core fragment shaders for FFP emulation.
|
||||
@@ -17,6 +18,15 @@ public final class FragmentShaderGenerator {
|
||||
|
||||
emitInputs(sb, key);
|
||||
emitUniforms(sb, key);
|
||||
if (key.combinedGlint()) {
|
||||
sb.append("#define GLINT_REPLACE_ALPHA ").append(key.glintReplaceAlpha() ? 1 : 0).append('\n');
|
||||
sb.append("#define GLINT_FOG_MODE ").append(key.fogMode()).append('\n');
|
||||
sb.append("#define GLINT_ALPHA_FUNC ").append(key.alphaTestEnabled() ? key.alphaTestFunc() : 7).append('\n');
|
||||
sb.append("#define GLINT_OVERLAY ").append(key.overlayInstanced() ? 2 : key.overlayEnabled() ? 1 : 0).append('\n');
|
||||
sb.append("#define GLINT_LIGHTMAP ").append(key.nrEnabledUnits() > 1 && key.unitEnabled(1) ? 1 : 0).append('\n');
|
||||
sb.append(ShaderLoader.getShaderSource("angelica:combined_glint.frag.glsl"));
|
||||
return sb.toString();
|
||||
}
|
||||
sb.append("out vec4 fragColor;\n\n");
|
||||
|
||||
sb.append("void main() {\n");
|
||||
|
||||
@@ -55,13 +55,18 @@ public final class InstancedAttribs {
|
||||
return (entity & 0xFFFFL) | ((blockEntity & 0xFFFFL) << 16) | ((item & 0xFFFFL) << 32);
|
||||
}
|
||||
|
||||
public static int unpackId(long info, int shift) {
|
||||
final int id = (int) (info >>> shift) & 0xFFFF;
|
||||
return id == 0xFFFF ? -1 : id;
|
||||
}
|
||||
|
||||
public static void pointHead(long base, int stride) {
|
||||
for (int r = 0; r < 3; r++) {
|
||||
GLStateManager.glVertexAttribPointer(LOC_ROW0 + r, 4, GL11.GL_FLOAT, false, stride, base + OFFSET_ROWS + r * 16L);
|
||||
}
|
||||
GLStateManager.glVertexAttribPointer(LOC_COLOR, 4, GL11.GL_UNSIGNED_BYTE, true, stride, base + OFFSET_COLOR);
|
||||
GLStateManager.glVertexAttribPointer(LOC_OVERLAY, 4, GL11.GL_UNSIGNED_BYTE, true, stride, base + OFFSET_OVERLAY);
|
||||
GLStateManager.glVertexAttribIPointer(LOC_ENTITY, 4, GL11.GL_SHORT, stride, base + OFFSET_ENTITY);
|
||||
GLStateManager.glVertexAttribIPointer(LOC_ENTITY, 4, GL11.GL_UNSIGNED_SHORT, stride, base + OFFSET_ENTITY);
|
||||
}
|
||||
|
||||
public static void pointTemplate(long base) {
|
||||
|
||||
@@ -102,7 +102,11 @@ public class Program {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (ffpProgram.locSampler[i] != -1) backend.uniform1i(ffpProgram.locSampler[i], i);
|
||||
}
|
||||
backend.useProgram(previousProgram);
|
||||
if (previousProgram != 0) {
|
||||
backend.useProgram(previousProgram);
|
||||
} else {
|
||||
ShaderManager.getInstance().invalidateProgram();
|
||||
}
|
||||
|
||||
return ffpProgram;
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
@@ -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,19 +110,21 @@ public final class ShaderManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
public static void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
public void activate() {
|
||||
active = true;
|
||||
lastBoundProgramId = GLStateManager.getActiveProgram();
|
||||
invalidateProgram();
|
||||
}
|
||||
|
||||
public static void invalidateBoundProgram() {
|
||||
final ShaderManager sm = Holder.INSTANCE;
|
||||
sm.lastBoundProgramId = -1;
|
||||
sm.currentProgram = null;
|
||||
/** Invalidate both binding and state key so the next draw selects and binds an FFP variant. */
|
||||
public void invalidateProgram() {
|
||||
lastBoundProgramId = -1;
|
||||
currentProgram = null;
|
||||
currentVertexKeyPacked = Long.MIN_VALUE;
|
||||
uniforms.invalidateBinding();
|
||||
}
|
||||
|
||||
public void deactivate() {
|
||||
@@ -136,30 +136,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);
|
||||
@@ -172,10 +171,11 @@ public final class ShaderManager {
|
||||
}
|
||||
|
||||
private void commitVariant(long vkPacked, int fkLen) {
|
||||
currentVertexKeyPacked = vkPacked;
|
||||
System.arraycopy(currentFKScratch, 0, currentFKPacked, 0, fkLen);
|
||||
currentFKLen = fkLen;
|
||||
currentProgram = cache.getOrCreate(vkPacked, currentFKPacked, currentFKLen);
|
||||
// Building a variant can invalidate the previous binding and key.
|
||||
currentVertexKeyPacked = vkPacked;
|
||||
final int programId = currentProgram.getProgramId();
|
||||
if (programId != lastBoundProgramId) {
|
||||
if (Tracy.ENABLED) variantSwitches++;
|
||||
@@ -191,44 +191,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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CommandRecorder;
|
||||
|
||||
/** Drains deferred geometry before changing state that is not stored with each batch. */
|
||||
public final class BatchStateGuard {
|
||||
public static Runnable flush;
|
||||
private static int suspended;
|
||||
|
||||
private BatchStateGuard() {}
|
||||
|
||||
public static void suspend() { suspended++; }
|
||||
public static void resume() { suspended--; }
|
||||
public static boolean isSuspended() { return suspended != 0; }
|
||||
|
||||
public static void beforeChange() {
|
||||
if (flush == null || suspended != 0 || !GLStateManager.isMainThread()
|
||||
|| DisplayListManager.getRecordMode() == DisplayListManager.RecordMode.COMPILE) return;
|
||||
final CommandRecorder recorder = DisplayListManager.isRecording() ? DisplayListManager.pauseRecording() : null;
|
||||
suspend();
|
||||
try {
|
||||
flush.run();
|
||||
} finally {
|
||||
resume();
|
||||
if (recorder != null) DisplayListManager.resumeRecording(recorder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,23 +1,24 @@
|
||||
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;
|
||||
|
||||
public static boolean extendedAttribsExpected;
|
||||
public static volatile boolean expandVertexFormats;
|
||||
public static int workerThreadCount;
|
||||
|
||||
private GLSMConfig() {}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ public final class GLSMHooks {
|
||||
public static DeferredDepthColorHandler depthColorHandler;
|
||||
public static GlintColorHandler glintColorHandler;
|
||||
public static ImmediateExtendedAttribHandler immediateExtendedHandler;
|
||||
public static ShaderWorkSubmitter shaderWorkSubmitter;
|
||||
public static ShaderTransformPostProcessor postTransformProcessor;
|
||||
public static PerFrameUniformBlock perFrameUniformBlock;
|
||||
public static PerFrameUniformBlock perPassUniformBlock;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface ShaderWorkSubmitter {
|
||||
<T> CompletableFuture<T> submit(Supplier<T> work);
|
||||
}
|
||||
@@ -1,32 +1,54 @@
|
||||
package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import org.lwjgl.opengl.EXTTextureFilterAnisotropic;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
public final class TextureUtilHooks {
|
||||
|
||||
private TextureUtilHooks() {}
|
||||
private static int pausedTexture;
|
||||
private static int pausedMin;
|
||||
private static int pausedMag;
|
||||
private static float pausedAniso;
|
||||
|
||||
private static int savedFilterTextureId;
|
||||
private static int nearestTexture;
|
||||
private static int nearestMin;
|
||||
private static int nearestMag;
|
||||
private static float nearestAniso;
|
||||
|
||||
private TextureUtilHooks() {}
|
||||
|
||||
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();
|
||||
final GLContextState ctx = GLStateManager.ctx();
|
||||
final int id = GLStateManager.getBoundTextureForServerState();
|
||||
ctx.savedFilterTextureId = id;
|
||||
final TextureInfo info = TextureInfoCache.INSTANCE.getInfo(id);
|
||||
if (info != null) {
|
||||
ctx.savedFilterMin = info.getMinFilter();
|
||||
ctx.savedFilterMag = info.getMagFilter();
|
||||
ctx.savedFilterAniso = info.getMaxAnisotropy();
|
||||
} else {
|
||||
ctx.savedFilterMin = -1;
|
||||
}
|
||||
}
|
||||
|
||||
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,11 +59,68 @@ 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 {
|
||||
GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean pauseTemporaryFilter() {
|
||||
final GLContextState ctx = GLStateManager.ctx();
|
||||
final int id = ctx.savedFilterTextureId;
|
||||
if (id == 0 || pausedTexture != 0 || ctx.savedFilterMin < 0
|
||||
|| DisplayListManager.getRecordMode() != DisplayListManager.RecordMode.NONE) return false;
|
||||
final TextureInfo info = TextureInfoCache.INSTANCE.getInfo(id);
|
||||
if (info == null) return false;
|
||||
pausedTexture = id;
|
||||
pausedMin = info.getMinFilter();
|
||||
pausedMag = info.getMagFilter();
|
||||
pausedAniso = info.getMaxAnisotropy();
|
||||
setFilter(id, ctx.savedFilterMin, ctx.savedFilterMag, ctx.savedFilterAniso);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void resumeTemporaryFilter() {
|
||||
if (pausedTexture == 0) return;
|
||||
final int id = pausedTexture;
|
||||
pausedTexture = 0;
|
||||
setFilter(id, pausedMin, pausedMag, pausedAniso);
|
||||
}
|
||||
|
||||
/** TextureUtil.func_152777_a(false, false, 1) without using vanilla's single save slot, which the caller may hold. */
|
||||
public static void forceNearestFilter() {
|
||||
final int id = GLStateManager.getBoundTextureForServerState();
|
||||
final TextureInfo info = id != 0 ? TextureInfoCache.INSTANCE.getInfo(id) : null;
|
||||
if (info == null) {
|
||||
nearestTexture = 0;
|
||||
return;
|
||||
}
|
||||
nearestTexture = id;
|
||||
nearestMin = info.getMinFilter();
|
||||
nearestMag = info.getMagFilter();
|
||||
nearestAniso = info.getMaxAnisotropy();
|
||||
setFilter(id, GL11.GL_NEAREST, GL11.GL_NEAREST, 1.0f);
|
||||
}
|
||||
|
||||
public static void restoreFilterAfterNearest() {
|
||||
if (nearestTexture == 0) return;
|
||||
final int id = nearestTexture;
|
||||
nearestTexture = 0;
|
||||
setFilter(id, nearestMin, nearestMag, nearestAniso);
|
||||
}
|
||||
|
||||
private static void setFilter(int id, int min, int mag, float aniso) {
|
||||
final boolean anisotropic = BackendManager.RENDER_BACKEND.isAnisotropicSupported();
|
||||
if (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);
|
||||
if (anisotropic) RenderSystem.texParameterf(id, GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
|
||||
} else {
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, min);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, mag);
|
||||
if (anisotropic) GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,10 @@ public class ModRedirector {
|
||||
return allExclusions;
|
||||
}
|
||||
|
||||
public String[] getConstantPoolSearchStrings() {
|
||||
return core.getConstantPoolSearchStrings();
|
||||
}
|
||||
|
||||
public boolean shouldTransform(byte[] basicClass) {
|
||||
return core.shouldTransform(basicClass);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.glsm.loading;
|
||||
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.BytePatternMatcher;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ClassHeaderMetadata;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.ExtensibleClassLoader;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.RfbClassTransformer;
|
||||
@@ -16,10 +18,12 @@ public abstract class RfbModRedirector implements RfbClassTransformer {
|
||||
|
||||
private final String id;
|
||||
private final ModRedirector inner;
|
||||
private final BytePatternMatcher cstPoolMatcher;
|
||||
|
||||
protected RfbModRedirector(@Pattern("[a-z0-9-]+") String id, ModRedirector inner) {
|
||||
this.id = id;
|
||||
this.inner = inner;
|
||||
this.cstPoolMatcher = new BytePatternMatcher(inner.getConstantPoolSearchStrings(), BytePatternMatcher.Mode.StartsWith);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,7 +57,11 @@ public abstract class RfbModRedirector implements RfbClassTransformer {
|
||||
if (!classNode.isPresent()) {
|
||||
return false;
|
||||
}
|
||||
return inner.shouldTransform(classNode.getOriginalBytes());
|
||||
final ClassHeaderMetadata metadata = classNode.getOriginalMetadata();
|
||||
if (metadata == null) {
|
||||
return inner.shouldTransform(classNode.getOriginalBytes());
|
||||
}
|
||||
return metadata.matchesBytes(classNode.getOriginalBytes(), cstPoolMatcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.gtnewhorizons.angelica.glsm.profiling;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.CaptureGate;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.sun.management.ThreadMXBean;
|
||||
@@ -66,12 +65,12 @@ public final class Tracy {
|
||||
|
||||
static {
|
||||
TracyBackend backend = null;
|
||||
if (SystemProperties.TRACY) {
|
||||
if (TracyOptions.enabled()) {
|
||||
backend = loadAndInit();
|
||||
}
|
||||
BACKEND = backend;
|
||||
ENABLED = backend != null;
|
||||
FINE_ZONES = ENABLED && SystemProperties.TRACY_FINE_ZONES;
|
||||
FINE_ZONES = ENABLED && TracyOptions.fineZones();
|
||||
if (ENABLED) {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(Tracy::shutdown, "Tracy-Shutdown"));
|
||||
SECTIONS.registerCategory(SECTION_WORLD, "World");
|
||||
@@ -86,6 +85,18 @@ public final class Tracy {
|
||||
|
||||
private static void shutdown() {
|
||||
SHUTTING_DOWN = true;
|
||||
if (isCaptureActive(BACKEND.captureState())) {
|
||||
BACKEND.captureStop();
|
||||
final long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(10);
|
||||
while (isCaptureActive(BACKEND.captureState()) && System.nanoTime() < deadline) {
|
||||
try {
|
||||
Thread.sleep(50);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
final Thread worker = new Thread(BACKEND::shutdown, "Tracy-Shutdown-Worker");
|
||||
worker.setDaemon(true);
|
||||
worker.start();
|
||||
@@ -96,6 +107,10 @@ public final class Tracy {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isCaptureActive(int state) {
|
||||
return state == TracyBackend.CAPTURE_CONNECTING || state == TracyBackend.CAPTURE_RECORDING || state == TracyBackend.CAPTURE_SAVING;
|
||||
}
|
||||
|
||||
private static TracyBackend loadAndInit() {
|
||||
TracyBackend found = null;
|
||||
try {
|
||||
@@ -104,11 +119,11 @@ public final class Tracy {
|
||||
found = it.next();
|
||||
}
|
||||
} catch (ServiceConfigurationError | LinkageError e) {
|
||||
LOGGER.warn("Tracy requested (-Dangelica.tracy=true) but backend unavailable: {}", e.getMessage());
|
||||
LOGGER.warn("Tracy: backend unavailable: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
if (found == null) {
|
||||
LOGGER.warn("Tracy requested (-Dangelica.tracy=true) but no TracyBackend service present");
|
||||
LOGGER.warn("Tracy: enabled but the angelica-tracy jar is not installed");
|
||||
return null;
|
||||
}
|
||||
if (!implementsCurrentAbi(found)) return null;
|
||||
@@ -120,7 +135,7 @@ public final class Tracy {
|
||||
init.start();
|
||||
try {
|
||||
if (!done.await(15, TimeUnit.SECONDS) && result.compareAndSet(null, Boolean.FALSE)) {
|
||||
LOGGER.warn("Tracy requested (-Dangelica.tracy=true) but init timed out; profiling disabled");
|
||||
LOGGER.warn("Tracy: init timed out; profiling disabled");
|
||||
return null;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
@@ -131,7 +146,7 @@ public final class Tracy {
|
||||
return null;
|
||||
}
|
||||
if (!Boolean.TRUE.equals(result.get())) {
|
||||
LOGGER.warn("Tracy requested (-Dangelica.tracy=true) but native init failed; profiling disabled");
|
||||
LOGGER.warn("Tracy: native init failed; profiling disabled");
|
||||
return null;
|
||||
}
|
||||
return backend;
|
||||
@@ -388,6 +403,31 @@ public final class Tracy {
|
||||
return ENABLED && BACKEND.isConnected();
|
||||
}
|
||||
|
||||
public static String captureStart(String path, int seconds) {
|
||||
if (!ENABLED) return "Tracy not enabled";
|
||||
return BACKEND.captureStart(path, seconds);
|
||||
}
|
||||
|
||||
public static void captureStop() {
|
||||
if (!ENABLED) return;
|
||||
BACKEND.captureStop();
|
||||
}
|
||||
|
||||
public static int captureState() {
|
||||
if (!ENABLED) return 0;
|
||||
return BACKEND.captureState();
|
||||
}
|
||||
|
||||
public static long captureElapsedMs() {
|
||||
if (!ENABLED) return 0;
|
||||
return BACKEND.captureElapsedMs();
|
||||
}
|
||||
|
||||
public static String captureError() {
|
||||
if (!ENABLED) return "";
|
||||
return BACKEND.captureError();
|
||||
}
|
||||
|
||||
private static boolean gpuInitAttempted;
|
||||
private static boolean gpuReady;
|
||||
@Setter private static boolean gpuZonesEnabled = true;
|
||||
|
||||
@@ -12,6 +12,13 @@ public interface TracyBackend {
|
||||
int SEVERITY_ERROR = 4;
|
||||
int SEVERITY_FATAL = 5;
|
||||
|
||||
int CAPTURE_IDLE = 0;
|
||||
int CAPTURE_CONNECTING = 1;
|
||||
int CAPTURE_RECORDING = 2;
|
||||
int CAPTURE_SAVING = 3;
|
||||
int CAPTURE_DONE = 4;
|
||||
int CAPTURE_FAILED = 5;
|
||||
|
||||
boolean init();
|
||||
|
||||
long internSrcLoc(String name, int color);
|
||||
@@ -47,4 +54,12 @@ public interface TracyBackend {
|
||||
boolean gpuBeginZone(long srcLoc);
|
||||
void gpuEndZone();
|
||||
void gpuCollect();
|
||||
|
||||
default String captureStart(String path, int seconds) {
|
||||
return "Tracy capture unsupported by this angelica-tracy build";
|
||||
}
|
||||
default void captureStop() {}
|
||||
default int captureState() { return CAPTURE_IDLE; }
|
||||
default long captureElapsedMs() { return 0; }
|
||||
default String captureError() { return ""; }
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.gtnewhorizons.angelica.glsm.profiling;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
|
||||
public final class TracyOptions {
|
||||
|
||||
private static boolean configEnabled;
|
||||
private static boolean configRemote;
|
||||
private static boolean configFineZones;
|
||||
private static int configMaxSrcLocs = 4096;
|
||||
|
||||
private TracyOptions() {}
|
||||
|
||||
public static void latch(boolean enabled, boolean allowRemote, boolean fineZones, int maxSrcLocs) {
|
||||
configEnabled = enabled;
|
||||
configRemote = allowRemote;
|
||||
configFineZones = fineZones;
|
||||
configMaxSrcLocs = maxSrcLocs;
|
||||
}
|
||||
|
||||
public static boolean enabled() {
|
||||
return resolveBoolean(SystemProperties.TRACY_OVERRIDE, configEnabled);
|
||||
}
|
||||
|
||||
public static boolean allowRemote() {
|
||||
return configRemote;
|
||||
}
|
||||
|
||||
public static boolean fineZones() {
|
||||
return resolveBoolean(SystemProperties.TRACY_FINE_ZONES_OVERRIDE, configFineZones);
|
||||
}
|
||||
|
||||
public static int maxSrcLocs() {
|
||||
return resolveMaxSrcLocs(SystemProperties.TRACY_MAX_SRC_LOCS_OVERRIDE, configMaxSrcLocs);
|
||||
}
|
||||
|
||||
public static boolean configEnabled() {
|
||||
return configEnabled;
|
||||
}
|
||||
|
||||
public static boolean configRemote() {
|
||||
return configRemote;
|
||||
}
|
||||
|
||||
public static boolean configFineZones() {
|
||||
return configFineZones;
|
||||
}
|
||||
|
||||
public static int configMaxSrcLocs() {
|
||||
return configMaxSrcLocs;
|
||||
}
|
||||
|
||||
public static boolean backendPresent() {
|
||||
return TracyOptions.class.getResource("/com/gtnewhorizons/angelica/tracy/TracyClientBackend.class") != null;
|
||||
}
|
||||
|
||||
static boolean resolveBoolean(Boolean override, boolean config) {
|
||||
return override != null ? override : config;
|
||||
}
|
||||
|
||||
static int resolveMaxSrcLocs(Integer override, int config) {
|
||||
return Math.max(16, override != null ? override : config);
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
+7
-1
@@ -97,7 +97,8 @@ public final class CommandBufferExecutor {
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.USE_PROGRAM -> {
|
||||
GLStateManager.glUseProgram(memGetInt(ptr));
|
||||
final int recordedProgram = memGetInt(ptr);
|
||||
GLStateManager.glUseProgram(compiledDisplayList.resolveProgram(recordedProgram));
|
||||
ptr += 4;
|
||||
}
|
||||
case GLCommand.PUSH_ATTRIB -> {
|
||||
@@ -252,6 +253,11 @@ public final class CommandBufferExecutor {
|
||||
}
|
||||
|
||||
// === Float commands ===
|
||||
case GLCommand.VERTEX_ATTRIB -> {
|
||||
GLStateManager.glVertexAttrib4f(memGetInt(ptr), memGetFloat(ptr + 4), memGetFloat(ptr + 8),
|
||||
memGetFloat(ptr + 12), memGetFloat(ptr + 16));
|
||||
ptr += 20;
|
||||
}
|
||||
case GLCommand.POINT_SIZE -> {
|
||||
GLStateManager.glPointSize(memGetFloat(ptr));
|
||||
ptr += 4;
|
||||
|
||||
@@ -10,6 +10,8 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.*;
|
||||
|
||||
@@ -21,6 +23,13 @@ public final class CommandRecorder {
|
||||
private long writePointer;
|
||||
private final ArrayList<DisplayListCommand> complexObjects = new ArrayList<>();
|
||||
private final IndexedDrawBatchBuilder indexedDraws = new IndexedDrawBatchBuilder();
|
||||
private Set<DisplayListCommand> onceCommands;
|
||||
private DisplayListCommand lastComplexCommand;
|
||||
private int lastComplexCommandEnd = -1;
|
||||
|
||||
public boolean isLastCommand(DisplayListCommand command) {
|
||||
return size() == lastComplexCommandEnd && command.equals(lastComplexCommand);
|
||||
}
|
||||
|
||||
public CommandRecorder() {
|
||||
this(DEFAULT_CAPACITY);
|
||||
@@ -32,6 +41,11 @@ public final class CommandRecorder {
|
||||
this.writePointer = basePointer;
|
||||
}
|
||||
|
||||
public boolean markRecordedOnce(DisplayListCommand command) {
|
||||
if (onceCommands == null) onceCommands = new HashSet<>();
|
||||
return onceCommands.add(command);
|
||||
}
|
||||
|
||||
// === Low-level write operations ===
|
||||
|
||||
private void writeInt(int value) {
|
||||
@@ -364,6 +378,16 @@ public final class CommandRecorder {
|
||||
writeFloat(b);
|
||||
}
|
||||
|
||||
public void writeVertexAttrib(int index, float x, float y, float z, float w) {
|
||||
ensureCapacity(24);
|
||||
writeInt(GLCommand.VERTEX_ATTRIB);
|
||||
writeInt(index);
|
||||
writeFloat(x);
|
||||
writeFloat(y);
|
||||
writeFloat(z);
|
||||
writeFloat(w);
|
||||
}
|
||||
|
||||
public void writeClearColor(float r, float g, float b, float a) {
|
||||
ensureCapacity(20);
|
||||
writeInt(GLCommand.CLEAR_COLOR);
|
||||
@@ -649,6 +673,8 @@ public final class CommandRecorder {
|
||||
complexObjects.add(cmd);
|
||||
writeInt(GLCommand.COMPLEX_REF);
|
||||
writeInt(index);
|
||||
lastComplexCommand = cmd;
|
||||
lastComplexCommandEnd = size();
|
||||
}
|
||||
|
||||
// === Lifecycle ===
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.glsm.recording;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.DisplayListCommand;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatch;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
@@ -35,6 +36,24 @@ public final class CompiledDisplayList {
|
||||
private final DisplayListCommand[] complexObjects; // Complex commands (TexImage2D, etc.)
|
||||
private final DisplayListVBO ownedVbos; // GPU resources referenced by index
|
||||
private final List<IndexedDrawBatch> indexedBatches; // Shared VAO/VBO/EBO triples per layout group
|
||||
private Int2IntMap replayPrograms;
|
||||
private long programLifetimeGeneration = -1;
|
||||
|
||||
int resolveProgram(int program) {
|
||||
if (program == 0) return 0;
|
||||
final long generation = GLStateManager.getProgramLifetimeGeneration();
|
||||
if (replayPrograms == null) replayPrograms = new Int2IntOpenHashMap();
|
||||
if (programLifetimeGeneration != generation) {
|
||||
replayPrograms.clear();
|
||||
programLifetimeGeneration = generation;
|
||||
}
|
||||
if (replayPrograms.containsKey(program)) return replayPrograms.get(program);
|
||||
final boolean valid = GLStateManager.glIsProgram(program);
|
||||
if (!valid || !GLStateManager.isProgramPendingDeletion(program)) {
|
||||
replayPrograms.put(program, valid ? program : 0);
|
||||
}
|
||||
return valid ? program : 0;
|
||||
}
|
||||
|
||||
public CompiledDisplayList(ByteBuffer commandBuffer, DisplayListCommand[] complexObjects, DisplayListVBO ownedVbos, List<IndexedDrawBatch> indexedBatches) {
|
||||
this.commandBuffer = commandBuffer;
|
||||
|
||||
@@ -24,9 +24,9 @@ public final class GLCommand {
|
||||
public static final int USE_PROGRAM = 11; // [cmd:4][program:4]
|
||||
public static final int PUSH_ATTRIB = 12; // [cmd:4][mask:4]
|
||||
public static final int POP_ATTRIB = 13; // [cmd:4][unused:4]
|
||||
public static final int LOAD_IDENTITY = 14; // [cmd:4][matrixMode:4]
|
||||
public static final int PUSH_MATRIX = 15; // [cmd:4][matrixMode:4]
|
||||
public static final int POP_MATRIX = 16; // [cmd:4][matrixMode:4]
|
||||
public static final int LOAD_IDENTITY = 14; // [cmd:4]
|
||||
public static final int PUSH_MATRIX = 15; // [cmd:4]
|
||||
public static final int POP_MATRIX = 16; // [cmd:4]
|
||||
public static final int STENCIL_MASK = 17; // [cmd:4][mask:4]
|
||||
public static final int DEPTH_MASK = 18; // [cmd:4][flag:4] (0 or 1)
|
||||
public static final int FRONT_FACE = 19; // [cmd:4][mode:4]
|
||||
@@ -62,6 +62,7 @@ public final class GLCommand {
|
||||
public static final int CLEAR_COLOR = 55; // [cmd:4][r:4f][g:4f][b:4f][a:4f]
|
||||
public static final int BLEND_COLOR = 56; // [cmd:4][r:4f][g:4f][b:4f][a:4f]
|
||||
public static final int SECONDARY_COLOR = 57; // [cmd:4][r:4f][g:4f][b:4f]
|
||||
public static final int VERTEX_ATTRIB = 58; // [cmd:4][index:4][x:4f][y:4f][z:4f][w:4f]
|
||||
|
||||
// === Mixed int+float commands ===
|
||||
public static final int ALPHA_FUNC = 60; // [cmd:4][func:4][ref:4f]
|
||||
@@ -73,14 +74,13 @@ public final class GLCommand {
|
||||
public static final int MATERIALF = 66; // [cmd:4][face:4][pname:4][param:4f]
|
||||
public static final int TEX_PARAMETERF = 67; // [cmd:4][target:4][pname:4][param:4f]
|
||||
|
||||
// === Double commands ===
|
||||
public static final int TRANSLATE = 70; // [cmd:4][mode:4][x:8d][y:8d][z:8d] = 32 bytes
|
||||
public static final int SCALE = 72; // [cmd:4][mode:4][x:8d][y:8d][z:8d] = 32 bytes
|
||||
public static final int TRANSLATE = 70; // [cmd:4][x:4f][y:4f][z:4f] = 16 bytes
|
||||
public static final int SCALE = 72; // [cmd:4][x:4f][y:4f][z:4f] = 16 bytes
|
||||
public static final int CLEAR_DEPTH = 75; // [cmd:4][depth:8d] = 12 bytes
|
||||
|
||||
// === Matrix commands (72 bytes) ===
|
||||
public static final int MULT_MATRIX = 80; // [cmd:4][mode:4][m00-m33:64f] = 72 bytes
|
||||
public static final int LOAD_MATRIX = 81; // [cmd:4][mode:4][m00-m33:64f] = 72 bytes
|
||||
// === Matrix commands (68 bytes) ===
|
||||
public static final int MULT_MATRIX = 80; // [cmd:4][m00-m33:64f] = 68 bytes
|
||||
public static final int LOAD_MATRIX = 81; // [cmd:4][m00-m33:64f] = 68 bytes
|
||||
|
||||
// === Buffer commands (inline 4 floats max) ===
|
||||
public static final int FOG = 90; // [cmd:4][pname:4][count:4][params:16f] = 28 bytes
|
||||
@@ -157,6 +157,7 @@ public final class GLCommand {
|
||||
case CLEAR_COLOR -> "CLEAR_COLOR";
|
||||
case BLEND_COLOR -> "BLEND_COLOR";
|
||||
case SECONDARY_COLOR -> "SECONDARY_COLOR";
|
||||
case VERTEX_ATTRIB -> "VERTEX_ATTRIB";
|
||||
case ALPHA_FUNC -> "ALPHA_FUNC";
|
||||
case FOGF -> "FOGF";
|
||||
case LIGHTF -> "LIGHTF";
|
||||
@@ -221,6 +222,8 @@ public final class GLCommand {
|
||||
GLCommand.SCISSOR,
|
||||
GLCommand.COLOR, GLCommand.CLEAR_COLOR, GLCommand.BLEND_COLOR -> 20;
|
||||
|
||||
case GLCommand.VERTEX_ATTRIB -> 24;
|
||||
|
||||
// DRAW_RANGE_RESTORE: 28 bytes
|
||||
case GLCommand.DRAW_RANGE_RESTORE -> 28;
|
||||
|
||||
|
||||
@@ -176,6 +176,7 @@ public class GLSMRedirector {
|
||||
private static final Map<Integer, String> glCapRedirects = new HashMap<>();
|
||||
private static final Map<String, String> typeRedirects = new HashMap<>();
|
||||
private static final ClassConstantPoolParser cstPoolParser;
|
||||
private static final String[] CONSTANT_POOL_SEARCH_STRINGS;
|
||||
|
||||
private static final String[] CORE_EXCLUSIONS = {
|
||||
"org.lwjgl",
|
||||
@@ -826,13 +827,18 @@ public class GLSMRedirector {
|
||||
stringsToSearch.add(Display);
|
||||
stringsToSearch.addAll(typeRedirects.keySet());
|
||||
stringsToSearch.addAll(methodRedirects.keySet());
|
||||
cstPoolParser = new ClassConstantPoolParser(stringsToSearch.toArray(new String[0]));
|
||||
CONSTANT_POOL_SEARCH_STRINGS = stringsToSearch.toArray(new String[0]);
|
||||
cstPoolParser = new ClassConstantPoolParser(CONSTANT_POOL_SEARCH_STRINGS);
|
||||
}
|
||||
|
||||
public String[] getCoreExclusions() {
|
||||
return CORE_EXCLUSIONS.clone();
|
||||
}
|
||||
|
||||
public String[] getConstantPoolSearchStrings() {
|
||||
return CONSTANT_POOL_SEARCH_STRINGS.clone();
|
||||
}
|
||||
|
||||
public static String getGLPrefix() { return GL_PREFIX; }
|
||||
|
||||
public static String getTargetClassName() { return GLStateManager; }
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -40,6 +40,7 @@ public final class CowDispatch {
|
||||
public static final byte LINE = 20;
|
||||
public static final byte TEXTURE_BINDING = 21;
|
||||
public static final byte TEX_ENV = 22;
|
||||
public static final byte SCISSOR = 23;
|
||||
|
||||
public static byte kindOf(CowStateStack<?> s) {
|
||||
final Class<?> c = s.getClass();
|
||||
@@ -66,6 +67,7 @@ public final class CowDispatch {
|
||||
if (c == LineStateStack.class) return LINE;
|
||||
if (c == TextureBindingStack.class) return TEXTURE_BINDING;
|
||||
if (c == TexEnvState.class) return TEX_ENV;
|
||||
if (c == ScissorStateStack.class) return SCISSOR;
|
||||
throw new IllegalStateException("Unknown CowStateStack class: " + c.getName());
|
||||
}
|
||||
|
||||
@@ -94,6 +96,7 @@ public final class CowDispatch {
|
||||
case LINE -> ((LineStateStack) s).topSlotChanged();
|
||||
case TEXTURE_BINDING -> ((TextureBindingStack) s).topSlotChanged();
|
||||
case TEX_ENV -> ((TexEnvState) s).topSlotChanged();
|
||||
case SCISSOR -> ((ScissorStateStack) s).topSlotChanged();
|
||||
default -> throw new IllegalStateException("Unknown kind: " + kind);
|
||||
};
|
||||
}
|
||||
@@ -123,6 +126,7 @@ public final class CowDispatch {
|
||||
case LINE -> ((LineStateStack) s).restoreSlot(slot);
|
||||
case TEXTURE_BINDING -> ((TextureBindingStack) s).restoreSlot(slot);
|
||||
case TEX_ENV -> ((TexEnvState) s).restoreSlot(slot);
|
||||
case SCISSOR -> ((ScissorStateStack) s).restoreSlot(slot);
|
||||
default -> throw new IllegalStateException("Unknown kind: " + kind);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ScissorState;
|
||||
|
||||
public final class ScissorStateStack extends ScissorState implements CowStateStack<ScissorStateStack> {
|
||||
|
||||
protected final ScissorState[] stack;
|
||||
private final CowDepths cow = new CowDepths(StateSet.R_SCISSOR);
|
||||
|
||||
public ScissorStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new ScissorState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack[i] = new ScissorState();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CowDepths cowDepths() {
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreSlot(int s) {
|
||||
set(stack[s]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean topSlotChanged() {
|
||||
return !cow.isEmpty() && !sameAs(stack[cow.top()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackId() {
|
||||
return cow.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;
|
||||
}
|
||||
}
|
||||
+3
@@ -24,6 +24,9 @@ public final class TextureUnitBooleanStateStack extends BooleanStateStack {
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
if (!GLStateManager.isCachingEnabled() || enabled != this.enabled) {
|
||||
GLStateManager.beforeUncapturedTextureUnitChange(glCap, unitIndex);
|
||||
}
|
||||
beforeModify();
|
||||
setEnabledWithUnitSwitch(enabled);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ public class BooleanState implements ISettableState<BooleanState> {
|
||||
public void setEnabled(boolean enabled) {
|
||||
final boolean bypass = !GLStateManager.isCachingEnabled();
|
||||
if (stateUnknown || bypass || enabled != this.enabled || (this.glCap == GL11.GL_BLEND && GLStateManager.vendorIsAMD() && GLStateManager.isPoppingAttributes())) {
|
||||
GLStateManager.beforeUncapturedCapabilityChange(glCap);
|
||||
stateUnknown = false;
|
||||
this.enabled = enabled;
|
||||
onEnabledChanged();
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.gtnewhorizons.angelica.glsm.states;
|
||||
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
public class ScissorState implements ISettableState<ScissorState> {
|
||||
public int x;
|
||||
public int y;
|
||||
public int width;
|
||||
public int height;
|
||||
|
||||
public void setScissor(int x, int y, int width, int height) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public void get(IntBuffer params) {
|
||||
params.put(0, x);
|
||||
params.put(1, y);
|
||||
params.put(2, width);
|
||||
params.put(3, height);
|
||||
}
|
||||
|
||||
public void get(int[] params) {
|
||||
params[0] = x;
|
||||
params[1] = y;
|
||||
params[2] = width;
|
||||
params[3] = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScissorState set(ScissorState state) {
|
||||
this.x = state.x;
|
||||
this.y = state.y;
|
||||
this.width = state.width;
|
||||
this.height = state.height;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sameAs(Object state) {
|
||||
if (this == state) return true;
|
||||
if (!(state instanceof ScissorState scissorState)) return false;
|
||||
return x == scissorState.x && y == scissorState.y && width == scissorState.width && height == scissorState.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScissorState copy() {
|
||||
return new ScissorState().set(this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.glsm.texture;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface TextureStaging {
|
||||
ByteBuffer buffer();
|
||||
boolean bgra();
|
||||
int level();
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package com.gtnewhorizons.angelica.glsm.threading;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class AngelicaWorkers {
|
||||
private static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
|
||||
private static final long IDLE_TIMEOUT_SECONDS = 120;
|
||||
|
||||
private static final Object lock = new Object();
|
||||
private static final List<Runnable> idleShutdownHooks = new ArrayList<>();
|
||||
private static ExecutorService executor;
|
||||
private static ScheduledExecutorService scheduler;
|
||||
private static volatile long lastActivityTime;
|
||||
private static final AtomicInteger inFlight = new AtomicInteger(0);
|
||||
private static boolean idleCheckScheduled;
|
||||
private static int resolvedThreads;
|
||||
|
||||
private AngelicaWorkers() {}
|
||||
|
||||
private static final class Worker extends Thread {
|
||||
Worker(Runnable r, String name) {
|
||||
super(r, name);
|
||||
setDaemon(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isWorkerThread() {
|
||||
return Thread.currentThread() instanceof Worker;
|
||||
}
|
||||
|
||||
public static void onIdleShutdown(Runnable hook) {
|
||||
Objects.requireNonNull(hook);
|
||||
synchronized (lock) {
|
||||
idleShutdownHooks.add(hook);
|
||||
}
|
||||
}
|
||||
|
||||
public static void prestart() {
|
||||
executor();
|
||||
}
|
||||
|
||||
public static int threads() {
|
||||
synchronized (lock) {
|
||||
resolveThreads();
|
||||
return resolvedThreads;
|
||||
}
|
||||
}
|
||||
|
||||
private static void resolveThreads() {
|
||||
if (resolvedThreads == 0) {
|
||||
final int configured = GLSMConfig.workerThreadCount;
|
||||
resolvedThreads = configured > 0 ? configured : Math.max(2, Math.min(12, Runtime.getRuntime().availableProcessors() / 2));
|
||||
}
|
||||
}
|
||||
|
||||
private static void noteActivity() {
|
||||
lastActivityTime = System.nanoTime();
|
||||
}
|
||||
|
||||
private static ExecutorService executor() {
|
||||
synchronized (lock) {
|
||||
noteActivity();
|
||||
|
||||
if (executor != null && !executor.isShutdown()) {
|
||||
return executor;
|
||||
}
|
||||
|
||||
resolveThreads();
|
||||
final AtomicInteger threadCounter = new AtomicInteger();
|
||||
final ThreadFactory factory = r -> new Worker(r, "Angelica-Worker-" + threadCounter.getAndIncrement());
|
||||
final ThreadPoolExecutor tpe = new ThreadPoolExecutor(resolvedThreads, resolvedThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), factory);
|
||||
tpe.prestartAllCoreThreads();
|
||||
executor = tpe;
|
||||
LOGGER.debug("Created Angelica worker pool with {} prestarted threads", resolvedThreads);
|
||||
|
||||
if (scheduler == null || scheduler.isShutdown()) {
|
||||
scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
|
||||
final Thread t = new Thread(r, "Angelica-Worker-Scheduler");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
}
|
||||
if (!idleCheckScheduled) {
|
||||
idleCheckScheduled = true;
|
||||
scheduleIdleCheck(IDLE_TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
|
||||
private static void scheduleIdleCheck(long delaySeconds) {
|
||||
try {
|
||||
scheduler.schedule(AngelicaWorkers::checkIdleShutdown, delaySeconds, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
idleCheckScheduled = false;
|
||||
LOGGER.warn("Failed to schedule idle check", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> CompletableFuture<T> submit(Supplier<T> work) {
|
||||
Objects.requireNonNull(work);
|
||||
noteActivity();
|
||||
inFlight.incrementAndGet();
|
||||
try {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return work.get();
|
||||
} finally {
|
||||
noteActivity();
|
||||
inFlight.decrementAndGet();
|
||||
}
|
||||
}, executor());
|
||||
} catch (Exception e) {
|
||||
inFlight.decrementAndGet();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static CompletableFuture<Void> run(Runnable work) {
|
||||
Objects.requireNonNull(work);
|
||||
noteActivity();
|
||||
inFlight.incrementAndGet();
|
||||
try {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
work.run();
|
||||
} finally {
|
||||
noteActivity();
|
||||
inFlight.decrementAndGet();
|
||||
}
|
||||
}, executor());
|
||||
} catch (Exception e) {
|
||||
inFlight.decrementAndGet();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkIdleShutdown() {
|
||||
synchronized (lock) {
|
||||
final ExecutorService current = executor;
|
||||
if (current == null || current.isShutdown()) {
|
||||
if (scheduler != null && !scheduler.isShutdown()) {
|
||||
scheduler.shutdown();
|
||||
scheduler = null;
|
||||
}
|
||||
idleCheckScheduled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
final long idleSeconds = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - lastActivityTime);
|
||||
|
||||
if (idleSeconds >= IDLE_TIMEOUT_SECONDS && inFlight.get() == 0) {
|
||||
LOGGER.debug("Shutting down idle Angelica worker pool after {} seconds", idleSeconds);
|
||||
current.shutdown();
|
||||
executor = null;
|
||||
scheduler.shutdown();
|
||||
scheduler = null;
|
||||
idleCheckScheduled = false;
|
||||
|
||||
for (int i = 0; i < idleShutdownHooks.size(); i++) {
|
||||
idleShutdownHooks.get(i).run();
|
||||
}
|
||||
} else {
|
||||
scheduleIdleCheck(Math.max(1, IDLE_TIMEOUT_SECONDS - idleSeconds + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
out vec4 fragColor;
|
||||
|
||||
vec4 glintLayer(vec4 coord, out bool visible) {
|
||||
vec4 color = textureProj(u_Sampler0, coord) * v_Color;
|
||||
#if GLINT_OVERLAY == 2
|
||||
color.rgb = mix(color.rgb, v_Overlay.rgb, v_Overlay.a);
|
||||
#elif GLINT_OVERLAY == 1
|
||||
color.rgb = mix(color.rgb, u_OverlayColor.rgb, u_OverlayColor.a);
|
||||
#endif
|
||||
#if GLINT_LIGHTMAP
|
||||
color *= textureProj(u_Sampler1, v_TexCoord1);
|
||||
#endif
|
||||
float qa = round(color.a * 255.0);
|
||||
float ref = u_AlphaRef * 255.0;
|
||||
#if GLINT_ALPHA_FUNC == 0
|
||||
visible = false;
|
||||
#elif GLINT_ALPHA_FUNC == 1
|
||||
visible = qa < ref;
|
||||
#elif GLINT_ALPHA_FUNC == 2
|
||||
visible = qa == round(ref);
|
||||
#elif GLINT_ALPHA_FUNC == 3
|
||||
visible = qa <= ref;
|
||||
#elif GLINT_ALPHA_FUNC == 4
|
||||
visible = qa > ref;
|
||||
#elif GLINT_ALPHA_FUNC == 5
|
||||
visible = qa != round(ref);
|
||||
#elif GLINT_ALPHA_FUNC == 6
|
||||
visible = qa >= ref;
|
||||
#else
|
||||
visible = true;
|
||||
#endif
|
||||
if (!visible) return vec4(0.0);
|
||||
#if GLINT_FOG_MODE == 1
|
||||
float fog = v_FogCoord * u_FogParams.x + u_FogParams.y;
|
||||
#elif GLINT_FOG_MODE == 2
|
||||
float fog = exp2(-(v_FogCoord * u_FogParams.z));
|
||||
#elif GLINT_FOG_MODE == 3
|
||||
float fogDistance = v_FogCoord * u_FogParams.w;
|
||||
float fog = exp2(-(fogDistance * fogDistance));
|
||||
#endif
|
||||
#if GLINT_FOG_MODE != 0
|
||||
color.rgb = mix(u_FogColor.rgb, color.rgb, clamp(fog, 0.0, 1.0));
|
||||
#endif
|
||||
return color;
|
||||
}
|
||||
|
||||
void main() {
|
||||
bool visible0, visible1;
|
||||
vec4 a = glintLayer(v_TexCoord0, visible0);
|
||||
vec4 b = glintLayer(u_TextureMatrix3 * v_TexCoord0, visible1);
|
||||
if (!visible0 && !visible1) discard;
|
||||
fragColor = sqrt(a * a + b * b);
|
||||
#if GLINT_REPLACE_ALPHA
|
||||
fragColor.a = visible1 ? b.a : a.a;
|
||||
#endif
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.recording.support.DisplayListTestFixture;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import java.nio.FloatBuffer;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class DisplayListMatrixRestorationTest extends DisplayListTestFixture {
|
||||
@Test
|
||||
void flushedScalesPreserveNegativeComponentsInEveryMatrixTarget() {
|
||||
for (int compileMode : new int[] { GL11.GL_COMPILE, GL11.GL_COMPILE_AND_EXECUTE }) {
|
||||
for (int target : new int[] { GL11.GL_MODELVIEW, GL11.GL_PROJECTION, GL11.GL_TEXTURE }) {
|
||||
resetMatrices();
|
||||
int list = newList();
|
||||
GLStateManager.glNewList(list, compileMode);
|
||||
GLStateManager.glMatrixMode(target);
|
||||
GLStateManager.glScalef(-2, 3, -4);
|
||||
GLStateManager.glEndList();
|
||||
resetMatrices();
|
||||
GLStateManager.glCallList(list);
|
||||
FloatBuffer matrix = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(switch (target) {
|
||||
case GL11.GL_PROJECTION -> GL11.GL_PROJECTION_MATRIX;
|
||||
case GL11.GL_TEXTURE -> GL11.GL_TEXTURE_MATRIX;
|
||||
default -> GL11.GL_MODELVIEW_MATRIX;
|
||||
}, matrix);
|
||||
assertEquals(-2f, matrix.get(0));
|
||||
assertEquals(3f, matrix.get(5));
|
||||
assertEquals(-4f, matrix.get(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
void resetMatrices() {
|
||||
for (int unit = 0; unit < 2; unit++) {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glLoadIdentity();
|
||||
}
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
}
|
||||
|
||||
private static float translation(int target) {
|
||||
FloatBuffer b = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(target, b);
|
||||
return b.get(12);
|
||||
}
|
||||
|
||||
@Test
|
||||
void transformAttributesSaveAndRestoreLazyMode() {
|
||||
for (int mask : new int[] { GL11.GL_TRANSFORM_BIT, GL11.GL_ALL_ATTRIB_BITS }) {
|
||||
for (int compileMode : new int[] { GL11.GL_COMPILE, GL11.GL_COMPILE_AND_EXECUTE }) {
|
||||
int list = newList();
|
||||
GLStateManager.glNewList(list, compileMode);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glPushAttrib(mask);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glTranslatef(3, 0, 0);
|
||||
GLStateManager.glPopAttrib();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glEndList();
|
||||
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glTranslatef(9, 0, 0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glTranslatef(5, 0, 0);
|
||||
GLStateManager.glCallList(list);
|
||||
assertEquals(3f, translation(GL11.GL_MODELVIEW_MATRIX));
|
||||
assertEquals(0f, translation(GL11.GL_TEXTURE_MATRIX));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void nestedAttributesRestoreInheritedCallerMode() {
|
||||
int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glPushAttrib(GL11.GL_TRANSFORM_BIT);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glPopAttrib();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glPopAttrib();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glEndList();
|
||||
|
||||
GLStateManager.glTranslatef(5, 0, 0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glTranslatef(7, 0, 0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glTranslatef(9, 0, 0);
|
||||
GLStateManager.glCallList(list);
|
||||
assertEquals(5f, translation(GL11.GL_MODELVIEW_MATRIX));
|
||||
assertEquals(0f, translation(GL11.GL_PROJECTION_MATRIX));
|
||||
assertEquals(0f, translation(GL11.GL_TEXTURE_MATRIX));
|
||||
assertEquals(GL11.GL_TEXTURE, GLStateManager.glGetInteger(GL11.GL_MATRIX_MODE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void implicitMatrixCommandsAndEmptyListsInheritCallerTarget() {
|
||||
int empty = newList();
|
||||
GLStateManager.glNewList(empty, GL11.GL_COMPILE);
|
||||
GLStateManager.glEndList();
|
||||
int load = newList();
|
||||
GLStateManager.glNewList(load, GL11.GL_COMPILE);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glEndList();
|
||||
GLStateManager.glTranslatef(5, 0, 0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glTranslatef(9, 0, 0);
|
||||
GLStateManager.glCallList(empty);
|
||||
assertEquals(GL11.GL_TEXTURE, GLStateManager.glGetInteger(GL11.GL_MATRIX_MODE));
|
||||
assertEquals(9f, translation(GL11.GL_TEXTURE_MATRIX));
|
||||
GLStateManager.glCallList(load);
|
||||
assertEquals(GL11.GL_TEXTURE, GLStateManager.glGetInteger(GL11.GL_MATRIX_MODE));
|
||||
assertEquals(0f, translation(GL11.GL_TEXTURE_MATRIX));
|
||||
assertEquals(5f, translation(GL11.GL_MODELVIEW_MATRIX));
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitInitialModelviewSurvivesReplayFromTexture() {
|
||||
int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glEndList();
|
||||
GLStateManager.glTranslatef(5, 0, 0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glTranslatef(9, 0, 0);
|
||||
GLStateManager.glCallList(list);
|
||||
assertEquals(0f, translation(GL11.GL_MODELVIEW_MATRIX));
|
||||
assertEquals(9f, translation(GL11.GL_TEXTURE_MATRIX));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = { GL11.GL_MODELVIEW, GL11.GL_PROJECTION, GL11.GL_TEXTURE })
|
||||
void transformsBeforeExplicitModeInheritCallerTarget(int explicitTarget) {
|
||||
for (int compileMode : new int[] { GL11.GL_COMPILE, GL11.GL_COMPILE_AND_EXECUTE }) {
|
||||
resetMatrices();
|
||||
int list = newList();
|
||||
GLStateManager.glNewList(list, compileMode);
|
||||
GLStateManager.glTranslatef(5, 0, 0);
|
||||
GLStateManager.glMatrixMode(explicitTarget);
|
||||
GLStateManager.glTranslatef(7, 0, 0);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
for (int callerTarget : new int[] { GL11.GL_MODELVIEW, GL11.GL_PROJECTION, GL11.GL_TEXTURE }) {
|
||||
resetMatrices();
|
||||
GLStateManager.glMatrixMode(callerTarget);
|
||||
GLStateManager.glCallList(list);
|
||||
String context = "compileMode=" + compileMode + ", callerTarget=" + callerTarget;
|
||||
assertEquals((callerTarget == GL11.GL_MODELVIEW ? 5f : 0f) + (explicitTarget == GL11.GL_MODELVIEW ? 7f : 0f),
|
||||
translation(GL11.GL_MODELVIEW_MATRIX), context);
|
||||
assertEquals((callerTarget == GL11.GL_PROJECTION ? 5f : 0f) + (explicitTarget == GL11.GL_PROJECTION ? 7f : 0f),
|
||||
translation(GL11.GL_PROJECTION_MATRIX), context);
|
||||
assertEquals((callerTarget == GL11.GL_TEXTURE ? 5f : 0f) + (explicitTarget == GL11.GL_TEXTURE ? 7f : 0f),
|
||||
translation(GL11.GL_TEXTURE_MATRIX), context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = { GL11.GL_TEXTURE_BIT, GL11.GL_TEXTURE_BIT | GL11.GL_TRANSFORM_BIT, GL11.GL_ALL_ATTRIB_BITS })
|
||||
void textureAttributesFlushTransformsBeforeRestoringUnit(int mask) {
|
||||
for (int compileMode : new int[] { GL11.GL_COMPILE, GL11.GL_COMPILE_AND_EXECUTE }) {
|
||||
resetMatrices();
|
||||
int list = newList();
|
||||
GLStateManager.glNewList(list, compileMode);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glPushAttrib(mask);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glTranslatef(7, 0, 0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glTranslatef(3, 0, 0);
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLStateManager.glPopAttrib();
|
||||
GLStateManager.glPopAttrib();
|
||||
GLStateManager.glEndList();
|
||||
|
||||
resetMatrices();
|
||||
GLStateManager.glCallList(list);
|
||||
String context = "compileMode=" + compileMode;
|
||||
assertEquals(GL13.GL_TEXTURE0, GLStateManager.glGetInteger(GL13.GL_ACTIVE_TEXTURE), context);
|
||||
assertEquals(3f, translation(GL11.GL_MODELVIEW_MATRIX), context);
|
||||
assertEquals(0f, translation(GL11.GL_TEXTURE_MATRIX), context);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
assertEquals(7f, translation(GL11.GL_TEXTURE_MATRIX), context);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void pendingTextureTransformStaysOnOriginalUnit() {
|
||||
int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glTranslatef(7, 0, 0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glEndList();
|
||||
GLStateManager.glCallList(list);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
assertEquals(7f, translation(GL11.GL_TEXTURE_MATRIX));
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
assertEquals(0f, translation(GL11.GL_TEXTURE_MATRIX));
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitModeAfterNestedListIsNotDeduplicated() {
|
||||
int child = newList();
|
||||
GLStateManager.glNewList(child, GL11.GL_COMPILE);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glTranslatef(5, 0, 0);
|
||||
GLStateManager.glEndList();
|
||||
int parent = newList();
|
||||
GLStateManager.glNewList(parent, GL11.GL_COMPILE);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(child);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glEndList();
|
||||
GLStateManager.glCallList(parent);
|
||||
assertEquals(5f, translation(GL11.GL_MODELVIEW_MATRIX));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
+62
@@ -3,8 +3,10 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyIsEnabled;
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@GLCoreTest
|
||||
@@ -170,6 +172,66 @@ public class GLSM_PopAttribMaskFidelity_GLTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void scissorBoxFollowsScissorBitOnly() {
|
||||
final int[] boxA = { 10, 20, 100, 200 };
|
||||
final int[] boxB = { 30, 40, 50, 60 };
|
||||
try {
|
||||
GLStateManager.glScissor(boxA[0], boxA[1], boxA[2], boxA[3]);
|
||||
GLStateManager.disableScissorTest();
|
||||
verifyState(GL11.GL_SCISSOR_BOX, boxA, "Scissor box - Initial");
|
||||
verifyIsEnabled(GL11.GL_SCISSOR_TEST, false, "Scissor - Initial");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_SCISSOR_BIT);
|
||||
GLStateManager.enableScissorTest();
|
||||
GLStateManager.glScissor(boxB[0], boxB[1], boxB[2], boxB[3]);
|
||||
GLStateManager.glPopAttrib();
|
||||
|
||||
verifyState(GL11.GL_SCISSOR_BOX, boxA, "Scissor box - GL_SCISSOR_BIT member, must restore");
|
||||
verifyIsEnabled(GL11.GL_SCISSOR_TEST, false, "Scissor - GL_SCISSOR_BIT member, must restore");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLStateManager.enableScissorTest();
|
||||
GLStateManager.glScissor(boxB[0], boxB[1], boxB[2], boxB[3]);
|
||||
GLStateManager.glPopAttrib();
|
||||
|
||||
verifyState(GL11.GL_SCISSOR_BOX, boxB, "Scissor box - Not a GL_ENABLE_BIT member, must stay changed");
|
||||
verifyIsEnabled(GL11.GL_SCISSOR_TEST, false, "Scissor - GL_ENABLE_BIT member, must restore");
|
||||
} finally {
|
||||
GLStateManager.disableScissorTest();
|
||||
GLStateManager.glScissor(0, 0, 800, 600);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void rasterizerDiscardIsNotAnEnableBitMember() {
|
||||
try {
|
||||
verifyIsEnabled(GL30.GL_RASTERIZER_DISCARD, false, "RasterizerDiscard - Initial");
|
||||
|
||||
GLStateManager.glEnable(GL30.GL_RASTERIZER_DISCARD);
|
||||
verifyIsEnabled(GL30.GL_RASTERIZER_DISCARD, true, "RasterizerDiscard - After enable");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLStateManager.glDisable(GL30.GL_RASTERIZER_DISCARD);
|
||||
verifyIsEnabled(GL30.GL_RASTERIZER_DISCARD, false, "RasterizerDiscard - After disable inside GL_ENABLE_BIT");
|
||||
GLStateManager.glPopAttrib();
|
||||
|
||||
verifyIsEnabled(GL30.GL_RASTERIZER_DISCARD, false, "RasterizerDiscard - Not a GL_ENABLE_BIT member, must stay disabled");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL30.GL_RASTERIZER_DISCARD);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void clearDepthIsClamped() {
|
||||
try {
|
||||
GLStateManager.glClearDepth(2.0);
|
||||
verifyState(GL11.GL_DEPTH_CLEAR_VALUE, 1.0f, "Depth clear value - glClearDepth(2.0) must clamp to 1.0");
|
||||
} finally {
|
||||
GLStateManager.glClearDepth(1.0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void colorBufferBitPopDoesNotRestoreClipPlaneButMaskStaysFaithful() {
|
||||
try {
|
||||
|
||||
+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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -706,4 +706,48 @@ public class GLSM_StateSet_GLTest {
|
||||
GLStateManager.glColorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void batchAppliesPassLightingAndRestoresLiveLighting() {
|
||||
final boolean lightmapWas = GLStateManager.getTextures().getTextureUnitStates(1).isEnabled();
|
||||
final LightingSnapshot pass = new LightingSnapshot();
|
||||
try {
|
||||
GLStateManager.glEnable(GL11.GL_LIGHT0);
|
||||
GLStateManager.glEnable(GL11.GL_LIGHT1);
|
||||
GLStateManager.enableColorMaterial();
|
||||
GLStateManager.glLightf(GL11.GL_LIGHT0, GL11.GL_CONSTANT_ATTENUATION, 2.0f);
|
||||
GLStateManager.captureLighting(pass);
|
||||
|
||||
GLStateManager.glDisable(GL11.GL_LIGHT0);
|
||||
GLStateManager.glDisable(GL11.GL_LIGHT1);
|
||||
GLStateManager.disableColorMaterial();
|
||||
GLStateManager.glLightf(GL11.GL_LIGHT0, GL11.GL_CONSTANT_ATTENUATION, 1.0f);
|
||||
GLStateManager.getTextures().getTextureUnitStates(1).setEnabled(false);
|
||||
final int liveGeneration = GLStateManager.getLightingGeneration();
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.BATCH);
|
||||
GLStateManager.applyLighting(pass);
|
||||
GLStateManager.getTextures().getTextureUnitStates(1).setEnabled(true);
|
||||
assertTrue(GLStateManager.getLightStates()[0].isEnabled(), "light 0 from the pass");
|
||||
assertTrue(GLStateManager.getLightStates()[1].isEnabled(), "light 1 from the pass");
|
||||
assertTrue(GLStateManager.getColorMaterial().isEnabled(), "color material from the pass");
|
||||
assertEquals(2.0f, GLStateManager.getLightDataStates()[0].constantAttenuation, "light 0 data from the pass");
|
||||
assertTrue(GLStateManager.getLightingGeneration() != liveGeneration, "lighting uniforms marked dirty");
|
||||
final int passGeneration = GLStateManager.getLightingGeneration();
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertFalse(GLStateManager.getLightStates()[0].isEnabled(), "light 0 restored by BATCH pop");
|
||||
assertFalse(GLStateManager.getLightStates()[1].isEnabled(), "light 1 restored by BATCH pop");
|
||||
assertFalse(GLStateManager.getColorMaterial().isEnabled(), "color material restored by BATCH pop");
|
||||
assertEquals(1.0f, GLStateManager.getLightDataStates()[0].constantAttenuation, "light 0 data restored by BATCH pop");
|
||||
assertFalse(GLStateManager.getTextures().getTextureUnitStates(1).isEnabled(), "lightmap enable restored by BATCH pop");
|
||||
assertTrue(GLStateManager.getLightingGeneration() != passGeneration, "lighting uniforms marked dirty after pop");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_LIGHT0);
|
||||
GLStateManager.glDisable(GL11.GL_LIGHT1);
|
||||
GLStateManager.disableColorMaterial();
|
||||
GLStateManager.glLightf(GL11.GL_LIGHT0, GL11.GL_CONSTANT_ATTENUATION, 1.0f);
|
||||
GLStateManager.getTextures().getTextureUnitStates(1).setEnabled(lightmapWas);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ public class GLSM_ValueStackCow_GLTest {
|
||||
"glAlphaFunc", "glBlendFunc", "tryBlendFuncSeparate", "glBlendEquation", "glBlendEquationSeparate",
|
||||
"glBlendColor", "glColorMask", "glClearColor", "glDrawBuffer", "glLogicOp",
|
||||
"glDepthFunc", "glDepthMask", "glClearDepth", "glStencilFunc", "glStencilOp",
|
||||
"glStencilMask", "glClearStencil", "glViewport", "glDepthRange", "glLineWidth",
|
||||
"glStencilMask", "glClearStencil", "glViewport", "glScissor", "glDepthRange", "glLineWidth",
|
||||
"glLineStipple", "glPointSize", "glPolygonMode", "glCullFace", "glFrontFace",
|
||||
"glPolygonOffset", "fogColor", "glFogf", "glFogi", "glLightf",
|
||||
"glLighti", "glLight", "glLightModelf", "glLightModeli", "glLightModel",
|
||||
@@ -74,6 +74,7 @@ public class GLSM_ValueStackCow_GLTest {
|
||||
() -> GLStateManager.glStencilMask(0x0F),
|
||||
() -> GLStateManager.glClearStencil(9),
|
||||
() -> GLStateManager.glViewport(1, 2, 3, 4),
|
||||
() -> GLStateManager.glScissor(1, 2, 3, 4),
|
||||
() -> GLStateManager.glDepthRange(0.2, 0.8),
|
||||
() -> GLStateManager.glLineWidth(3.0f),
|
||||
() -> GLStateManager.glLineStipple(3, (short) 0x00FF),
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@GLCoreTest
|
||||
class ReadbackGLTest {
|
||||
|
||||
@AfterEach
|
||||
void noErrors() {
|
||||
ReadbackFixture.resetPack();
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rgba8() {
|
||||
ReadbackFixture.rgba8();
|
||||
}
|
||||
|
||||
@Test
|
||||
void packState() {
|
||||
ReadbackFixture.packState();
|
||||
}
|
||||
|
||||
@Test
|
||||
void pixelPackBuffer() {
|
||||
ReadbackFixture.pixelPackBuffer();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rgb8() {
|
||||
ReadbackFixture.rgb8();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rgba16f() {
|
||||
ReadbackFixture.rgba16f();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rgba32f() {
|
||||
ReadbackFixture.rgba32f();
|
||||
}
|
||||
|
||||
@Test
|
||||
void r8() {
|
||||
ReadbackFixture.r8();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rg16f() {
|
||||
ReadbackFixture.rg16f();
|
||||
}
|
||||
|
||||
@Test
|
||||
void rgb10a2() {
|
||||
ReadbackFixture.rgb10a2();
|
||||
}
|
||||
|
||||
@Test
|
||||
void r11g11b10f() {
|
||||
ReadbackFixture.r11g11b10f();
|
||||
}
|
||||
|
||||
@Test
|
||||
void depthStencil() {
|
||||
ReadbackFixture.depthStencil();
|
||||
}
|
||||
}
|
||||
+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));
|
||||
|
||||
+11
-11
@@ -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();
|
||||
@@ -206,7 +206,7 @@ class InstancedFFPDrawGLTest {
|
||||
|
||||
assertPixel(51, 50, 255, 0, 0, "entity -1 must render red");
|
||||
assertPixel(150, 50, 0, 255, 0, "entity 7 must render green");
|
||||
assertPixel(249, 50, 0, 0, 255, "entity 42 must render blue");
|
||||
assertPixel(249, 50, 0, 0, 255, "entity 50020 must render blue");
|
||||
}
|
||||
|
||||
private static void assertPixel(int x, int y, int r, int g, int b, String label) {
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLCoreTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@GLCoreTest
|
||||
class LogicOpGLTest {
|
||||
|
||||
private static final int SIZE = 8;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
FfpFixture.resetFfpState();
|
||||
ShaderManager.enable();
|
||||
ShaderManager.getInstance().activate();
|
||||
GLStateManager.glViewport(0, 0, SIZE, SIZE);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
GLStateManager.glDisable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
GLStateManager.glDisable(GL11.GL_COLOR_LOGIC_OP);
|
||||
GLStateManager.glLogicOp(GL11.GL_COPY);
|
||||
GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
LogicOpFixture.deleteResources();
|
||||
FfpFixture.resetFfpState();
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
void everyOpMatchesTheBitwiseReference() {
|
||||
for (int op : LogicOpFixture.OPS) {
|
||||
for (int[] src : LogicOpFixture.SOURCES) {
|
||||
LogicOpFixture.clearToDst();
|
||||
LogicOpFixture.drawQuad(op, src);
|
||||
assertRgb(op, src);
|
||||
}
|
||||
}
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void blendingIsIgnoredWhileLogicOpIsEnabled() {
|
||||
GLStateManager.glEnable(GL11.GL_BLEND);
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
|
||||
final int[] src = LogicOpFixture.SOURCES[1];
|
||||
LogicOpFixture.clearToDst();
|
||||
LogicOpFixture.drawQuad(GL11.GL_XOR, src);
|
||||
assertRgb(GL11.GL_XOR, src);
|
||||
}
|
||||
|
||||
private static void assertRgb(int op, int[] src) {
|
||||
final int[] px = FfpFixture.readPixel(SIZE / 2, SIZE / 2);
|
||||
final String label = LogicOpFixture.label(op, src);
|
||||
assertEquals(LogicOpFixture.expected(op, src[0], LogicOpFixture.DST_R), px[0], () -> label + " red");
|
||||
assertEquals(LogicOpFixture.expected(op, src[1], LogicOpFixture.DST_G), px[1], () -> label + " green");
|
||||
assertEquals(LogicOpFixture.expected(op, src[2], LogicOpFixture.DST_B), px[2], () -> label + " blue");
|
||||
}
|
||||
}
|
||||
+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");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.gtnewhorizons.angelica.glsm.profiling;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class TracyOptionsTest {
|
||||
|
||||
@Test
|
||||
void resolveBooleanFallsBackToConfigWhenNoOverride() {
|
||||
assertTrue(TracyOptions.resolveBoolean(null, true));
|
||||
assertFalse(TracyOptions.resolveBoolean(null, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveBooleanOverrideWinsEitherWay() {
|
||||
assertTrue(TracyOptions.resolveBoolean(Boolean.TRUE, false));
|
||||
assertFalse(TracyOptions.resolveBoolean(Boolean.FALSE, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveMaxSrcLocsFallsBackToConfigWhenNoOverride() {
|
||||
assertEquals(4096, TracyOptions.resolveMaxSrcLocs(null, 4096));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveMaxSrcLocsOverrideWins() {
|
||||
assertEquals(8192, TracyOptions.resolveMaxSrcLocs(8192, 4096));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveMaxSrcLocsClampsBothConfigAndOverrideToTheMinimum() {
|
||||
assertEquals(16, TracyOptions.resolveMaxSrcLocs(null, 4));
|
||||
assertEquals(16, TracyOptions.resolveMaxSrcLocs(1, 4096));
|
||||
}
|
||||
|
||||
@Test
|
||||
void latchExposesConfigValuesThroughTheAccessors() {
|
||||
TracyOptions.latch(true, true, true, 8192);
|
||||
assertTrue(TracyOptions.configEnabled());
|
||||
assertTrue(TracyOptions.configRemote());
|
||||
assertTrue(TracyOptions.configFineZones());
|
||||
assertEquals(8192, TracyOptions.configMaxSrcLocs());
|
||||
|
||||
TracyOptions.latch(false, false, false, 4096);
|
||||
assertFalse(TracyOptions.configEnabled());
|
||||
assertFalse(TracyOptions.configRemote());
|
||||
assertFalse(TracyOptions.configFineZones());
|
||||
assertEquals(4096, TracyOptions.configMaxSrcLocs());
|
||||
}
|
||||
|
||||
@Test
|
||||
void allowRemoteHasNoJvmOverrideAndTracksConfigDirectly() {
|
||||
TracyOptions.latch(false, true, false, 4096);
|
||||
assertTrue(TracyOptions.allowRemote());
|
||||
|
||||
TracyOptions.latch(false, false, false, 4096);
|
||||
assertFalse(TracyOptions.allowRemote());
|
||||
}
|
||||
}
|
||||
+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();
|
||||
|
||||
@@ -264,6 +264,15 @@ class GLSMRedirectorTest {
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void pixelPackBufferReadbackOverloadsHaveDeclaredTargets() throws Exception {
|
||||
assertRedirectedToDeclaredTarget(new String[][]{
|
||||
{"org/lwjgl/opengl/GL11", "glReadPixels", "(IIIIIIJ)V"},
|
||||
{"org/lwjgl/opengl/GL11C", "glReadPixels", "(IIIIIIJ)V"},
|
||||
{"org/lwjgl/opengl/GL11", "glGetTexImage", "(IIIIJ)V"},
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void newlyMappedOcclusionQueryAliasIsRedirected() {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/ARBOcclusionQuery", "glGenQueriesARB", "(Ljava/nio/IntBuffer;)V");
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public final class ReadbackFixture {
|
||||
|
||||
public static final int SIZE = 4;
|
||||
|
||||
private ReadbackFixture() {}
|
||||
|
||||
public static int colorFbo(int internalFormat, int format, int type) {
|
||||
return colorFbo(internalFormat, format, type, null);
|
||||
}
|
||||
|
||||
public static int colorFbo(int internalFormat, int format, int type, ByteBuffer data) {
|
||||
final int tex = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, internalFormat, SIZE, SIZE, 0, format, type, data);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
final int fbo = GLStateManager.glGenFramebuffers();
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, fbo);
|
||||
GLStateManager.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, tex, 0);
|
||||
GLStateManager.glViewport(0, 0, SIZE, SIZE);
|
||||
return fbo;
|
||||
}
|
||||
|
||||
public static int depthStencilFbo() {
|
||||
final int fbo = colorFbo(GL11.GL_RGBA8, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE);
|
||||
final int rb = GLStateManager.glGenRenderbuffers();
|
||||
GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, rb);
|
||||
GLStateManager.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_DEPTH24_STENCIL8, SIZE, SIZE);
|
||||
GLStateManager.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, rb);
|
||||
return fbo;
|
||||
}
|
||||
|
||||
public static void release(int fbo) {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
GLStateManager.glDeleteFramebuffers(fbo);
|
||||
resetPack();
|
||||
}
|
||||
|
||||
public static void clearColor(float r, float g, float b, float a) {
|
||||
GLStateManager.glClearColor(r, g, b, a);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
public static void clearTopHalf(float r, float g, float b, float a) {
|
||||
GLStateManager.glEnable(GL11.GL_SCISSOR_TEST);
|
||||
GLStateManager.glScissor(0, SIZE / 2, SIZE, SIZE / 2);
|
||||
clearColor(r, g, b, a);
|
||||
GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
public static void resetPack() {
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 0);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_PIXELS, 0);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 0);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_SWAP_BYTES, GL11.GL_FALSE);
|
||||
}
|
||||
|
||||
public static ByteBuffer read(int format, int type, int bytes) {
|
||||
final ByteBuffer out = BufferUtils.createByteBuffer(bytes);
|
||||
for (int i = 0; i < bytes; i++) out.put(i, (byte) 0x7E);
|
||||
GLStateManager.glReadPixels(0, 0, SIZE, SIZE, format, type, out);
|
||||
return out.order(ByteOrder.nativeOrder());
|
||||
}
|
||||
|
||||
public static void assertBytes(ByteBuffer buf, int offset, int[] expected, String label) {
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
final int idx = i;
|
||||
assertEquals(expected[i], buf.get(offset + i) & 0xFF, () -> label + " byte " + idx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertFloats(ByteBuffer buf, int offset, float[] expected, String label) {
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
final int idx = i;
|
||||
assertEquals(expected[i], buf.getFloat(offset + i * 4), () -> label + " float " + idx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertFloatsNear(ByteBuffer buf, int offset, float[] expected, String label) {
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
final int idx = i;
|
||||
assertEquals(expected[i], buf.getFloat(offset + i * 4), 1f / 4096f, () -> label + " float " + idx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertShorts(ByteBuffer buf, int offset, int[] expected, String label) {
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
final int idx = i;
|
||||
assertEquals(expected[i], buf.getShort(offset + i * 2) & 0xFFFF, () -> label + " short " + idx);
|
||||
}
|
||||
}
|
||||
|
||||
public static void rgba8() {
|
||||
final int fbo = colorFbo(GL11.GL_RGBA8, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE);
|
||||
clearColor(51 / 255f, 102 / 255f, 153 / 255f, 204 / 255f);
|
||||
clearTopHalf(1f, 0f, 0f, 1f);
|
||||
|
||||
final ByteBuffer rgba = read(GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, SIZE * SIZE * 4);
|
||||
assertBytes(rgba, 0, new int[] { 51, 102, 153, 204 }, "RGBA8 as RGBA/UBYTE bottom row");
|
||||
assertBytes(rgba, (SIZE - 1) * SIZE * 4, new int[] { 255, 0, 0, 255 }, "RGBA8 as RGBA/UBYTE top row");
|
||||
|
||||
assertBytes(read(GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, SIZE * SIZE * 4), 0, new int[] { 153, 102, 51, 204 }, "RGBA8 as BGRA/UBYTE");
|
||||
|
||||
final ByteBuffer rev = read(GL11.GL_RGBA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, SIZE * SIZE * 4);
|
||||
assertEquals(0xCC996633, rev.getInt(0), "RGBA8 as RGBA/8_8_8_8_REV");
|
||||
|
||||
final ByteBuffer f = read(GL11.GL_RGBA, GL11.GL_FLOAT, SIZE * SIZE * 16);
|
||||
assertFloatsNear(f, 0, new float[] { 51 / 255f, 102 / 255f, 153 / 255f, 204 / 255f }, "RGBA8 as RGBA/FLOAT");
|
||||
|
||||
final ByteBuffer rgb = read(GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, SIZE * 12);
|
||||
assertBytes(rgb, 0, new int[] { 51, 102, 153, 51, 102, 153 }, "RGBA8 as RGB/UBYTE");
|
||||
assertBytes(rgb, 12 * (SIZE - 1), new int[] { 255, 0, 0 }, "RGBA8 as RGB/UBYTE top row at 4-aligned stride");
|
||||
|
||||
assertBytes(read(GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, SIZE * SIZE), 0, new int[] { 51, 51, 51, 51 }, "RGBA8 as RED/UBYTE");
|
||||
|
||||
release(fbo);
|
||||
}
|
||||
|
||||
public static void packState() {
|
||||
final int fbo = colorFbo(GL11.GL_RGBA8, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE);
|
||||
clearColor(51 / 255f, 102 / 255f, 153 / 255f, 204 / 255f);
|
||||
clearTopHalf(1f, 0f, 0f, 1f);
|
||||
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1);
|
||||
final ByteBuffer rgb = read(GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, SIZE * SIZE * 3);
|
||||
assertBytes(rgb, (SIZE - 1) * SIZE * 3, new int[] { 255, 0, 0 }, "PACK_ALIGNMENT 1 tightly packs RGB rows");
|
||||
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 6);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_PIXELS, 1);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 2);
|
||||
final int rowBytes = 6 * 4;
|
||||
final ByteBuffer skipped = read(GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, rowBytes * (SIZE + 2));
|
||||
assertBytes(skipped, 0, new int[] { 0x7E, 0x7E, 0x7E, 0x7E }, "skipped rows untouched");
|
||||
assertBytes(skipped, 2 * rowBytes, new int[] { 0x7E, 0x7E, 0x7E, 0x7E }, "skipped pixel untouched");
|
||||
assertBytes(skipped, 2 * rowBytes + 4, new int[] { 51, 102, 153, 204 }, "first pixel after skips");
|
||||
assertBytes(skipped, (2 + SIZE - 1) * rowBytes + 4, new int[] { 255, 0, 0, 255 }, "top row at ROW_LENGTH stride");
|
||||
assertBytes(skipped, 2 * rowBytes + 4 + SIZE * 4, new int[] { 0x7E, 0x7E, 0x7E, 0x7E }, "row padding untouched");
|
||||
resetPack();
|
||||
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_SWAP_BYTES, GL11.GL_TRUE);
|
||||
final ByteBuffer swapped = read(GL11.GL_RGBA, GL30.GL_HALF_FLOAT, SIZE * SIZE * 8);
|
||||
assertBytes(swapped, 0, new int[] { 0x32, 0x66 }, "SWAP_BYTES on HALF_FLOAT");
|
||||
resetPack();
|
||||
|
||||
release(fbo);
|
||||
}
|
||||
|
||||
public static void pixelPackBuffer() {
|
||||
final int fbo = colorFbo(GL11.GL_RGBA8, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE);
|
||||
clearColor(51 / 255f, 102 / 255f, 153 / 255f, 204 / 255f);
|
||||
final int pbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, pbo);
|
||||
GLStateManager.glBufferData(GL21.GL_PIXEL_PACK_BUFFER, 16 + SIZE * SIZE * 4, GL15.GL_STREAM_READ);
|
||||
GLStateManager.glReadPixels(0, 0, SIZE, SIZE, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, 16L);
|
||||
final ByteBuffer mapped = GLStateManager.glMapBuffer(GL21.GL_PIXEL_PACK_BUFFER, GL15.GL_READ_ONLY);
|
||||
assertTrue(mapped != null, "PBO map");
|
||||
assertBytes(mapped, 16, new int[] { 51, 102, 153, 204 }, "PBO readPixels at offset 16");
|
||||
GLStateManager.glUnmapBuffer(GL21.GL_PIXEL_PACK_BUFFER);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
|
||||
GLStateManager.glDeleteBuffers(pbo);
|
||||
release(fbo);
|
||||
}
|
||||
|
||||
public static void rgb8() {
|
||||
final int fbo = colorFbo(GL11.GL_RGB8, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE);
|
||||
clearColor(51 / 255f, 102 / 255f, 153 / 255f, 0.5f);
|
||||
assertBytes(read(GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, SIZE * SIZE * 4), 0, new int[] { 51, 102, 153, 255 }, "RGB8 as RGBA reads alpha 1");
|
||||
release(fbo);
|
||||
}
|
||||
|
||||
public static void rgba16f() {
|
||||
final int fbo = colorFbo(GL30.GL_RGBA16F, GL11.GL_RGBA, GL11.GL_FLOAT);
|
||||
clearColor(0.25f, 0.625f, 0.75f, 2.0f);
|
||||
assertFloats(read(GL11.GL_RGBA, GL11.GL_FLOAT, SIZE * SIZE * 16), 0, new float[] { 0.25f, 0.625f, 0.75f, 2.0f }, "RGBA16F as RGBA/FLOAT keeps >1");
|
||||
assertShorts(read(GL11.GL_RGBA, GL30.GL_HALF_FLOAT, SIZE * SIZE * 8), 0, new int[] { 0x3400, 0x3900, 0x3A00, 0x4000 }, "RGBA16F as RGBA/HALF_FLOAT");
|
||||
assertBytes(read(GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, SIZE * SIZE * 4), 0, new int[] { 64, 159, 191, 255 }, "RGBA16F as RGBA/UBYTE clamps");
|
||||
release(fbo);
|
||||
}
|
||||
|
||||
public static void rgba32f() {
|
||||
final int fbo = colorFbo(GL30.GL_RGBA32F, GL11.GL_RGBA, GL11.GL_FLOAT);
|
||||
clearColor(0.2f, -0.5f, 3.0f, 1.0f);
|
||||
assertFloats(read(GL11.GL_RGBA, GL11.GL_FLOAT, SIZE * SIZE * 16), 0, new float[] { 0.2f, -0.5f, 3.0f, 1.0f }, "RGBA32F as RGBA/FLOAT");
|
||||
assertBytes(read(GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, SIZE * SIZE * 4), 0, new int[] { 51, 0, 255, 255 }, "RGBA32F as RGBA/UBYTE clamps both ends");
|
||||
release(fbo);
|
||||
}
|
||||
|
||||
public static void r8() {
|
||||
final int fbo = colorFbo(GL30.GL_R8, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE);
|
||||
clearColor(0.2f, 0.9f, 0.9f, 0.9f);
|
||||
assertBytes(read(GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, SIZE * SIZE * 4), 0, new int[] { 51, 0, 0, 255 }, "R8 as RGBA rebases G/B=0 A=1");
|
||||
release(fbo);
|
||||
}
|
||||
|
||||
public static void rg16f() {
|
||||
final int fbo = colorFbo(GL30.GL_RG16F, GL30.GL_RG, GL11.GL_FLOAT);
|
||||
clearColor(0.25f, 0.625f, 0.9f, 0.9f);
|
||||
assertFloats(read(GL11.GL_RGBA, GL11.GL_FLOAT, SIZE * SIZE * 16), 0, new float[] { 0.25f, 0.625f, 0f, 1f }, "RG16F as RGBA/FLOAT");
|
||||
release(fbo);
|
||||
}
|
||||
|
||||
public static void rgb10a2() {
|
||||
final int texel = (1 << 30) | (614 << 20) | (409 << 10) | 205;
|
||||
final ByteBuffer data = BufferUtils.createByteBuffer(SIZE * SIZE * 4).order(ByteOrder.nativeOrder());
|
||||
for (int i = 0; i < SIZE * SIZE; i++) data.putInt(i * 4, texel);
|
||||
final int fbo = colorFbo(GL11.GL_RGB10_A2, GL11.GL_RGBA, GL12.GL_UNSIGNED_INT_2_10_10_10_REV, data);
|
||||
final ByteBuffer raw = read(GL11.GL_RGBA, GL12.GL_UNSIGNED_INT_2_10_10_10_REV, SIZE * SIZE * 4);
|
||||
assertEquals(texel, raw.getInt(0), "RGB10A2 as RGBA/2_10_10_10_REV");
|
||||
assertBytes(read(GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, SIZE * SIZE * 4), 0, new int[] { 51, 102, 153, 85 }, "RGB10A2 as RGBA/UBYTE");
|
||||
release(fbo);
|
||||
}
|
||||
|
||||
public static void r11g11b10f() {
|
||||
final int fbo = colorFbo(GL30.GL_R11F_G11F_B10F, GL11.GL_RGB, GL11.GL_FLOAT);
|
||||
clearColor(0.25f, 0.625f, 1.5f, 1f);
|
||||
assertFloats(read(GL11.GL_RGBA, GL11.GL_FLOAT, SIZE * SIZE * 16), 0, new float[] { 0.25f, 0.625f, 1.5f, 1f }, "R11G11B10F as RGBA/FLOAT");
|
||||
release(fbo);
|
||||
}
|
||||
|
||||
public static void depthStencil() {
|
||||
final int fbo = depthStencilFbo();
|
||||
GLStateManager.glDepthMask(true);
|
||||
GLStateManager.glStencilMask(0xFF);
|
||||
GLStateManager.glClearDepth(0.25);
|
||||
GLStateManager.glClearStencil(0xC3);
|
||||
GLStateManager.glClear(GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_SCISSOR_TEST);
|
||||
GLStateManager.glScissor(0, SIZE / 2, SIZE, SIZE / 2);
|
||||
GLStateManager.glClearDepth(0.75);
|
||||
GLStateManager.glClearStencil(0x5A);
|
||||
GLStateManager.glClear(GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
|
||||
GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
GLStateManager.glClearDepth(1.0);
|
||||
GLStateManager.glClearStencil(0);
|
||||
|
||||
final ByteBuffer depth = read(GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, SIZE * SIZE * 4);
|
||||
assertEquals(0.25f, depth.getFloat(0), 1e-6f, "depth bottom row as FLOAT");
|
||||
assertEquals(0.75f, depth.getFloat((SIZE - 1) * SIZE * 4), 1e-6f, "depth top row as FLOAT");
|
||||
|
||||
final ByteBuffer depthUs = read(GL11.GL_DEPTH_COMPONENT, GL11.GL_UNSIGNED_SHORT, SIZE * SIZE * 2);
|
||||
assertEquals(16384, depthUs.getShort(0) & 0xFFFF, 1, "depth as UNSIGNED_SHORT");
|
||||
|
||||
final ByteBuffer depthUi = read(GL11.GL_DEPTH_COMPONENT, GL11.GL_UNSIGNED_INT, SIZE * SIZE * 4);
|
||||
assertEquals(0x40000000L >>> 8, (depthUi.getInt(0) & 0xFFFFFFFFL) >>> 8, 1, "depth as UNSIGNED_INT (top 24 bits)");
|
||||
|
||||
final ByteBuffer stencil = read(GL11.GL_STENCIL_INDEX, GL11.GL_UNSIGNED_BYTE, SIZE * SIZE);
|
||||
assertBytes(stencil, 0, new int[] { 0xC3, 0xC3, 0xC3, 0xC3 }, "stencil bottom row");
|
||||
assertBytes(stencil, (SIZE - 1) * SIZE, new int[] { 0x5A, 0x5A, 0x5A, 0x5A }, "stencil top row");
|
||||
|
||||
final ByteBuffer ds = read(GL30.GL_DEPTH_STENCIL, GL30.GL_UNSIGNED_INT_24_8, SIZE * SIZE * 4);
|
||||
final int packed = ds.getInt(0);
|
||||
assertEquals(0xC3, packed & 0xFF, "DEPTH_STENCIL stencil byte");
|
||||
assertEquals(0x400000, (packed >>> 8) & 0xFFFFFF, 1, "DEPTH_STENCIL depth 24 bits");
|
||||
|
||||
release(fbo);
|
||||
}
|
||||
}
|
||||
+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);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,16 +30,17 @@ public final class FfpFixture {
|
||||
layout(location = 5) in vec4 a_InstRow0;
|
||||
layout(location = 6) in vec4 a_InstRow1;
|
||||
layout(location = 7) in vec4 a_InstRow2;
|
||||
layout(location = 11) in ivec4 iris_Entity;
|
||||
layout(location = 11) in uvec4 iris_Entity;
|
||||
flat out vec3 v_Color;
|
||||
void main() {
|
||||
""";
|
||||
|
||||
private static final String VS_TAIL = """
|
||||
gl_Position = instMV * vec4(a_Position, 1.0);
|
||||
if (iris_Entity.x == -1) { v_Color = vec3(1.0, 0.0, 0.0); }
|
||||
else if (iris_Entity.x == 7) { v_Color = vec3(0.0, 1.0, 0.0); }
|
||||
else { v_Color = vec3(0.0, 0.0, 1.0); }
|
||||
if (iris_Entity.x == 65535u) { v_Color = vec3(1.0, 0.0, 0.0); }
|
||||
else if (iris_Entity.x == 7u) { v_Color = vec3(0.0, 1.0, 0.0); }
|
||||
else if (iris_Entity.x == 50020u) { v_Color = vec3(0.0, 0.0, 1.0); }
|
||||
else { v_Color = vec3(1.0, 0.0, 1.0); }
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -64,7 +65,7 @@ public final class FfpFixture {
|
||||
GLStateManager.glUseProgram(program);
|
||||
|
||||
buildTemplate();
|
||||
buildInstances(new float[] { -spacing, 0.0f, spacing }, new int[] { -1, 7, 42 });
|
||||
buildInstances(new float[] { -spacing, 0.0f, spacing }, new int[] { -1, 7, 50020 });
|
||||
}
|
||||
|
||||
public static void draw() {
|
||||
@@ -244,10 +245,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);
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
public final class LogicOpFixture {
|
||||
|
||||
public static final int[] OPS = {
|
||||
GL11.GL_CLEAR, GL11.GL_AND, GL11.GL_AND_REVERSE, GL11.GL_COPY,
|
||||
GL11.GL_AND_INVERTED, GL11.GL_NOOP, GL11.GL_XOR, GL11.GL_OR,
|
||||
GL11.GL_NOR, GL11.GL_EQUIV, GL11.GL_INVERT, GL11.GL_OR_REVERSE,
|
||||
GL11.GL_COPY_INVERTED, GL11.GL_OR_INVERTED, GL11.GL_NAND, GL11.GL_SET
|
||||
};
|
||||
|
||||
public static final int DST_R = 0x3C;
|
||||
public static final int DST_G = 0xA5;
|
||||
public static final int DST_B = 0xF0;
|
||||
public static final int DST_A = 0x5A;
|
||||
|
||||
public static final int[][] SOURCES = {
|
||||
{ 0x00, 0x00, 0xFF, 0xFF },
|
||||
{ 0x33, 0x99, 0xE6, 0x80 },
|
||||
{ 0xFF, 0xFF, 0xFF, 0xFF },
|
||||
};
|
||||
|
||||
private static int vao;
|
||||
private static int vbo;
|
||||
|
||||
private LogicOpFixture() {}
|
||||
|
||||
public static int expected(int op, int s, int d) {
|
||||
final int r = switch (op) {
|
||||
case GL11.GL_CLEAR -> 0;
|
||||
case GL11.GL_AND -> s & d;
|
||||
case GL11.GL_AND_REVERSE -> s & ~d;
|
||||
case GL11.GL_COPY -> s;
|
||||
case GL11.GL_AND_INVERTED -> ~s & d;
|
||||
case GL11.GL_NOOP -> d;
|
||||
case GL11.GL_XOR -> s ^ d;
|
||||
case GL11.GL_OR -> s | d;
|
||||
case GL11.GL_NOR -> ~(s | d);
|
||||
case GL11.GL_EQUIV -> ~(s ^ d);
|
||||
case GL11.GL_INVERT -> ~d;
|
||||
case GL11.GL_OR_REVERSE -> s | ~d;
|
||||
case GL11.GL_COPY_INVERTED -> ~s;
|
||||
case GL11.GL_OR_INVERTED -> ~s | d;
|
||||
case GL11.GL_NAND -> ~(s & d);
|
||||
case GL11.GL_SET -> 0xFF;
|
||||
default -> throw new IllegalArgumentException("op 0x" + Integer.toHexString(op));
|
||||
};
|
||||
return r & 0xFF;
|
||||
}
|
||||
|
||||
public static void clearToDst() {
|
||||
GLStateManager.glClearColor(DST_R / 255f, DST_G / 255f, DST_B / 255f, DST_A / 255f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
public static void drawQuad(int op, int[] src) {
|
||||
GLStateManager.glEnable(GL11.GL_COLOR_LOGIC_OP);
|
||||
GLStateManager.glLogicOp(op);
|
||||
quad(src);
|
||||
GLStateManager.glDisable(GL11.GL_COLOR_LOGIC_OP);
|
||||
GLStateManager.glLogicOp(GL11.GL_COPY);
|
||||
}
|
||||
|
||||
public static void quad(int[] src) {
|
||||
if (vao == 0) {
|
||||
vao = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
vbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
final FloatBuffer positions = BufferUtils.createFloatBuffer(12);
|
||||
positions.put(new float[] { -1f, -1f, 0f, 1f, -1f, 0f, 1f, 1f, 0f, -1f, 1f, 0f }).flip();
|
||||
GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, positions, GL15.GL_STATIC_DRAW);
|
||||
FfpFixture.attrib(0, 3, GL11.GL_FLOAT, false, 12, 0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
VAOManager.setCurrentVertexFlags(0);
|
||||
GLStateManager.glColor4f(src[0] / 255f, src[1] / 255f, src[2] / 255f, src[3] / 255f);
|
||||
GLStateManager.glDrawArrays(GL11.GL_QUADS, 0, 4);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
public static void deleteResources() {
|
||||
if (vbo != 0) { GLStateManager.glDeleteBuffers(vbo); vbo = 0; }
|
||||
if (vao != 0) { GLStateManager.glDeleteVertexArrays(vao); vao = 0; }
|
||||
}
|
||||
|
||||
public static String label(int op, int[] src) {
|
||||
return "op=0x" + Integer.toHexString(op) + " src=" + src[0] + "," + src[1] + "," + src[2] + "," + src[3];
|
||||
}
|
||||
}
|
||||
+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();
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,13 @@ asmCompile = "5.0.3"
|
||||
lwjgl3 = "3.4.2-SNAPSHOT"
|
||||
lwjgl3ify = "3.0.31"
|
||||
|
||||
celeritas = "2.5.11-GTNH"
|
||||
celeritas = "2.5.12-GTNH"
|
||||
gtnhlib = "0.11.36"
|
||||
eventbus = "7.0-beta.15"
|
||||
glslTransformLib = "0.2.0-33.g3ec381e"
|
||||
antlr4Runtime = "4.13.2"
|
||||
jcpp = "1.4.14"
|
||||
tracy = "0.14.0"
|
||||
tracy = "0.14.1"
|
||||
asyncProfiler = "4.4"
|
||||
retroFuturaBootstrap = "1.1.0"
|
||||
|
||||
|
||||
+31
-1
@@ -10,6 +10,7 @@ import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
|
||||
import me.eigenraven.lwjgl3ify.api.Lwjgl3Aware;
|
||||
import me.eigenraven.lwjgl3ify.client.MainThreadExec;
|
||||
import org.lwjgl.PointerBuffer;
|
||||
import org.lwjglx.Lwjgl3ifyEventLoop;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -59,6 +60,7 @@ import java.nio.ShortBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
@@ -73,6 +75,7 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
private GLCapabilities caps;
|
||||
private GLDebugMessageCallback debugCallback;
|
||||
private boolean debugOutputActive;
|
||||
private boolean amdShaderSourceWorkaround;
|
||||
|
||||
private SDL_EventFilter dropEventFilter;
|
||||
private final ConcurrentLinkedQueue<String> droppedFiles = new ConcurrentLinkedQueue<>();
|
||||
@@ -82,6 +85,15 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
@Override
|
||||
public void init() {
|
||||
caps = GL.getCapabilities();
|
||||
if (GLStateManager.isWindows()) {
|
||||
final String vendor = GL11C.glGetString(GL11C.GL_VENDOR);
|
||||
if (vendor != null) {
|
||||
final String normalizedVendor = vendor.toLowerCase(Locale.ROOT);
|
||||
amdShaderSourceWorkaround = normalizedVendor.startsWith("amd")
|
||||
|| normalizedVendor.startsWith("ati technologies")
|
||||
|| normalizedVendor.startsWith("advanced micro devices");
|
||||
}
|
||||
}
|
||||
if (RenderSystem.isGLES()) {
|
||||
pfnClearDepthf = GL.getFunctionProvider().getFunctionAddress("glClearDepthf");
|
||||
pfnDepthRangef = GL.getFunctionProvider().getFunctionAddress("glDepthRangef");
|
||||
@@ -778,6 +790,11 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
GL11C.glReadPixels(x, y, width, height, format, type, pixels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readPixels(int x, int y, int width, int height, int format, int type, long pixelBufferOffset) {
|
||||
GL11C.glReadPixels(x, y, width, height, format, type, pixelBufferOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getTexImage(int target, int level, int format, int type, ByteBuffer pixels) {
|
||||
GL11C.glGetTexImage(target, level, format, type, pixels);
|
||||
@@ -810,7 +827,20 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
|
||||
@Override
|
||||
public void shaderSource(int shader, CharSequence source) {
|
||||
GL20C.glShaderSource(shader, source);
|
||||
if (!amdShaderSourceWorkaround) {
|
||||
GL20C.glShaderSource(shader, source);
|
||||
return;
|
||||
}
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
final ByteBuffer encodedSource = MemoryUtil.memUTF8(source, true);
|
||||
try {
|
||||
final PointerBuffer pointers = stack.mallocPointer(1);
|
||||
pointers.put(encodedSource);
|
||||
GL20C.nglShaderSource(shader, 1, pointers.address0(), 0L);
|
||||
} finally {
|
||||
MemoryUtil.memFree(encodedSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -104,6 +104,9 @@ if [ -n "$JFR_OUT" ]; then
|
||||
PROFILE_ARGS+=(-Dangelica.profile.opts="$ASPROF_OPTS")
|
||||
fi
|
||||
fi
|
||||
if [ "$MODE" = "tracy" ]; then
|
||||
PROFILE_ARGS+=(-Dangelica.tracy=true)
|
||||
fi
|
||||
|
||||
if [ "$MODE" = "tracy" ]; then
|
||||
if ! command -v "$TRACY_CAPTURE" >/dev/null 2>&1 && [ ! -x "$TRACY_CAPTURE" ]; then
|
||||
|
||||
+540
-182
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,10 @@
|
||||
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.resource.PackState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.LogicOpVariant;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.UniformStaging;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
|
||||
@@ -12,6 +15,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import org.lwjgl.PointerBuffer;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
@@ -48,12 +52,17 @@ public final class ContextState {
|
||||
public int unpackSkipPixels = 0;
|
||||
public int unpackSkipRows = 0;
|
||||
public int packAlignment = 4;
|
||||
public int packRowLength = 0;
|
||||
public int packSkipPixels = 0;
|
||||
public int packSkipRows = 0;
|
||||
public boolean packSwapBytes;
|
||||
public boolean isDefault() {
|
||||
return unpackAlignment == 4 && unpackRowLength == 0 && unpackSkipPixels == 0 && unpackSkipRows == 0 && packAlignment == 4;
|
||||
}
|
||||
}
|
||||
|
||||
public final PixelStoreState pixelStore = new PixelStoreState();
|
||||
public final PackState packState = new PackState();
|
||||
|
||||
public static final class ProxyTextureState {
|
||||
public int target;
|
||||
@@ -113,6 +122,7 @@ public final class ContextState {
|
||||
public float viewportDepthFar = 1.0f;
|
||||
public int scissorX, scissorY, scissorW, scissorH;
|
||||
public boolean scissorEnabled;
|
||||
public boolean rasterizerDiscard;
|
||||
public int activeTextureUnit;
|
||||
public int boundProgram;
|
||||
public int autoPushedProgram;
|
||||
@@ -124,6 +134,21 @@ public final class ContextState {
|
||||
public int ssboBinds;
|
||||
|
||||
public float blendColorR, blendColorG, blendColorB, blendColorA;
|
||||
public boolean logicOpEnabled;
|
||||
public int logicOpMode = GL11.GL_COPY;
|
||||
public long appliedLogicOpKey;
|
||||
public LogicOpVariant activeLogicOpVariant;
|
||||
public final int[] blitRect = new int[8];
|
||||
public final long[] logicOpScratch = new long[MAX_COLOR_ATTACHMENTS];
|
||||
public final int[] logicOpScratchFormat = new int[MAX_COLOR_ATTACHMENTS];
|
||||
public final int[] logicOpScratchWidth = new int[MAX_COLOR_ATTACHMENTS];
|
||||
public final int[] logicOpScratchHeight = new int[MAX_COLOR_ATTACHMENTS];
|
||||
public long readbackDepthScratch;
|
||||
public int readbackDepthScratchWidth;
|
||||
public int readbackDepthScratchHeight;
|
||||
public long readbackStencilScratch;
|
||||
public int readbackStencilScratchWidth;
|
||||
public int readbackStencilScratchHeight;
|
||||
public int stencilRef;
|
||||
public float depthClearValue = 1.0f;
|
||||
public int stencilClearValue;
|
||||
@@ -303,6 +328,7 @@ public final class ContextState {
|
||||
public final long[] lastVertSamplerTex = new long[MAX_SAMPLERS];
|
||||
public final long[] lastVertSamplerSmp = new long[MAX_SAMPLERS];
|
||||
public int lastFragSamplerProgram;
|
||||
public LogicOpVariant lastFragSamplerVariant;
|
||||
public int lastVertSamplerProgram;
|
||||
|
||||
public final PointerBuffer fragStorageTexBindings = PointerBuffer.allocateDirect(MAX_IMAGE_UNITS);
|
||||
@@ -327,9 +353,8 @@ public final class ContextState {
|
||||
|
||||
public int boundFboId;
|
||||
public int boundReadFboId;
|
||||
|
||||
public final LongOpenHashSet clearedTexturesThisFrame = new LongOpenHashSet();
|
||||
public final LongOpenHashSet clearedStencilTexturesThisFrame = new LongOpenHashSet();
|
||||
public int defaultFboId;
|
||||
public GLContextState mirroredContext;
|
||||
|
||||
public final LongOpenHashSet pendingColorTextures = new LongOpenHashSet();
|
||||
public final Long2ObjectOpenHashMap<float[]> pendingColorValues = new Long2ObjectOpenHashMap<>();
|
||||
@@ -415,6 +440,7 @@ public final class ContextState {
|
||||
lastAppliedSamplerProgram = 0;
|
||||
lastAppliedSamplerCb = 0;
|
||||
lastFragSamplerProgram = 0;
|
||||
lastFragSamplerVariant = null;
|
||||
lastVertSamplerProgram = 0;
|
||||
lastAppliedStorageTexBindGen = -1;
|
||||
lastAppliedStorageTexProgram = 0;
|
||||
|
||||
@@ -55,7 +55,9 @@ public final class FrameManager {
|
||||
public static final int PASS_END_TARGET = 3;
|
||||
public static final int PASS_END_UNIFORM_BLOCK = 4;
|
||||
public static final int PASS_END_FRAME_END = 5;
|
||||
public static final int PASS_END_CAUSE_COUNT = 6;
|
||||
public static final int PASS_END_LOGIC_OP = 6;
|
||||
public static final int PASS_END_READBACK = 7;
|
||||
public static final int PASS_END_CAUSE_COUNT = 8;
|
||||
|
||||
public static final class FrameState {
|
||||
public final Thread owner = Thread.currentThread();
|
||||
@@ -647,7 +649,11 @@ public final class FrameManager {
|
||||
|
||||
public void submitMidFrame() {
|
||||
final FrameState f = frame();
|
||||
if (f.commandBuffer == 0) return;
|
||||
if (f.commandBuffer == 0) {
|
||||
if (f.wantFenceOnNextSubmit) getOrCreatePendingUploadCommandBuffer(f);
|
||||
flushPendingUploadCommandBuffer(f);
|
||||
return;
|
||||
}
|
||||
endCopyPassIfActive(f);
|
||||
endRenderPassIfActive(f, PASS_END_FRAME_END);
|
||||
Tracy.beginZone(Z_SDL_SUBMIT);
|
||||
@@ -747,7 +753,14 @@ public final class FrameManager {
|
||||
Tracy.beginZone(Z_SDL_SUBMIT);
|
||||
final boolean submitted;
|
||||
try {
|
||||
submitted = SDL_SubmitGPUCommandBuffer(f.pendingUploadCommandBuffer);
|
||||
if (f.wantFenceOnNextSubmit) {
|
||||
f.wantFenceOnNextSubmit = false;
|
||||
if (f.lastAcquiredFence != 0) SDL_ReleaseGPUFence(device.getDevice(), f.lastAcquiredFence);
|
||||
f.lastAcquiredFence = SDL_SubmitGPUCommandBufferAndAcquireFence(f.pendingUploadCommandBuffer);
|
||||
submitted = f.lastAcquiredFence != 0;
|
||||
} else {
|
||||
submitted = SDL_SubmitGPUCommandBuffer(f.pendingUploadCommandBuffer);
|
||||
}
|
||||
} finally {
|
||||
Tracy.endZone();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -67,6 +67,8 @@ final class SdlFramePlots {
|
||||
private static final long PLOT_PASS_END_TARGET = Tracy.plotHandle("sdl.passEnd.target");
|
||||
private static final long PLOT_PASS_END_UNIFORM_BLOCK = Tracy.plotHandle("sdl.passEnd.uniformBlock");
|
||||
private static final long PLOT_PASS_END_FRAME_END = Tracy.plotHandle("sdl.passEnd.frameEnd");
|
||||
private static final long PLOT_PASS_END_LOGIC_OP = Tracy.plotHandle("sdl.passEnd.logicOp");
|
||||
private static final long PLOT_PASS_END_READBACK = Tracy.plotHandle("sdl.passEnd.readback");
|
||||
|
||||
private static final int[] contextCounters = new int[SDLGPURenderBackend.CTR_COUNT];
|
||||
|
||||
@@ -129,5 +131,7 @@ final class SdlFramePlots {
|
||||
Tracy.plotInt(PLOT_PASS_END_TARGET, f.passEndCauseCountsThisFrame[FrameManager.PASS_END_TARGET]);
|
||||
Tracy.plotInt(PLOT_PASS_END_UNIFORM_BLOCK, f.passEndCauseCountsThisFrame[FrameManager.PASS_END_UNIFORM_BLOCK]);
|
||||
Tracy.plotInt(PLOT_PASS_END_FRAME_END, f.passEndCauseCountsThisFrame[FrameManager.PASS_END_FRAME_END]);
|
||||
Tracy.plotInt(PLOT_PASS_END_LOGIC_OP, f.passEndCauseCountsThisFrame[FrameManager.PASS_END_LOGIC_OP]);
|
||||
Tracy.plotInt(PLOT_PASS_END_READBACK, f.passEndCauseCountsThisFrame[FrameManager.PASS_END_READBACK]);
|
||||
}
|
||||
}
|
||||
+145
-154
@@ -1,29 +1,19 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.pipeline;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.GlslTransformUtils;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager.FrameState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.FBOClearTracker;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.FboState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PixelOps;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.util.MemoryAccess;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.sdl.SDLError;
|
||||
import org.lwjgl.sdl.SDL_GPUColorTargetDescription;
|
||||
import org.lwjgl.sdl.SDL_GPUDepthStencilState;
|
||||
import org.lwjgl.sdl.SDL_GPUGraphicsPipelineCreateInfo;
|
||||
import org.lwjgl.sdl.SDL_GPUGraphicsPipelineTargetInfo;
|
||||
import org.lwjgl.sdl.SDL_GPUMultisampleState;
|
||||
import org.lwjgl.sdl.SDL_GPURasterizerState;
|
||||
import org.lwjgl.sdl.SDL_GPUVertexInputState;
|
||||
import org.lwjgl.sdl.SDL_GPUViewport;
|
||||
import org.lwjgl.sdl.SDL_Rect;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.taumc.glsl.Transformer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.LongSupplier;
|
||||
|
||||
@@ -32,21 +22,34 @@ import static org.lwjgl.system.MemoryStack.stackPush;
|
||||
|
||||
public final class AttachmentClear {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger("Angelica-SDLGPU");
|
||||
|
||||
private static final String VERTEX_SOURCE = "angelica:sdlgpu/clear_attachment.vsh";
|
||||
private static final String FRAGMENT_SOURCE = "angelica:sdlgpu/clear_attachment.fsh";
|
||||
private static final long KEY_SEED = 0x5B1D2A7C4E9F3061L;
|
||||
private static final int CACHE_SLOTS = 4;
|
||||
private static final int MAX_COLOR_TARGETS = ContextState.MAX_COLOR_ATTACHMENTS;
|
||||
|
||||
public static final int EMPTY = 0;
|
||||
public static final int PARTIAL = 1;
|
||||
public static final int FULL = 2;
|
||||
|
||||
public static final class Target {
|
||||
public int[] colorFormats;
|
||||
public int[] drawBuffers;
|
||||
public int depthFormat;
|
||||
public long depthTexture;
|
||||
public int width;
|
||||
public int height;
|
||||
public boolean flipY;
|
||||
}
|
||||
|
||||
private final FrameManager frameManager;
|
||||
private final PipelineStore pipelineStore;
|
||||
private final ShaderManager shaderManager;
|
||||
|
||||
private int program;
|
||||
private long sdlVertexShader;
|
||||
private long sdlFragmentShader;
|
||||
private boolean failed;
|
||||
private final Target fboTarget = new Target();
|
||||
private final Target fbo0Target = new Target();
|
||||
|
||||
private final FullscreenPass fullscreen;
|
||||
private final long[] sdlFragmentShaders = new long[MAX_COLOR_TARGETS + 1];
|
||||
private final boolean[] fragmentFailed = new boolean[MAX_COLOR_TARGETS + 1];
|
||||
|
||||
private final long[] cacheKeys = new long[CACHE_SLOTS];
|
||||
private final long[] cachePipelines = new long[CACHE_SLOTS];
|
||||
@@ -55,118 +58,143 @@ public final class AttachmentClear {
|
||||
private final int[] scissorRect = new int[4];
|
||||
|
||||
private final Builder builder = new Builder();
|
||||
private int[] buildColorFormats;
|
||||
private int buildColorCount;
|
||||
private int buildDepthFormat;
|
||||
private Target buildTarget;
|
||||
private int buildColorTargets;
|
||||
private boolean buildDepthFlag;
|
||||
private boolean buildStencilFlag;
|
||||
private int buildWriteMask;
|
||||
private int buildStencilMask;
|
||||
private int buildColorMask;
|
||||
|
||||
public AttachmentClear(FrameManager frameManager, PipelineStore pipelineStore, ShaderManager shaderManager) {
|
||||
this.frameManager = frameManager;
|
||||
this.pipelineStore = pipelineStore;
|
||||
this.shaderManager = shaderManager;
|
||||
this.fullscreen = new FullscreenPass(shaderManager);
|
||||
}
|
||||
|
||||
private final class Builder implements LongSupplier {
|
||||
@Override public long getAsLong() { return createPipeline(); }
|
||||
}
|
||||
|
||||
public boolean eligible(ContextState st, FrameState f, FboState fbo, boolean wantDepth, boolean wantStencil) {
|
||||
if (SystemProperties.SDL_DISABLE_IN_PASS_CLEAR) return false;
|
||||
if (st.boundFboId == 0) return false;
|
||||
if (f.renderPass == 0) return false;
|
||||
if (fbo.depthTexture == 0) return false;
|
||||
if (f.currentDepthTarget != fbo.depthTexture) return false;
|
||||
if (f.activeLayoutHash != fbo.structuralLayoutHash || f.currentColorTarget != fbo.primaryTarget) return false;
|
||||
if (FBOClearTracker.fboHasPendingClear(st, fbo)) return false;
|
||||
if (wantStencil && !PixelOps.isDepthStencilFormat(fbo.depthFormat)) return false;
|
||||
if (fbo.width <= 0 || fbo.height <= 0) return false;
|
||||
return computeScissor(st, fbo);
|
||||
public Target forFbo(FboState fbo) {
|
||||
final Target t = fboTarget;
|
||||
t.colorFormats = fbo.cachedColorFormats;
|
||||
t.drawBuffers = fbo.drawBuffers;
|
||||
t.depthFormat = fbo.depthTexture != 0 ? fbo.depthFormat : 0;
|
||||
t.depthTexture = fbo.depthTexture;
|
||||
t.width = fbo.width;
|
||||
t.height = fbo.height;
|
||||
t.flipY = false;
|
||||
return t;
|
||||
}
|
||||
|
||||
private boolean computeScissor(ContextState st, FboState fbo) {
|
||||
public Target forFbo0(int[] swapchainFormats, int w, int h) {
|
||||
final Target t = fbo0Target;
|
||||
t.colorFormats = swapchainFormats;
|
||||
t.drawBuffers = null;
|
||||
t.depthFormat = 0;
|
||||
t.depthTexture = 0;
|
||||
t.width = w;
|
||||
t.height = h;
|
||||
t.flipY = true;
|
||||
return t;
|
||||
}
|
||||
|
||||
static boolean enabled(Target t, int i) {
|
||||
return t.drawBuffers == null || t.drawBuffers[i] >= 0;
|
||||
}
|
||||
|
||||
public boolean hasIntegerColorTarget(Target t) {
|
||||
final int[] formats = t.colorFormats;
|
||||
if (formats == null) return false;
|
||||
for (int i = 0; i < formats.length; i++) {
|
||||
if (enabled(t, i) && PixelOps.isSdlFormatInteger(formats[i])) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int coverage(ContextState st, Target t) {
|
||||
final int sx, sy, sw, sh;
|
||||
if (st.scissorEnabled) {
|
||||
sx = st.scissorX;
|
||||
sy = st.scissorY;
|
||||
sy = t.flipY ? t.height - st.scissorY - st.scissorH : st.scissorY;
|
||||
sw = st.scissorW;
|
||||
sh = st.scissorH;
|
||||
} else {
|
||||
sx = 0;
|
||||
sy = 0;
|
||||
sw = fbo.width;
|
||||
sh = fbo.height;
|
||||
sw = t.width;
|
||||
sh = t.height;
|
||||
}
|
||||
return ScissorClamp.clamp(sx, sy, sw, sh, fbo.width, fbo.height, scissorRect);
|
||||
if (!ScissorClamp.clamp(sx, sy, sw, sh, t.width, t.height, scissorRect)) return EMPTY;
|
||||
if (scissorRect[ScissorClamp.X] == 0 && scissorRect[ScissorClamp.Y] == 0
|
||||
&& scissorRect[ScissorClamp.W] == t.width && scissorRect[ScissorClamp.H] == t.height) return FULL;
|
||||
return PARTIAL;
|
||||
}
|
||||
|
||||
public boolean clearInPass(ContextState st, FrameState f, FboState fbo, boolean depth, boolean stencil) {
|
||||
final int writeMask = stencil ? (st.pipeline.stencilWriteMask & 0xFF) : 0;
|
||||
final long pipeline = getOrCreatePipeline(fbo, depth, stencil, writeMask);
|
||||
public boolean clearInPass(ContextState st, FrameState f, Target t, boolean color, boolean depth, boolean stencil) {
|
||||
final int colorTargets = color ? t.colorFormats.length : 0;
|
||||
final int stencilMask = stencil ? (st.pipeline.stencilWriteMask & 0xFF) : 0;
|
||||
final int colorMask = color ? st.pipeline.colorWriteMask : 0;
|
||||
final long pipeline = getOrCreatePipeline(t, colorTargets, depth, stencil, stencilMask, colorMask);
|
||||
if (pipeline == 0) return false;
|
||||
if (!computeScissor(st, fbo)) return false;
|
||||
if (coverage(st, t) == EMPTY) return false;
|
||||
|
||||
final long rp = f.renderPass;
|
||||
SDL_BindGPUGraphicsPipeline(rp, pipeline);
|
||||
|
||||
final float clearDepth = Math.min(1.0f, Math.max(0.0f, st.depthClearValue));
|
||||
final long vpAddr = st.cachedViewport.address();
|
||||
MemoryAccess.putFloat(vpAddr + SDL_GPUViewport.X, 0f);
|
||||
MemoryAccess.putFloat(vpAddr + SDL_GPUViewport.Y, 0f);
|
||||
MemoryAccess.putFloat(vpAddr + SDL_GPUViewport.W, fbo.width);
|
||||
MemoryAccess.putFloat(vpAddr + SDL_GPUViewport.H, fbo.height);
|
||||
MemoryAccess.putFloat(vpAddr + SDL_GPUViewport.MIN_DEPTH, clearDepth);
|
||||
MemoryAccess.putFloat(vpAddr + SDL_GPUViewport.MAX_DEPTH, clearDepth);
|
||||
SDL_SetGPUViewport(rp, st.cachedViewport);
|
||||
|
||||
final long scAddr = st.cachedScissor.address();
|
||||
MemoryAccess.putInt(scAddr + SDL_Rect.X, scissorRect[ScissorClamp.X]);
|
||||
MemoryAccess.putInt(scAddr + SDL_Rect.Y, scissorRect[ScissorClamp.Y]);
|
||||
MemoryAccess.putInt(scAddr + SDL_Rect.W, scissorRect[ScissorClamp.W]);
|
||||
MemoryAccess.putInt(scAddr + SDL_Rect.H, scissorRect[ScissorClamp.H]);
|
||||
SDL_SetGPUScissor(rp, st.cachedScissor);
|
||||
FullscreenPass.setViewport(rp, st, t.width, t.height, clearDepth);
|
||||
FullscreenPass.setScissor(rp, st, scissorRect[ScissorClamp.X], scissorRect[ScissorClamp.Y], scissorRect[ScissorClamp.W], scissorRect[ScissorClamp.H]);
|
||||
|
||||
if (stencil) {
|
||||
SDL_SetGPUStencilReference(rp, (byte) st.stencilClearValue);
|
||||
}
|
||||
|
||||
if (colorTargets > 0) {
|
||||
try (MemoryStack stack = stackPush()) {
|
||||
final ByteBuffer clearColor = stack.malloc(16);
|
||||
clearColor.putFloat(0, st.clearR);
|
||||
clearColor.putFloat(4, st.clearG);
|
||||
clearColor.putFloat(8, st.clearB);
|
||||
clearColor.putFloat(12, st.clearA);
|
||||
SDL_PushGPUFragmentUniformData(frameManager.getCommandBuffer(f), 0, clearColor);
|
||||
}
|
||||
st.lastPushedProgramFs = -1;
|
||||
}
|
||||
|
||||
SDL_DrawGPUPrimitives(rp, 3, 1, 0, 0);
|
||||
|
||||
st.lastBoundPipeline = 0;
|
||||
st.viewportDirty = true;
|
||||
st.scissorDirty = true;
|
||||
st.lastAppliedStencilRef = Integer.MIN_VALUE;
|
||||
st.lastAppliedVboBindCb = 0;
|
||||
|
||||
if (depth) st.clearedTexturesThisFrame.add(fbo.depthTexture);
|
||||
if (stencil) st.clearedStencilTexturesThisFrame.add(fbo.depthTexture);
|
||||
FullscreenPass.markStateClobbered(st);
|
||||
|
||||
frameManager.noteInPassClear();
|
||||
return true;
|
||||
}
|
||||
|
||||
long getOrCreatePipeline(FboState fbo, boolean depthFlag, boolean stencilFlag, int writeMask) {
|
||||
if (!ensureShaders()) return 0;
|
||||
long getOrCreatePipeline(Target t, int colorTargets, boolean depth, boolean stencil, int stencilMask, int colorMask) {
|
||||
if (!ensureShaders(colorTargets)) return 0;
|
||||
|
||||
final int[] formats = fbo.cachedColorFormats;
|
||||
final int[] formats = t.colorFormats;
|
||||
final int count = formats != null ? formats.length : 0;
|
||||
long key = KEY_SEED;
|
||||
key = Hashing.fmix64(key, count);
|
||||
for (int i = 0; i < count; i++) key = Hashing.fmix64(key, formats[i]);
|
||||
key = Hashing.fmix64(key, fbo.depthFormat);
|
||||
key = Hashing.fmix64(key, (depthFlag ? 1 : 0) | ((stencilFlag ? 1 : 0) << 1) | (writeMask << 8));
|
||||
for (int i = 0; i < count; i++) {
|
||||
key = Hashing.fmix64(key, formats[i]);
|
||||
key = Hashing.fmix64(key, enabled(t, i) ? 1 : 0);
|
||||
}
|
||||
key = Hashing.fmix64(key, t.depthFormat);
|
||||
key = Hashing.fmix64(key, colorTargets);
|
||||
key = Hashing.fmix64(key, (depth ? 1 : 0) | ((stencil ? 1 : 0) << 1) | (stencilMask << 8) | (colorMask << 16));
|
||||
|
||||
for (int i = 0; i < CACHE_SLOTS; i++) {
|
||||
if (cacheKeys[i] == key && cachePipelines[i] != 0) return cachePipelines[i];
|
||||
}
|
||||
|
||||
buildColorFormats = formats;
|
||||
buildColorCount = count;
|
||||
buildDepthFormat = fbo.depthFormat;
|
||||
buildDepthFlag = depthFlag;
|
||||
buildStencilFlag = stencilFlag;
|
||||
buildWriteMask = writeMask;
|
||||
buildTarget = t;
|
||||
buildColorTargets = colorTargets;
|
||||
buildDepthFlag = depth;
|
||||
buildStencilFlag = stencil;
|
||||
buildStencilMask = stencilMask;
|
||||
buildColorMask = colorMask;
|
||||
final long pipeline = pipelineStore.getOrBuild(key, builder);
|
||||
if (pipeline != 0) {
|
||||
cacheKeys[cacheNext] = key;
|
||||
@@ -178,14 +206,16 @@ public final class AttachmentClear {
|
||||
|
||||
private long createPipeline() {
|
||||
try (var stack = stackPush()) {
|
||||
final int numColorTargets = buildColorCount;
|
||||
final Target t = buildTarget;
|
||||
final int numColorTargets = t.colorFormats != null ? t.colorFormats.length : 0;
|
||||
final SDL_GPUColorTargetDescription.Buffer colorDesc = SDL_GPUColorTargetDescription.calloc(numColorTargets, stack);
|
||||
for (int i = 0; i < numColorTargets; i++) {
|
||||
final var desc = colorDesc.get(i);
|
||||
desc.format(buildColorFormats[i]);
|
||||
desc.format(t.colorFormats[i]);
|
||||
final int mask = (buildColorTargets > 0 && enabled(t, i)) ? buildColorMask : 0;
|
||||
desc.blend_state()
|
||||
.enable_blend(false)
|
||||
.color_write_mask((byte) 0)
|
||||
.color_write_mask((byte) mask)
|
||||
.enable_color_write_mask(true);
|
||||
}
|
||||
|
||||
@@ -195,7 +225,7 @@ public final class AttachmentClear {
|
||||
.compare_op(SDL_GPU_COMPAREOP_ALWAYS)
|
||||
.enable_stencil_test(buildStencilFlag)
|
||||
.compare_mask((byte) 0xFF)
|
||||
.write_mask((byte) buildWriteMask);
|
||||
.write_mask((byte) buildStencilMask);
|
||||
if (buildStencilFlag) {
|
||||
depthStencil.front_stencil_state()
|
||||
.fail_op(SDL_GPU_STENCILOP_REPLACE)
|
||||
@@ -209,83 +239,44 @@ public final class AttachmentClear {
|
||||
.compare_op(SDL_GPU_COMPAREOP_ALWAYS);
|
||||
}
|
||||
|
||||
final SDL_GPURasterizerState rasterizer = SDL_GPURasterizerState.calloc(stack)
|
||||
.cull_mode(SDL_GPU_CULLMODE_NONE)
|
||||
.front_face(SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE)
|
||||
.fill_mode(SDL_GPU_FILLMODE_FILL);
|
||||
|
||||
final SDL_GPUMultisampleState multisample = SDL_GPUMultisampleState.calloc(stack)
|
||||
.sample_count(SDL_GPU_SAMPLECOUNT_1);
|
||||
|
||||
final SDL_GPUGraphicsPipelineTargetInfo targetInfo = SDL_GPUGraphicsPipelineTargetInfo.calloc(stack)
|
||||
.num_color_targets(numColorTargets)
|
||||
.color_target_descriptions(colorDesc)
|
||||
.depth_stencil_format(buildDepthFormat)
|
||||
.has_depth_stencil_target(true);
|
||||
|
||||
final SDL_GPUVertexInputState vertexInput = SDL_GPUVertexInputState.calloc(stack);
|
||||
|
||||
final SDL_GPUGraphicsPipelineCreateInfo ci = SDL_GPUGraphicsPipelineCreateInfo.calloc(stack)
|
||||
.vertex_shader(sdlVertexShader)
|
||||
.fragment_shader(sdlFragmentShader)
|
||||
.primitive_type(SDL_GPU_PRIMITIVETYPE_TRIANGLELIST)
|
||||
.rasterizer_state(rasterizer)
|
||||
.multisample_state(multisample)
|
||||
.depth_stencil_state(depthStencil)
|
||||
.target_info(targetInfo)
|
||||
.vertex_input_state(vertexInput);
|
||||
|
||||
final long pipeline = SDL_CreateGPUGraphicsPipeline(pipelineStore.device().getDevice(), ci);
|
||||
if (pipeline == 0) {
|
||||
LOG.error("AttachmentClear: pipeline creation failed: {}", SDLError.SDL_GetError());
|
||||
return PipelineStore.BAD_PIPELINE_SENTINEL;
|
||||
}
|
||||
return pipeline;
|
||||
return fullscreen.createPipeline(pipelineStore, stack, sdlFragmentShaders[buildColorTargets], colorDesc, numColorTargets, depthStencil, t.depthFormat, "AttachmentClear");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean ensureShaders() {
|
||||
if (program != 0) return true;
|
||||
if (failed) return false;
|
||||
failed = true;
|
||||
private boolean ensureShaders(int colorTargets) {
|
||||
if (colorTargets < 0 || colorTargets > MAX_COLOR_TARGETS) return false;
|
||||
if (sdlFragmentShaders[colorTargets] != 0) return true;
|
||||
if (fragmentFailed[colorTargets]) return false;
|
||||
fragmentFailed[colorTargets] = true;
|
||||
|
||||
final int vs = shaderManager.createShader(GL20.GL_VERTEX_SHADER);
|
||||
shaderManager.shaderSource(vs, ShaderLoader.getShaderSource(VERTEX_SOURCE));
|
||||
shaderManager.compileShader(vs);
|
||||
final int fs = shaderManager.createShader(GL20.GL_FRAGMENT_SHADER);
|
||||
shaderManager.shaderSource(fs, ShaderLoader.getShaderSource(FRAGMENT_SOURCE));
|
||||
shaderManager.compileShader(fs);
|
||||
final String baseSource = ShaderLoader.getShaderSource(FRAGMENT_SOURCE);
|
||||
final String fragmentSource = colorTargets == 0 ? baseSource : withColorOutputs(baseSource, colorTargets);
|
||||
final long fragmentShader = fullscreen.compileFragment(fragmentSource, colorTargets > 0 ? 16 : -1, "AttachmentClear: clear_attachment COLOR_TARGETS=" + colorTargets);
|
||||
if (fragmentShader == 0) return false;
|
||||
|
||||
final int prog = shaderManager.createProgram();
|
||||
shaderManager.attachShader(prog, vs);
|
||||
shaderManager.attachShader(prog, fs);
|
||||
shaderManager.linkProgram(prog);
|
||||
|
||||
final ShaderManager.ProgramObject po = shaderManager.getProgram(prog);
|
||||
if (po == null || !po.linked) {
|
||||
LOG.error("AttachmentClear: clear_attachment program failed to link: {}", po == null ? "no program object" : po.infoLog);
|
||||
return false;
|
||||
}
|
||||
|
||||
po.sdlVertexShader = shaderManager.createSDLShader(po.vertexSpirv, SDL_GPU_SHADERSTAGE_VERTEX, po.vertexResources.numSamplers(), po.vertexResources.numUBOs(), po.vertexResources.numStorageBuffers(), po.vertexResources.numStorageTextures());
|
||||
po.sdlFragmentShader = shaderManager.createSDLShader(po.fragmentSpirv, SDL_GPU_SHADERSTAGE_FRAGMENT, po.fragmentResources.numSamplers(), po.fragmentResources.numUBOs(), po.fragmentResources.numStorageBuffers(), po.fragmentResources.numStorageTextures());
|
||||
if (po.sdlVertexShader == 0 || po.sdlFragmentShader == 0) {
|
||||
LOG.error("AttachmentClear: clear_attachment SDL shader creation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
program = prog;
|
||||
sdlVertexShader = po.sdlVertexShader;
|
||||
sdlFragmentShader = po.sdlFragmentShader;
|
||||
failed = false;
|
||||
sdlFragmentShaders[colorTargets] = fragmentShader;
|
||||
fragmentFailed[colorTargets] = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String withColorOutputs(String source, int count) {
|
||||
final GlslTransformUtils.QuietParse parsed = GlslTransformUtils.parseBothQuiet(source);
|
||||
final Transformer transformer = new Transformer(parsed.full());
|
||||
transformer.injectVariable("uniform vec4 clearColor;");
|
||||
for (int i = 0; i < count; i++) {
|
||||
transformer.injectVariable("layout(location = " + i + ") out vec4 angelica_ClearColor" + i + ";");
|
||||
transformer.appendMain("angelica_ClearColor" + i + " = clearColor;");
|
||||
}
|
||||
final String header = GlslTransformUtils.getFormattedShader(parsed.pre(), "").trim();
|
||||
final StringBuilder out = new StringBuilder();
|
||||
transformer.mutateTree(tree -> out.append(GlslTransformUtils.getFormattedShader(tree, header)));
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
program = 0;
|
||||
sdlVertexShader = 0;
|
||||
sdlFragmentShader = 0;
|
||||
failed = false;
|
||||
fullscreen.shutdown();
|
||||
Arrays.fill(sdlFragmentShaders, 0L);
|
||||
Arrays.fill(fragmentFailed, false);
|
||||
Arrays.fill(cacheKeys, 0L);
|
||||
Arrays.fill(cachePipelines, 0L);
|
||||
cacheNext = 0;
|
||||
|
||||
+314
@@ -0,0 +1,314 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.pipeline;
|
||||
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager.FrameState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.FBOClearTracker;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.util.MemoryAccess;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
|
||||
import org.lwjgl.sdl.SDLError;
|
||||
import org.lwjgl.sdl.SDL_FColor;
|
||||
import org.lwjgl.sdl.SDL_GPUColorTargetDescription;
|
||||
import org.lwjgl.sdl.SDL_GPUColorTargetInfo;
|
||||
import org.lwjgl.sdl.SDL_GPUDepthStencilState;
|
||||
import org.lwjgl.sdl.SDL_GPUDepthStencilTargetInfo;
|
||||
import org.lwjgl.sdl.SDL_GPUSamplerCreateInfo;
|
||||
import org.lwjgl.sdl.SDL_GPUTextureSamplerBinding;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.LongSupplier;
|
||||
|
||||
import static org.lwjgl.sdl.SDLGPU.*;
|
||||
import static org.lwjgl.system.MemoryStack.stackPush;
|
||||
|
||||
public final class DepthStencilReadback {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger("Angelica-SDLGPU");
|
||||
|
||||
private static final String DEPTH_FRAGMENT_SOURCE = "angelica:sdlgpu/readback_depth.fsh";
|
||||
private static final String STENCIL_FRAGMENT_SOURCE = "angelica:sdlgpu/readback_stencil.fsh";
|
||||
private static final long KEY_SEED = 0x3C6EF372A54FF53AL;
|
||||
private static final int STENCIL_BITS = 8;
|
||||
private static final int DEPTH_TARGET_FORMAT = SDL_GPU_TEXTUREFORMAT_R32_FLOAT;
|
||||
private static final int STENCIL_TARGET_FORMAT = SDL_GPU_TEXTUREFORMAT_R8_UNORM;
|
||||
|
||||
private final FrameManager frameManager;
|
||||
private final ResourceManager resourceManager;
|
||||
private final PipelineStore pipelineStore;
|
||||
private final FBOClearTracker fboClearTracker;
|
||||
private final FullscreenPass fullscreen;
|
||||
|
||||
private long depthFragmentShader;
|
||||
private boolean depthFragmentFailed;
|
||||
private long stencilFragmentShader;
|
||||
private boolean stencilFragmentFailed;
|
||||
private long nearestSampler;
|
||||
|
||||
private long depthPipeline;
|
||||
private final long[] stencilPipelines = new long[STENCIL_BITS];
|
||||
private int stencilPipelinesFormat;
|
||||
|
||||
private final Builder builder = new Builder();
|
||||
private int buildBit;
|
||||
private int buildDepthFormat;
|
||||
|
||||
public DepthStencilReadback(FrameManager frameManager, ResourceManager resourceManager, PipelineStore pipelineStore, ShaderManager shaderManager, FBOClearTracker fboClearTracker) {
|
||||
this.frameManager = frameManager;
|
||||
this.resourceManager = resourceManager;
|
||||
this.pipelineStore = pipelineStore;
|
||||
this.fboClearTracker = fboClearTracker;
|
||||
this.fullscreen = new FullscreenPass(shaderManager);
|
||||
}
|
||||
|
||||
private final class Builder implements LongSupplier {
|
||||
@Override public long getAsLong() { return buildBit < 0 ? createDepthPipeline() : createStencilPipeline(buildBit, buildDepthFormat); }
|
||||
}
|
||||
|
||||
public long renderDepth(ContextState st, long depthTexture, int depthSdlFormat, int width, int height) {
|
||||
final FrameState f = frameManager.frame();
|
||||
if (!canRender(f, depthTexture, width, height)) return 0L;
|
||||
if (depthTexture == resourceManager.getSwapchainDepthStencil() && !resourceManager.isSwapchainDepthStencilSampleable()) return 0L;
|
||||
final long pipeline = getDepthPipeline();
|
||||
final long sampler = getNearestSampler();
|
||||
if (pipeline == 0L || sampler == 0L) return 0L;
|
||||
applyPendingClears(st, f, depthTexture);
|
||||
final long scratch = ensureDepthScratch(st, width, height);
|
||||
|
||||
frameManager.endRenderPassIfActive(f, FrameManager.PASS_END_READBACK);
|
||||
try (MemoryStack stack = stackPush()) {
|
||||
final SDL_GPUColorTargetInfo.Buffer color = colorTarget(stack, scratch, SDL_GPU_LOADOP_DONT_CARE);
|
||||
final long rp = frameManager.beginRenderPass(color, null);
|
||||
SDL_BindGPUGraphicsPipeline(rp, pipeline);
|
||||
FullscreenPass.setViewport(rp, st, width, height, 0f);
|
||||
FullscreenPass.setScissor(rp, st, 0, 0, width, height);
|
||||
final SDL_GPUTextureSamplerBinding.Buffer binding = SDL_GPUTextureSamplerBinding.calloc(1, stack);
|
||||
binding.get(0).texture(depthTexture).sampler(sampler);
|
||||
SDL_BindGPUFragmentSamplers(rp, 0, binding);
|
||||
SDL_DrawGPUPrimitives(rp, 3, 1, 0, 0);
|
||||
frameManager.endRenderPassIfActive(f, FrameManager.PASS_END_READBACK);
|
||||
}
|
||||
FullscreenPass.markStateClobbered(st);
|
||||
return scratch;
|
||||
}
|
||||
|
||||
public long renderStencil(ContextState st, long depthStencilTexture, int depthSdlFormat, int width, int height) {
|
||||
final FrameState f = frameManager.frame();
|
||||
if (!canRender(f, depthStencilTexture, width, height)) return 0L;
|
||||
if (!ensureStencilPipelines(depthSdlFormat)) return 0L;
|
||||
applyPendingClears(st, f, depthStencilTexture);
|
||||
final long scratch = ensureStencilScratch(st, width, height);
|
||||
|
||||
frameManager.endRenderPassIfActive(f, FrameManager.PASS_END_READBACK);
|
||||
try (MemoryStack stack = stackPush()) {
|
||||
final SDL_GPUColorTargetInfo.Buffer color = colorTarget(stack, scratch, SDL_GPU_LOADOP_CLEAR);
|
||||
final SDL_GPUDepthStencilTargetInfo ds = SDL_GPUDepthStencilTargetInfo.calloc(stack);
|
||||
final long dsAddr = ds.address();
|
||||
MemoryAccess.putAddress(dsAddr + SDL_GPUDepthStencilTargetInfo.TEXTURE, depthStencilTexture);
|
||||
MemoryAccess.putInt(dsAddr + SDL_GPUDepthStencilTargetInfo.LOAD_OP, SDL_GPU_LOADOP_LOAD);
|
||||
MemoryAccess.putInt(dsAddr + SDL_GPUDepthStencilTargetInfo.STORE_OP, SDL_GPU_STOREOP_STORE);
|
||||
MemoryAccess.putInt(dsAddr + SDL_GPUDepthStencilTargetInfo.STENCIL_LOAD_OP, SDL_GPU_LOADOP_LOAD);
|
||||
MemoryAccess.putInt(dsAddr + SDL_GPUDepthStencilTargetInfo.STENCIL_STORE_OP, SDL_GPU_STOREOP_STORE);
|
||||
|
||||
final long rp = frameManager.beginRenderPass(color, ds);
|
||||
final long cb = frameManager.getCommandBuffer(f);
|
||||
FullscreenPass.setViewport(rp, st, width, height, 0f);
|
||||
FullscreenPass.setScissor(rp, st, 0, 0, width, height);
|
||||
final ByteBuffer bitValue = stack.calloc(16);
|
||||
for (int b = 0; b < STENCIL_BITS; b++) {
|
||||
SDL_BindGPUGraphicsPipeline(rp, stencilPipelines[b]);
|
||||
SDL_SetGPUStencilReference(rp, (byte) (1 << b));
|
||||
bitValue.putFloat(0, (1 << b) / 255.0f);
|
||||
SDL_PushGPUFragmentUniformData(cb, 0, bitValue);
|
||||
SDL_DrawGPUPrimitives(rp, 3, 1, 0, 0);
|
||||
}
|
||||
frameManager.endRenderPassIfActive(f, FrameManager.PASS_END_READBACK);
|
||||
}
|
||||
FullscreenPass.markStateClobbered(st);
|
||||
st.lastPushedProgramFs = -1;
|
||||
return scratch;
|
||||
}
|
||||
|
||||
private static boolean canRender(FrameState f, long texture, int width, int height) {
|
||||
return f.frameActive && f.commandBuffer != 0L && texture != 0L && width > 0 && height > 0;
|
||||
}
|
||||
|
||||
private void applyPendingClears(ContextState st, FrameState f, long texture) {
|
||||
fboClearTracker.materializePendingClearForRead(st, f, texture);
|
||||
}
|
||||
|
||||
private static SDL_GPUColorTargetInfo.Buffer colorTarget(MemoryStack stack, long texture, int loadOp) {
|
||||
final SDL_GPUColorTargetInfo.Buffer color = SDL_GPUColorTargetInfo.calloc(1, stack);
|
||||
final long addr = color.get(0).address();
|
||||
MemoryAccess.putAddress(addr + SDL_GPUColorTargetInfo.TEXTURE, texture);
|
||||
MemoryAccess.putInt(addr + SDL_GPUColorTargetInfo.LOAD_OP, loadOp);
|
||||
MemoryAccess.putInt(addr + SDL_GPUColorTargetInfo.STORE_OP, SDL_GPU_STOREOP_STORE);
|
||||
final long ccAddr = addr + SDL_GPUColorTargetInfo.CLEAR_COLOR;
|
||||
MemoryAccess.putFloat(ccAddr + SDL_FColor.R, 0f);
|
||||
MemoryAccess.putFloat(ccAddr + SDL_FColor.G, 0f);
|
||||
MemoryAccess.putFloat(ccAddr + SDL_FColor.B, 0f);
|
||||
MemoryAccess.putFloat(ccAddr + SDL_FColor.A, 0f);
|
||||
return color;
|
||||
}
|
||||
|
||||
private long ensureDepthScratch(ContextState st, int width, int height) {
|
||||
if (st.readbackDepthScratch == 0L || st.readbackDepthScratchWidth != width || st.readbackDepthScratchHeight != height) {
|
||||
if (st.readbackDepthScratch != 0L) resourceManager.releaseTextureDeferred(st.readbackDepthScratch);
|
||||
st.readbackDepthScratch = resourceManager.createScratchTexture(DEPTH_TARGET_FORMAT, width, height, SDL_GPU_TEXTUREUSAGE_COLOR_TARGET);
|
||||
st.readbackDepthScratchWidth = width;
|
||||
st.readbackDepthScratchHeight = height;
|
||||
}
|
||||
return st.readbackDepthScratch;
|
||||
}
|
||||
|
||||
private long ensureStencilScratch(ContextState st, int width, int height) {
|
||||
if (st.readbackStencilScratch == 0L || st.readbackStencilScratchWidth != width || st.readbackStencilScratchHeight != height) {
|
||||
if (st.readbackStencilScratch != 0L) resourceManager.releaseTextureDeferred(st.readbackStencilScratch);
|
||||
st.readbackStencilScratch = resourceManager.createScratchTexture(STENCIL_TARGET_FORMAT, width, height, SDL_GPU_TEXTUREUSAGE_COLOR_TARGET);
|
||||
st.readbackStencilScratchWidth = width;
|
||||
st.readbackStencilScratchHeight = height;
|
||||
}
|
||||
return st.readbackStencilScratch;
|
||||
}
|
||||
|
||||
private long getDepthPipeline() {
|
||||
if (depthPipeline != 0L) return depthPipeline;
|
||||
if (!ensureDepthShader()) return 0L;
|
||||
buildBit = -1;
|
||||
buildDepthFormat = 0;
|
||||
depthPipeline = pipelineStore.getOrBuild(Hashing.fmix64(KEY_SEED, -1), builder);
|
||||
return depthPipeline;
|
||||
}
|
||||
|
||||
private boolean ensureStencilPipelines(int depthFormat) {
|
||||
if (depthFormat == 0) return false;
|
||||
if (stencilPipelinesFormat == depthFormat) return true;
|
||||
if (!ensureStencilShader()) return false;
|
||||
buildDepthFormat = depthFormat;
|
||||
final long formatKey = Hashing.fmix64(KEY_SEED, depthFormat);
|
||||
for (int b = 0; b < STENCIL_BITS; b++) {
|
||||
buildBit = b;
|
||||
final long pipeline = pipelineStore.getOrBuild(Hashing.fmix64(formatKey, b), builder);
|
||||
if (pipeline == 0L) {
|
||||
stencilPipelinesFormat = 0;
|
||||
return false;
|
||||
}
|
||||
stencilPipelines[b] = pipeline;
|
||||
}
|
||||
stencilPipelinesFormat = depthFormat;
|
||||
return true;
|
||||
}
|
||||
|
||||
private long createDepthPipeline() {
|
||||
try (MemoryStack stack = stackPush()) {
|
||||
final SDL_GPUColorTargetDescription.Buffer colorDesc = SDL_GPUColorTargetDescription.calloc(1, stack);
|
||||
colorDesc.get(0).format(DEPTH_TARGET_FORMAT).blend_state().enable_blend(false);
|
||||
final SDL_GPUDepthStencilState depthStencil = SDL_GPUDepthStencilState.calloc(stack)
|
||||
.compare_op(SDL_GPU_COMPAREOP_ALWAYS);
|
||||
return fullscreen.createPipeline(pipelineStore, stack, depthFragmentShader, colorDesc, 1, depthStencil, 0, "DepthStencilReadback depth");
|
||||
}
|
||||
}
|
||||
|
||||
private long createStencilPipeline(int bit, int depthFormat) {
|
||||
try (MemoryStack stack = stackPush()) {
|
||||
final SDL_GPUColorTargetDescription.Buffer colorDesc = SDL_GPUColorTargetDescription.calloc(1, stack);
|
||||
colorDesc.get(0).format(STENCIL_TARGET_FORMAT).blend_state()
|
||||
.enable_blend(true)
|
||||
.src_color_blendfactor(SDL_GPU_BLENDFACTOR_ONE)
|
||||
.dst_color_blendfactor(SDL_GPU_BLENDFACTOR_ONE)
|
||||
.color_blend_op(SDL_GPU_BLENDOP_ADD)
|
||||
.src_alpha_blendfactor(SDL_GPU_BLENDFACTOR_ONE)
|
||||
.dst_alpha_blendfactor(SDL_GPU_BLENDFACTOR_ONE)
|
||||
.alpha_blend_op(SDL_GPU_BLENDOP_ADD);
|
||||
final SDL_GPUDepthStencilState depthStencil = SDL_GPUDepthStencilState.calloc(stack)
|
||||
.enable_depth_test(false)
|
||||
.enable_depth_write(false)
|
||||
.compare_op(SDL_GPU_COMPAREOP_ALWAYS)
|
||||
.enable_stencil_test(true)
|
||||
.compare_mask((byte) (1 << bit))
|
||||
.write_mask((byte) 0);
|
||||
depthStencil.front_stencil_state()
|
||||
.fail_op(SDL_GPU_STENCILOP_KEEP)
|
||||
.pass_op(SDL_GPU_STENCILOP_KEEP)
|
||||
.depth_fail_op(SDL_GPU_STENCILOP_KEEP)
|
||||
.compare_op(SDL_GPU_COMPAREOP_EQUAL);
|
||||
depthStencil.back_stencil_state()
|
||||
.fail_op(SDL_GPU_STENCILOP_KEEP)
|
||||
.pass_op(SDL_GPU_STENCILOP_KEEP)
|
||||
.depth_fail_op(SDL_GPU_STENCILOP_KEEP)
|
||||
.compare_op(SDL_GPU_COMPAREOP_EQUAL);
|
||||
return fullscreen.createPipeline(pipelineStore, stack, stencilFragmentShader, colorDesc, 1, depthStencil, depthFormat, "DepthStencilReadback stencil");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean ensureDepthShader() {
|
||||
if (depthFragmentShader != 0L) return true;
|
||||
if (depthFragmentFailed) return false;
|
||||
depthFragmentFailed = true;
|
||||
depthFragmentShader = fullscreen.compileFragment(ShaderLoader.getShaderSource(DEPTH_FRAGMENT_SOURCE), -1, "DepthStencilReadback: readback_depth");
|
||||
if (depthFragmentShader == 0L) return false;
|
||||
depthFragmentFailed = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean ensureStencilShader() {
|
||||
if (stencilFragmentShader != 0L) return true;
|
||||
if (stencilFragmentFailed) return false;
|
||||
stencilFragmentFailed = true;
|
||||
stencilFragmentShader = fullscreen.compileFragment(ShaderLoader.getShaderSource(STENCIL_FRAGMENT_SOURCE), 16, "DepthStencilReadback: readback_stencil");
|
||||
if (stencilFragmentShader == 0L) return false;
|
||||
stencilFragmentFailed = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private long getNearestSampler() {
|
||||
if (nearestSampler != 0L) return nearestSampler;
|
||||
try (MemoryStack stack = stackPush()) {
|
||||
final SDL_GPUSamplerCreateInfo ci = SDL_GPUSamplerCreateInfo.calloc(stack)
|
||||
.min_filter(SDL_GPU_FILTER_NEAREST)
|
||||
.mag_filter(SDL_GPU_FILTER_NEAREST)
|
||||
.mipmap_mode(SDL_GPU_SAMPLERMIPMAPMODE_NEAREST)
|
||||
.address_mode_u(SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE)
|
||||
.address_mode_v(SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE)
|
||||
.address_mode_w(SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE);
|
||||
nearestSampler = SDL_CreateGPUSampler(pipelineStore.device().getDevice(), ci);
|
||||
if (nearestSampler == 0L) LOG.error("DepthStencilReadback: sampler creation failed: {}", SDLError.SDL_GetError());
|
||||
}
|
||||
return nearestSampler;
|
||||
}
|
||||
|
||||
public void release(ContextState st) {
|
||||
if (st.readbackDepthScratch != 0L) resourceManager.releaseTextureHandle(st.readbackDepthScratch);
|
||||
st.readbackDepthScratch = 0L;
|
||||
st.readbackDepthScratchWidth = 0;
|
||||
st.readbackDepthScratchHeight = 0;
|
||||
if (st.readbackStencilScratch != 0L) resourceManager.releaseTextureHandle(st.readbackStencilScratch);
|
||||
st.readbackStencilScratch = 0L;
|
||||
st.readbackStencilScratchWidth = 0;
|
||||
st.readbackStencilScratchHeight = 0;
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
final long dev = pipelineStore.device().getDevice();
|
||||
if (dev != 0L) {
|
||||
if (depthFragmentShader != 0L) SDL_ReleaseGPUShader(dev, depthFragmentShader);
|
||||
if (stencilFragmentShader != 0L) SDL_ReleaseGPUShader(dev, stencilFragmentShader);
|
||||
if (fullscreen.vertexShader() != 0L) SDL_ReleaseGPUShader(dev, fullscreen.vertexShader());
|
||||
if (nearestSampler != 0L) SDL_ReleaseGPUSampler(dev, nearestSampler);
|
||||
}
|
||||
fullscreen.shutdown();
|
||||
depthFragmentShader = 0L;
|
||||
depthFragmentFailed = false;
|
||||
stencilFragmentShader = 0L;
|
||||
stencilFragmentFailed = false;
|
||||
nearestSampler = 0L;
|
||||
depthPipeline = 0L;
|
||||
Arrays.fill(stencilPipelines, 0L);
|
||||
stencilPipelinesFormat = 0;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 397 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user