mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f274a64ea | ||
|
|
9814a96b10 | ||
|
|
af599f292e | ||
|
|
7bb5bfd45c | ||
|
|
74ea5c4709 | ||
|
|
233c74e0d7 | ||
|
|
e17b45c842 | ||
|
|
c9d46e70e0 |
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"))
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
) {}
|
||||
@@ -175,7 +174,9 @@ public class DisplayListManager {
|
||||
|
||||
static void flushAll() {
|
||||
drawBarrier();
|
||||
flushMatrix();
|
||||
for (int slot = 0; slot < DisplayListCallback.SLOT_COUNT; slot++) {
|
||||
flushMatrix(slot);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,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;
|
||||
}
|
||||
@@ -204,32 +213,48 @@ 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() {
|
||||
@@ -319,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);
|
||||
@@ -375,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();
|
||||
}
|
||||
|
||||
@@ -390,6 +443,7 @@ public class DisplayListManager {
|
||||
// Discard any transformations & flush any pending draw operations
|
||||
drawBarrier();
|
||||
resetRelativeTransform();
|
||||
syncStreamMatrixMode(transformCallback.getMatrixMode());
|
||||
currentRecorder.writePopMatrix();
|
||||
}
|
||||
|
||||
@@ -439,6 +493,8 @@ public class DisplayListManager {
|
||||
}
|
||||
|
||||
public static void recordActiveTexture(int texture) {
|
||||
drawBarrier();
|
||||
flushMatrix(DisplayListCallback.SLOT_TEXTURE);
|
||||
currentRecorder.writeActiveTexture(texture);
|
||||
}
|
||||
|
||||
@@ -447,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) {
|
||||
@@ -575,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) {
|
||||
@@ -598,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);
|
||||
@@ -606,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();
|
||||
|
||||
}
|
||||
@@ -649,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) {
|
||||
@@ -662,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) {
|
||||
@@ -680,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) {
|
||||
@@ -702,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) {
|
||||
@@ -720,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) {
|
||||
@@ -735,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) {
|
||||
@@ -753,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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -829,6 +913,9 @@ public class DisplayListManager {
|
||||
|
||||
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 {
|
||||
@@ -901,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
|
||||
@@ -1399,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;
|
||||
@@ -1416,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1467,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
|
||||
@@ -1488,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);
|
||||
}
|
||||
@@ -1515,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,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;
|
||||
@@ -240,6 +242,9 @@ public class GLContextState {
|
||||
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;
|
||||
|
||||
@@ -10,7 +10,6 @@ import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager.RecordMode;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.GLDebugMessageListener;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.Instancing;
|
||||
@@ -23,6 +22,7 @@ import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GlintColorHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CommandRecorder;
|
||||
@@ -118,6 +118,7 @@ import java.nio.ShortBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -146,6 +147,36 @@ import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACK
|
||||
@SuppressWarnings("unused") // Used in ASM
|
||||
public class GLStateManager {
|
||||
|
||||
/** Called before a mutation, while deferred draws can still use their original state. */
|
||||
public static void beforeUncapturedStateChange() {
|
||||
if (BatchStateGuard.isSuspended()) return;
|
||||
if (DisplayListManager.getRecordMode() == RecordMode.COMPILE) return;
|
||||
BatchStateGuard.beforeChange();
|
||||
ctx().batchStateGeneration++;
|
||||
}
|
||||
|
||||
public static void beforeUncapturedCapabilityChange(int cap) {
|
||||
if (cap == GL11.GL_FOG || cap == GL11.GL_SCISSOR_TEST || cap == GL11.GL_STENCIL_TEST
|
||||
|| cap == GL11.GL_COLOR_LOGIC_OP || cap == GL14.GL_COLOR_SUM || cap == GL30.GL_RASTERIZER_DISCARD
|
||||
|| cap == GL13.GL_SAMPLE_ALPHA_TO_COVERAGE || cap == GL13.GL_SAMPLE_ALPHA_TO_ONE || cap == GL13.GL_SAMPLE_COVERAGE
|
||||
|| (cap >= GL11.GL_CLIP_PLANE0 && cap < GL11.GL_CLIP_PLANE0 + MAX_CLIP_PLANES)) {
|
||||
beforeUncapturedStateChange();
|
||||
}
|
||||
}
|
||||
|
||||
public static void beforeUncapturedTextureUnitChange(int cap, int unit) {
|
||||
if (cap == GL11.GL_TEXTURE_2D && unit <= 1) return;
|
||||
beforeUncapturedStateChange();
|
||||
}
|
||||
|
||||
private static void beforeMatrixChange() {
|
||||
final GLContextState c = ctx();
|
||||
if (c.matrixMode.getMode() == GL11.GL_PROJECTION
|
||||
|| (c.matrixMode.getMode() == GL11.GL_TEXTURE && c.activeTextureUnit.getValue() != 0)) {
|
||||
beforeUncapturedStateChange();
|
||||
}
|
||||
}
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger("GLSM");
|
||||
|
||||
private static final Set<String> WARN_ONCE = ConcurrentHashMap.newKeySet();
|
||||
@@ -167,7 +198,7 @@ public class GLStateManager {
|
||||
return st != null ? st : primaryContext();
|
||||
}
|
||||
|
||||
private static GLContextState primaryContext() {
|
||||
public static GLContextState primaryContext() {
|
||||
GLContextState p = primaryContext;
|
||||
if (p != null) return p;
|
||||
synchronized (GLStateManager.class) {
|
||||
@@ -1387,6 +1418,7 @@ public class GLStateManager {
|
||||
final boolean caching = isCachingEnabled();
|
||||
final boolean bypass = !caching;
|
||||
if (bypass || red != glCtx.blendState.getBlendColorR() || green != glCtx.blendState.getBlendColorG() || blue != glCtx.blendState.getBlendColorB() || alpha != glCtx.blendState.getBlendColorA()) {
|
||||
beforeUncapturedStateChange();
|
||||
glCtx.blendState.setBlendColorR(red);
|
||||
glCtx.blendState.setBlendColorG(green);
|
||||
glCtx.blendState.setBlendColorB(blue);
|
||||
@@ -1624,6 +1656,7 @@ public class GLStateManager {
|
||||
final boolean caching = isCachingEnabled();
|
||||
final boolean bypass = !caching;
|
||||
if (bypass || glCtx.blendState.getEquationRgb() != mode || glCtx.blendState.getEquationAlpha() != mode) {
|
||||
beforeUncapturedStateChange();
|
||||
glCtx.blendState.setEquationRgb(mode);
|
||||
glCtx.blendState.setEquationAlpha(mode);
|
||||
RENDER_BACKEND.blendEquation(mode);
|
||||
@@ -1639,6 +1672,7 @@ public class GLStateManager {
|
||||
final boolean caching = isCachingEnabled();
|
||||
final boolean bypass = !caching;
|
||||
if (bypass || glCtx.blendState.getEquationRgb() != modeRGB || glCtx.blendState.getEquationAlpha() != modeAlpha) {
|
||||
beforeUncapturedStateChange();
|
||||
glCtx.blendState.setEquationRgb(modeRGB);
|
||||
glCtx.blendState.setEquationAlpha(modeAlpha);
|
||||
RENDER_BACKEND.blendEquationSeparate(modeRGB, modeAlpha);
|
||||
@@ -1936,6 +1970,7 @@ public class GLStateManager {
|
||||
private static void changeSecondaryColor(float red, float green, float blue) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled() || red != glCtx.secondaryColor.getRed() || green != glCtx.secondaryColor.getGreen() || blue != glCtx.secondaryColor.getBlue()) {
|
||||
if (glCtx.colorSumState.isEnabled()) beforeUncapturedStateChange();
|
||||
mod(glCtx.secondaryColor);
|
||||
glCtx.secondaryColor.setRed(red);
|
||||
glCtx.secondaryColor.setGreen(green);
|
||||
@@ -2205,6 +2240,7 @@ public class GLStateManager {
|
||||
final int cachedBinding = textureUnit.getBinding();
|
||||
|
||||
if (cachedBinding != texture || (texture == 0 && textureUnit.getTarget() != target)) {
|
||||
if (activeUnit != 0 || target != GL11.GL_TEXTURE_2D) beforeUncapturedStateChange();
|
||||
if (Tracy.ENABLED) texBindMisses++;
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
mod(textureUnit).setBinding(texture, target);
|
||||
@@ -2809,6 +2845,7 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!isCachingEnabled() || ctx().drawBuffer.getValue() != mode) beforeUncapturedStateChange();
|
||||
if (isCachingEnabled()) mod(ctx().drawBuffer).setValue(mode);
|
||||
RENDER_BACKEND.drawBuffer(mode);
|
||||
}
|
||||
@@ -3121,18 +3158,24 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glVertexAttrib2f(int index, float v0, float v1) {
|
||||
RENDER_BACKEND.vertexAttrib2f(index, v0, v1);
|
||||
glVertexAttrib4f(index, v0, v1, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
public static void glVertexAttrib2s(int index, short v0, short v1) {
|
||||
RENDER_BACKEND.vertexAttrib2f(index, v0, v1);
|
||||
glVertexAttrib4f(index, v0, v1, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
public static void glVertexAttrib3f(int index, float v0, float v1, float v2) {
|
||||
RENDER_BACKEND.vertexAttrib3f(index, v0, v1, v2);
|
||||
glVertexAttrib4f(index, v0, v1, v2, 1.0f);
|
||||
}
|
||||
|
||||
public static void glVertexAttrib4f(int index, float v0, float v1, float v2, float v3) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordVertexAttrib(index, v0, v1, v2, v3);
|
||||
if (mode == RecordMode.COMPILE) return;
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
RENDER_BACKEND.vertexAttrib4f(index, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
@@ -3361,6 +3404,7 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!isCachingEnabled() || ctx().logicOpMode.getValue() != opcode) beforeUncapturedStateChange();
|
||||
if (isCachingEnabled()) mod(ctx().logicOpMode).setValue(opcode);
|
||||
RENDER_BACKEND.logicOp(opcode);
|
||||
}
|
||||
@@ -3651,6 +3695,7 @@ public class GLStateManager {
|
||||
final float green = param.get(1);
|
||||
final float blue = param.get(2);
|
||||
|
||||
beforeUncapturedStateChange();
|
||||
mod(glCtx.fogState);
|
||||
glCtx.fogState.getFogColor().set(red, green, blue);
|
||||
glCtx.fogState.setFogAlpha(param.get(3));
|
||||
@@ -3674,6 +3719,7 @@ public class GLStateManager {
|
||||
final GLContextState glCtx = ctx();
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || red != glCtx.fogState.getFogColor().x || green != glCtx.fogState.getFogColor().y || blue != glCtx.fogState.getFogColor().z || alpha != glCtx.fogState.getFogAlpha()) {
|
||||
beforeUncapturedStateChange();
|
||||
mod(glCtx.fogState);
|
||||
glCtx.fogState.getFogColor().set(red, green, blue);
|
||||
glCtx.fogState.setFogAlpha(alpha);
|
||||
@@ -3699,10 +3745,10 @@ public class GLStateManager {
|
||||
if (isCachingEnabled()) {
|
||||
boolean changed = false;
|
||||
switch (pname) {
|
||||
case GL11.GL_FOG_DENSITY -> { if (!isCachingEnabled() || glCtx.fogState.getDensity() != param) { mod(glCtx.fogState).setDensity(param); changed = true; } }
|
||||
case GL11.GL_FOG_START -> { if (!isCachingEnabled() || glCtx.fogState.getStart() != param) { mod(glCtx.fogState).setStart(param); changed = true; } }
|
||||
case GL11.GL_FOG_END -> { if (!isCachingEnabled() || glCtx.fogState.getEnd() != param) { mod(glCtx.fogState).setEnd(param); changed = true; } }
|
||||
case GL11.GL_FOG_MODE -> { if (!isCachingEnabled() || glCtx.fogState.getFogMode() != (int) param) { mod(glCtx.fogState).setFogMode((int) param); changed = true; } }
|
||||
case GL11.GL_FOG_DENSITY -> { if (!isCachingEnabled() || glCtx.fogState.getDensity() != param) { beforeUncapturedStateChange(); mod(glCtx.fogState).setDensity(param); changed = true; } }
|
||||
case GL11.GL_FOG_START -> { if (!isCachingEnabled() || glCtx.fogState.getStart() != param) { beforeUncapturedStateChange(); mod(glCtx.fogState).setStart(param); changed = true; } }
|
||||
case GL11.GL_FOG_END -> { if (!isCachingEnabled() || glCtx.fogState.getEnd() != param) { beforeUncapturedStateChange(); mod(glCtx.fogState).setEnd(param); changed = true; } }
|
||||
case GL11.GL_FOG_MODE -> { if (!isCachingEnabled() || glCtx.fogState.getFogMode() != (int) param) { beforeUncapturedStateChange(); mod(glCtx.fogState).setFogMode((int) param); changed = true; } }
|
||||
default -> {}
|
||||
}
|
||||
if (changed) {
|
||||
@@ -3725,6 +3771,7 @@ public class GLStateManager {
|
||||
}
|
||||
// Update cached state (FFP shader reads from cache)
|
||||
if (isCachingEnabled() && pname == GL11.GL_FOG_MODE && (!isCachingEnabled() || glCtx.fogState.getFogMode() != param)) {
|
||||
beforeUncapturedStateChange();
|
||||
mod(glCtx.fogState).setFogMode(param);
|
||||
glCtx.fragmentGeneration++;
|
||||
if (GLSMHooks.FOG_STATE_CHANGE.hasListeners()) {
|
||||
@@ -3979,6 +4026,7 @@ public class GLStateManager {
|
||||
}
|
||||
final int depthBefore = glCtx.attribDepth;
|
||||
glCtx.attribSets[depthBefore] = set;
|
||||
glCtx.savedBatchStateGen[depthBefore] = glCtx.batchStateGeneration;
|
||||
|
||||
snapshotGenerations(glCtx, set.bump);
|
||||
|
||||
@@ -4027,6 +4075,9 @@ public class GLStateManager {
|
||||
|
||||
public static void popState() {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (glCtx.attribDepth > 0 && glCtx.savedBatchStateGen[glCtx.attribDepth - 1] != glCtx.batchStateGeneration) {
|
||||
beforeUncapturedStateChange();
|
||||
}
|
||||
final StateSet set = glCtx.attribSets[glCtx.attribDepth - 1];
|
||||
final boolean inspectAlpha = GLSMHooks.ALPHA_STATE_CHANGE.hasListeners() && (set.contains(glCtx.alphaState.stackId()) || set.contains(glCtx.alphaTest.stackId()));
|
||||
final AlphaState effectiveAlpha = inspectAlpha ? glCtx.alphaState.readEffective(glCtx.restoredAlphaScratch) : null;
|
||||
@@ -4418,6 +4469,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glLoadMatrix(FloatBuffer m) {
|
||||
beforeMatrixChange();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
final Matrix4f matrix = new Matrix4f().set(m);
|
||||
@@ -4434,6 +4486,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glLoadMatrix(DoubleBuffer m) {
|
||||
beforeMatrixChange();
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
@@ -4464,6 +4517,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void setTextureMatrix(int unit, Matrix4fc m) {
|
||||
if (unit != 0) beforeUncapturedStateChange();
|
||||
if (isCachingEnabled()) {
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.textures.getTextureUnitMatrix(unit).set(m);
|
||||
@@ -4513,6 +4567,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glLoadIdentity() {
|
||||
beforeMatrixChange();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordLoadIdentity();
|
||||
@@ -4527,6 +4582,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glTranslatef(float x, float y, float z) {
|
||||
beforeMatrixChange();
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixTranslation(x, y, z);
|
||||
return;
|
||||
@@ -4538,6 +4594,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glTranslated(double x, double y, double z) {
|
||||
beforeMatrixChange();
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixTranslation((float) x, (float) y, (float) z);
|
||||
return;
|
||||
@@ -4549,6 +4606,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glScalef(float x, float y, float z) {
|
||||
beforeMatrixChange();
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixScale(x, y, z);
|
||||
return;
|
||||
@@ -4560,6 +4618,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glScaled(double x, double y, double z) {
|
||||
beforeMatrixChange();
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.applyMatrixScale((float) x, (float) y, (float) z);
|
||||
return;
|
||||
@@ -4571,6 +4630,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glMultMatrix(FloatBuffer floatBuffer) {
|
||||
beforeMatrixChange();
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.multMatrix.set(floatBuffer);
|
||||
final int currentMode = glCtx.matrixMode.getMode();
|
||||
@@ -4586,6 +4646,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void applyMultMatrix(Matrix4f multMatrix) {
|
||||
beforeMatrixChange();
|
||||
if (isCachingEnabled()) {
|
||||
getMatrixStack().mul(multMatrix);
|
||||
bumpMatrixGeneration();
|
||||
@@ -4593,6 +4654,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glMultMatrix(DoubleBuffer matrix) {
|
||||
beforeMatrixChange();
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.conversionMatrix4d.set(matrix);
|
||||
glCtx.conversionMatrix4f.set(glCtx.conversionMatrix4d);
|
||||
@@ -4608,6 +4670,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glRotatef(float angle, float x, float y, float z) {
|
||||
beforeMatrixChange();
|
||||
final float lenSq = x * x + y * y + z * z;
|
||||
if (lenSq == 0.0f) return;
|
||||
|
||||
@@ -4630,6 +4693,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glRotated(double angle, double x, double y, double z) {
|
||||
beforeMatrixChange();
|
||||
final double lenSq = x * x + y * y + z * z;
|
||||
if (lenSq == 0.0) return;
|
||||
|
||||
@@ -4652,6 +4716,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glOrtho(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
beforeMatrixChange();
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.updateRelativeTransformOrtho(left, right, bottom, top, zNear, zFar);
|
||||
return;
|
||||
@@ -4663,6 +4728,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glFrustum(double left, double right, double bottom, double top, double zNear, double zFar) {
|
||||
beforeMatrixChange();
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.updateRelativeTransformFrustum(left, right, bottom, top, zNear, zFar);
|
||||
return;
|
||||
@@ -4692,6 +4758,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glPopMatrix() {
|
||||
beforeMatrixChange();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordPopMatrix(); // Handles flush + relativeTransform stack + lastRecordedTransform sync
|
||||
@@ -4787,6 +4854,7 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
RENDER_BACKEND.viewport(x, y, width, height);
|
||||
// Only update cached state when caching is enabled
|
||||
if (isCachingEnabled()) {
|
||||
@@ -4905,6 +4973,7 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (getActiveTextureUnit() != 0) beforeUncapturedStateChange();
|
||||
if (target != GL11.GL_TEXTURE_2D) {
|
||||
RENDER_BACKEND.texParameteri(target, pname, param);
|
||||
return;
|
||||
@@ -4948,6 +5017,7 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (getActiveTextureUnit() != 0) beforeUncapturedStateChange();
|
||||
if (target != GL11.GL_TEXTURE_2D) {
|
||||
RENDER_BACKEND.texParameterf(target, pname, param);
|
||||
return;
|
||||
@@ -5194,6 +5264,7 @@ public class GLStateManager {
|
||||
public static void glBindSampler(int unit, int sampler) {
|
||||
final SamplerUnitArray units = ctx().samplerUnits;
|
||||
if (isCachingEnabled() && unit >= 0 && unit < units.size() && units.get(unit) == sampler) return;
|
||||
beforeUncapturedStateChange();
|
||||
if (!units.set(unit, sampler)) {
|
||||
warnOnce("samplerUnitRange", "glBindSampler unit {} exceeds GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS {}", unit, units.size());
|
||||
}
|
||||
@@ -5609,7 +5680,9 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glDepthRange(double near, double far) {
|
||||
if (isCachingEnabled()) mod(ctx().viewportState).setDepthRange(near, far);
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled() || glCtx.viewportState.depthRangeNear != near || glCtx.viewportState.depthRangeFar != far) beforeUncapturedStateChange();
|
||||
if (isCachingEnabled()) mod(glCtx.viewportState).setDepthRange(near, far);
|
||||
RENDER_BACKEND.depthRange(near, far);
|
||||
}
|
||||
|
||||
@@ -5646,6 +5719,10 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
// Non-zero program or FFP emulation not enabled
|
||||
if (program == 0) {
|
||||
warnOnce("useprogram-zero-ffp-disabled",
|
||||
"[FFP] glUseProgram(0) reached the driver because emulation is disabled");
|
||||
}
|
||||
if (ffp.isActive()) {
|
||||
ffp.deactivate();
|
||||
}
|
||||
@@ -6052,24 +6129,19 @@ public class GLStateManager {
|
||||
}
|
||||
// Track front/back separately in cache (Mesa compat semantics), but always issue GL_FRONT_AND_BACK to the driver (core profile constraint).
|
||||
final boolean caching = isCachingEnabled();
|
||||
final boolean needsUpdate;
|
||||
if (face == GL11.GL_FRONT) {
|
||||
needsUpdate = !caching || glCtx.polygonState.getFrontMode() != polygonMode;
|
||||
if (caching && needsUpdate) mod(glCtx.polygonState).setFrontMode(polygonMode);
|
||||
} else if (face == GL11.GL_BACK) {
|
||||
needsUpdate = !caching || glCtx.polygonState.getBackMode() != polygonMode;
|
||||
if (caching && needsUpdate) mod(glCtx.polygonState).setBackMode(polygonMode);
|
||||
} else { // GL_FRONT_AND_BACK
|
||||
needsUpdate = !caching || glCtx.polygonState.getFrontMode() != polygonMode || glCtx.polygonState.getBackMode() != polygonMode;
|
||||
if (caching && needsUpdate) {
|
||||
mod(glCtx.polygonState);
|
||||
glCtx.polygonState.setFrontMode(polygonMode);
|
||||
glCtx.polygonState.setBackMode(polygonMode);
|
||||
}
|
||||
}
|
||||
if (needsUpdate) {
|
||||
RENDER_BACKEND.polygonMode(GL11.GL_FRONT_AND_BACK, polygonMode);
|
||||
final boolean front = face != GL11.GL_BACK;
|
||||
final boolean back = face != GL11.GL_FRONT;
|
||||
final boolean needsUpdate = !caching
|
||||
|| (front && glCtx.polygonState.getFrontMode() != polygonMode)
|
||||
|| (back && glCtx.polygonState.getBackMode() != polygonMode);
|
||||
if (!needsUpdate) return;
|
||||
beforeUncapturedStateChange();
|
||||
if (caching) {
|
||||
mod(glCtx.polygonState);
|
||||
if (front) glCtx.polygonState.setFrontMode(polygonMode);
|
||||
if (back) glCtx.polygonState.setBackMode(polygonMode);
|
||||
}
|
||||
RENDER_BACKEND.polygonMode(GL11.GL_FRONT_AND_BACK, polygonMode);
|
||||
}
|
||||
|
||||
public static void glPolygonOffset(float factor, float units) {
|
||||
@@ -6138,6 +6210,7 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
RENDER_BACKEND.scissor(x, y, width, height);
|
||||
if (isCachingEnabled()) {
|
||||
mod(ctx().scissorState).setScissor(x, y, width, height);
|
||||
@@ -6156,6 +6229,7 @@ public class GLStateManager {
|
||||
final int clampedMask = mask & glCtx.stencilBitMask;
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || glCtx.stencilState.getFuncFront() != func || glCtx.stencilState.getRefFront() != ref || glCtx.stencilState.getValueMaskFront() != clampedMask) {
|
||||
beforeUncapturedStateChange();
|
||||
mod(glCtx.stencilState).setFunc(func, ref, clampedMask);
|
||||
RENDER_BACKEND.stencilFunc(func, ref, clampedMask);
|
||||
}
|
||||
@@ -6173,6 +6247,7 @@ public class GLStateManager {
|
||||
final int clampedMask = mask & glCtx.stencilBitMask;
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || glCtx.stencilState.getWriteMaskFront() != clampedMask) {
|
||||
beforeUncapturedStateChange();
|
||||
mod(glCtx.stencilState).setWriteMask(clampedMask);
|
||||
RENDER_BACKEND.stencilMask(clampedMask);
|
||||
}
|
||||
@@ -6189,6 +6264,7 @@ public class GLStateManager {
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
if (!caching || glCtx.stencilState.getFailOpFront() != fail || glCtx.stencilState.getZFailOpFront() != zfail || glCtx.stencilState.getZPassOpFront() != zpass) {
|
||||
beforeUncapturedStateChange();
|
||||
mod(glCtx.stencilState).setOp(fail, zfail, zpass);
|
||||
RENDER_BACKEND.stencilOp(fail, zfail, zpass);
|
||||
}
|
||||
@@ -6244,6 +6320,7 @@ public class GLStateManager {
|
||||
final int index = plane - GL11.GL_CLIP_PLANE0;
|
||||
if (index < 0 || index >= MAX_CLIP_PLANES) return;
|
||||
final int pos = equation.position();
|
||||
beforeUncapturedStateChange();
|
||||
glCtx.clipPlaneState.setPlane(index, equation.get(pos), equation.get(pos + 1), equation.get(pos + 2), equation.get(pos + 3), glCtx.modelViewMatrix);
|
||||
glCtx.clipPlaneGeneration++;
|
||||
}
|
||||
@@ -6283,6 +6360,7 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
RENDER_BACKEND.drawBuffers(buffer);
|
||||
}
|
||||
|
||||
@@ -6294,6 +6372,7 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
RENDER_BACKEND.drawBuffers(bufs);
|
||||
}
|
||||
|
||||
@@ -6302,6 +6381,7 @@ public class GLStateManager {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
throw DisplayListManager.unsupportedInList("glSampleCoverage");
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
RENDER_BACKEND.sampleCoverage(value, invert);
|
||||
}
|
||||
|
||||
@@ -6315,6 +6395,7 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
final int clampedMask = mask & glCtx.stencilBitMask;
|
||||
final boolean caching = isCachingEnabled();
|
||||
boolean needsUpdate = !caching;
|
||||
@@ -6351,6 +6432,7 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
final int clampedMask = mask & glCtx.stencilBitMask;
|
||||
final boolean caching = isCachingEnabled();
|
||||
boolean needsUpdate = !caching;
|
||||
@@ -6383,6 +6465,7 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
final boolean caching = isCachingEnabled();
|
||||
boolean needsUpdate = !caching;
|
||||
if (!needsUpdate) {
|
||||
@@ -7149,7 +7232,7 @@ public class GLStateManager {
|
||||
glCtx.ffp.setTexCoord(s, t, 0.0f, 1.0f);
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
} else if (target == GL13.GL_TEXTURE1) {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
if (ImmediateModeRecorder.isDrawing()) {
|
||||
ImmediateModeRecorder.setLightmapCoord(s, t);
|
||||
return;
|
||||
}
|
||||
@@ -7160,6 +7243,7 @@ public class GLStateManager {
|
||||
if (DisplayListManager.isRecording() || ImmediateModeRecorder.isDrawing()) {
|
||||
glCtx.unit23TexCoordSetDuringDraw = true;
|
||||
}
|
||||
beforeUncapturedStateChange();
|
||||
glCtx.ffp.setTexCoord(unit, s, t, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
@@ -7175,6 +7259,11 @@ public class GLStateManager {
|
||||
|
||||
public static void setLightmapTextureCoords(int unit, float x, float y) {
|
||||
if (unit == GL13.GL_TEXTURE1) {
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordStateCommand(() -> setLightmapTextureCoords(unit, x, y));
|
||||
if (mode == RecordMode.COMPILE) return;
|
||||
}
|
||||
final GLContextState glCtx = ctx();
|
||||
glCtx.lastBrightnessX = x;
|
||||
glCtx.lastBrightnessY = y;
|
||||
@@ -7253,6 +7342,7 @@ public class GLStateManager {
|
||||
public static void glTexEnv(int target, int pname, FloatBuffer params) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (target == GL11.GL_TEXTURE_ENV && pname == GL11.GL_TEXTURE_ENV_COLOR && params.remaining() >= 4) {
|
||||
beforeUncapturedStateChange();
|
||||
final int pos = params.position();
|
||||
final var envState = glCtx.textures.getTexEnvState(glCtx.activeTextureUnit.getValue());
|
||||
envState.beforeModify();
|
||||
@@ -7283,6 +7373,7 @@ public class GLStateManager {
|
||||
* GL_TEXTURE_ENV parameters including GL_COMBINE sub-params are tracked in per-unit TexEnvState.
|
||||
*/
|
||||
private static void handleTexEnvScalar(int target, int pname, float param) {
|
||||
beforeUncapturedStateChange();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (target == GL14.GL_TEXTURE_FILTER_CONTROL) {
|
||||
// LOD bias via legacy glTexEnv path - remap to core-profile glTexParameterf
|
||||
@@ -7429,6 +7520,7 @@ public class GLStateManager {
|
||||
|
||||
|
||||
public static void glTexGeni(int coord, int pname, int param) {
|
||||
beforeUncapturedStateChange();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled()) return;
|
||||
if (pname == GL11.GL_TEXTURE_GEN_MODE) {
|
||||
@@ -7448,6 +7540,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glTexGen(int coord, int pname, FloatBuffer params) {
|
||||
beforeUncapturedStateChange();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled()) return;
|
||||
final int unit = glCtx.activeTextureUnit.getValue();
|
||||
@@ -7478,6 +7571,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glTexGen(int coord, int pname, DoubleBuffer params) {
|
||||
beforeUncapturedStateChange();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled()) return;
|
||||
final int pos = params.position();
|
||||
@@ -7504,6 +7598,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glTexGen(int coord, int pname, IntBuffer params) {
|
||||
beforeUncapturedStateChange();
|
||||
final GLContextState glCtx = ctx();
|
||||
if (!isCachingEnabled()) return;
|
||||
if (pname == GL11.GL_TEXTURE_GEN_MODE) {
|
||||
@@ -7672,6 +7767,14 @@ public class GLStateManager {
|
||||
RENDER_BACKEND.deleteShader(vertShader);
|
||||
}
|
||||
|
||||
@Getter
|
||||
private static long programLifetimeGeneration;
|
||||
private static final IntOpenHashSet programsPendingDeletion = new IntOpenHashSet();
|
||||
|
||||
public static boolean isProgramPendingDeletion(int program) {
|
||||
return programsPendingDeletion.contains(program);
|
||||
}
|
||||
|
||||
public static void glDeleteProgram(int program) {
|
||||
if (program == 0) return;
|
||||
CompatUniformManager.onDeleteProgram(program);
|
||||
@@ -7683,6 +7786,9 @@ public class GLStateManager {
|
||||
GLSMHooks.PROGRAM_DELETE.post(GLSMHooks.programDeleteEvent);
|
||||
}
|
||||
RENDER_BACKEND.deleteProgram(program);
|
||||
programsPendingDeletion.add(program);
|
||||
programsPendingDeletion.removeIf((int id) -> !RENDER_BACKEND.isProgram(id));
|
||||
programLifetimeGeneration++;
|
||||
}
|
||||
|
||||
public static int glCreateShader(int type) {
|
||||
@@ -7694,7 +7800,10 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static int glCreateProgram() {
|
||||
return RENDER_BACKEND.createProgram();
|
||||
final int program = RENDER_BACKEND.createProgram();
|
||||
programsPendingDeletion.remove(program);
|
||||
programLifetimeGeneration++;
|
||||
return program;
|
||||
}
|
||||
|
||||
public static void glAttachShader(int program, int shader) {
|
||||
@@ -7742,7 +7851,9 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static boolean glIsProgram(int obj) {
|
||||
return RENDER_BACKEND.isProgram(obj);
|
||||
final boolean valid = RENDER_BACKEND.isProgram(obj);
|
||||
if (!valid) programsPendingDeletion.remove(obj);
|
||||
return valid;
|
||||
}
|
||||
|
||||
public static boolean glIsShader(int obj) {
|
||||
@@ -8009,6 +8120,9 @@ public class GLStateManager {
|
||||
public static void glGetTexImage(int target, int level, int format, int type, long pixels) {
|
||||
RENDER_BACKEND.getTexImage(target, level, format, type, pixels);
|
||||
}
|
||||
public static void glReadPixels(int x, int y, int width, int height, int format, int type, long pixels) {
|
||||
RENDER_BACKEND.readPixels(x, y, width, height, format, remapPixelTypeForGLES(format, type), pixels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw modes that rely on the previous vertex data cannot be merged currently.
|
||||
@@ -8404,6 +8518,56 @@ public class GLStateManager {
|
||||
return ctx().backMaterial;
|
||||
}
|
||||
|
||||
public static void captureLighting(LightingSnapshot out) {
|
||||
final GLContextState glCtx = ctx();
|
||||
for (int i = 0; i < out.lights.length; i++) {
|
||||
out.lightEnabled[i] = glCtx.lightStates[i].isEnabled();
|
||||
out.lights[i].set(glCtx.lightDataStates[i]);
|
||||
}
|
||||
out.lightModel.set(glCtx.lightModel);
|
||||
out.frontMaterial.set(glCtx.frontMaterial);
|
||||
out.backMaterial.set(glCtx.backMaterial);
|
||||
out.colorMaterial = glCtx.colorMaterial.isEnabled();
|
||||
out.colorMaterialFace = glCtx.colorMaterialFace.getValue();
|
||||
out.colorMaterialParameter = glCtx.colorMaterialParameter.getValue();
|
||||
out.normalize = glCtx.normalizeState.isEnabled();
|
||||
out.rescaleNormal = glCtx.rescaleNormalState.isEnabled();
|
||||
}
|
||||
|
||||
public static void applyLighting(LightingSnapshot in) {
|
||||
final GLContextState glCtx = ctx();
|
||||
boolean changed = false;
|
||||
for (int i = 0; i < in.lights.length; i++) {
|
||||
glCtx.lightStates[i].setEnabled(in.lightEnabled[i]);
|
||||
if (!glCtx.lightDataStates[i].sameAs(in.lights[i])) {
|
||||
mod(glCtx.lightDataStates[i]).set(in.lights[i]);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (!glCtx.lightModel.sameAs(in.lightModel)) {
|
||||
mod(glCtx.lightModel).set(in.lightModel);
|
||||
changed = true;
|
||||
}
|
||||
if (!glCtx.frontMaterial.sameAs(in.frontMaterial)) {
|
||||
mod(glCtx.frontMaterial).set(in.frontMaterial);
|
||||
changed = true;
|
||||
}
|
||||
if (!glCtx.backMaterial.sameAs(in.backMaterial)) {
|
||||
mod(glCtx.backMaterial).set(in.backMaterial);
|
||||
changed = true;
|
||||
}
|
||||
if (glCtx.colorMaterialFace.getValue() != in.colorMaterialFace || glCtx.colorMaterialParameter.getValue() != in.colorMaterialParameter) {
|
||||
mod(glCtx.colorMaterialFace).setValue(in.colorMaterialFace);
|
||||
mod(glCtx.colorMaterialParameter).setValue(in.colorMaterialParameter);
|
||||
changed = true;
|
||||
}
|
||||
// Direct set: glEnable(GL_COLOR_MATERIAL) would bake the current color over the captured material.
|
||||
glCtx.colorMaterial.setEnabled(in.colorMaterial);
|
||||
glCtx.normalizeState.setEnabled(in.normalize);
|
||||
glCtx.rescaleNormalState.setEnabled(in.rescaleNormal);
|
||||
if (changed) glCtx.lightingGeneration++;
|
||||
}
|
||||
|
||||
public static ViewPortStateStack getViewportState() {
|
||||
return ctx().viewportState;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -496,10 +496,12 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -393,6 +393,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);
|
||||
|
||||
@@ -4,6 +4,7 @@ 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;
|
||||
@@ -226,8 +227,13 @@ 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(GLContextState glCtx, int vboId) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -116,9 +116,10 @@ public final class ShaderManager {
|
||||
|
||||
public void activate() {
|
||||
active = true;
|
||||
lastBoundProgramId = GLStateManager.getActiveProgram();
|
||||
invalidateProgram();
|
||||
}
|
||||
|
||||
/** Invalidate both binding and state key so the next draw selects and binds an FFP variant. */
|
||||
public void invalidateProgram() {
|
||||
lastBoundProgramId = -1;
|
||||
currentProgram = null;
|
||||
@@ -170,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++;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,28 @@
|
||||
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 static int pausedTexture;
|
||||
private static int pausedMin;
|
||||
private static int pausedMag;
|
||||
private static float pausedAniso;
|
||||
|
||||
private static int nearestTexture;
|
||||
private static int nearestMin;
|
||||
private static int nearestMag;
|
||||
private static float nearestAniso;
|
||||
|
||||
private TextureUtilHooks() {}
|
||||
|
||||
public static void setMaxLevel(int mipmapLevels) {
|
||||
@@ -16,7 +30,17 @@ public final class TextureUtilHooks {
|
||||
}
|
||||
|
||||
public static void captureBoundFilterTexture() {
|
||||
GLStateManager.ctx().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() {
|
||||
@@ -42,4 +66,61 @@ public final class TextureUtilHooks {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+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;
|
||||
|
||||
|
||||
+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();
|
||||
|
||||
+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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
+242
-107
@@ -25,6 +25,7 @@ import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager.FrameState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.Presenter;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.MainStartOnFirstThread;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.pipeline.AttachmentClear;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.pipeline.DepthStencilReadback;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.pipeline.DrawDispatch;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.pipeline.FFPTrace;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineApplier;
|
||||
@@ -39,8 +40,10 @@ import com.gtnewhorizons.angelica.sdlgpu.resource.FboState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.FormatMap;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.Image3DClear;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PersistentBufferSync;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PackState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PersistentMapping;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PixelOps;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PixelPack;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ReadbackShadows;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.TextureOps;
|
||||
@@ -154,6 +157,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
});
|
||||
private final PipelineApplier pipelineApplier = new PipelineApplier(frameManager, resourceManager, shaderManager, pipelineStore, fboClearTracker, persistentSync, samplerBinder, storageTextureBinder, storageBufferBinder);
|
||||
private final AttachmentClear attachmentClear = new AttachmentClear(frameManager, pipelineStore, shaderManager);
|
||||
private final DepthStencilReadback depthStencilReadback = new DepthStencilReadback(frameManager, resourceManager, pipelineStore, shaderManager, fboClearTracker);
|
||||
private final DrawDispatch drawDispatch = new DrawDispatch(device, frameManager, resourceManager, pipelineApplier, this::enqueuePreCopied);
|
||||
private final FFPTrace ffpTrace = SystemProperties.FFP_TRACE ? new FFPTrace(resourceManager) : null;
|
||||
|
||||
@@ -494,6 +498,14 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
if (st.cachedViewport != null) { st.cachedViewport.free(); st.cachedViewport = null; }
|
||||
if (st.cachedScissor != null) { st.cachedScissor.free(); st.cachedScissor = null; }
|
||||
if (st.cachedBlendColor != null) { st.cachedBlendColor.free(); st.cachedBlendColor = null; }
|
||||
for (int i = 0; i < st.logicOpScratch.length; i++) {
|
||||
if (st.logicOpScratch[i] != 0L) resourceManager.releaseTextureHandle(st.logicOpScratch[i]);
|
||||
st.logicOpScratch[i] = 0L;
|
||||
}
|
||||
depthStencilReadback.release(st);
|
||||
st.activeLogicOpVariant = null;
|
||||
st.lastFragSamplerVariant = null;
|
||||
st.appliedLogicOpKey = 0;
|
||||
st.fragSamplerBindings.free();
|
||||
st.vertSamplerBindings.free();
|
||||
if (st.mappedStagingBuffer != null) {
|
||||
@@ -505,6 +517,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
|
||||
drawDispatch.clearWarnedState();
|
||||
attachmentClear.shutdown();
|
||||
depthStencilReadback.shutdown();
|
||||
textureOps.shutdown();
|
||||
pipelineStore.shutdown();
|
||||
shaderManager.shutdown();
|
||||
resourceManager.shutdown();
|
||||
@@ -542,11 +556,12 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
|
||||
@Override public void onRenderThreadReleased(Thread t) {
|
||||
if (shutdown || t == GLStateManager.getMainThread()) return;
|
||||
final ContextState st = tlState.get();
|
||||
if (frameManager.isFrameActive()) {
|
||||
if (st != null) fboClearTracker.materializeAllPendingClears(st);
|
||||
frameManager.endFrame();
|
||||
}
|
||||
frameManager.releaseThreadState();
|
||||
final ContextState st = tlState.get();
|
||||
if (st != null) {
|
||||
tlState.remove();
|
||||
registeredStates.remove(st);
|
||||
@@ -601,6 +616,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
@Override public boolean handleSwapBuffers() {
|
||||
final OffscreenTarget splash = splashTarget;
|
||||
if (splash != null && splash.isFor(s()) && Thread.currentThread() != GLStateManager.getMainThread()) {
|
||||
if (frameManager.isFrameActive()) fboClearTracker.materializeAllPendingClears(s());
|
||||
endFrameUploadFlush();
|
||||
frameManager.endFrame();
|
||||
SplashDispatcher.signalFrameReady((int) s().viewportW, (int) s().viewportH);
|
||||
@@ -647,8 +663,6 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
GLStateManager.replayStateToBackend();
|
||||
}
|
||||
ResourceManager.setSingleThreadedReads(GLStateManager.isSplashComplete());
|
||||
st.clearedTexturesThisFrame.clear();
|
||||
st.clearedStencilTexturesThisFrame.clear();
|
||||
st.pendingColorTextures.clear();
|
||||
st.pendingDepthTextures.clear();
|
||||
st.pendingStencilTextures.clear();
|
||||
@@ -728,6 +742,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
frameManager.markFrameEmpty(f);
|
||||
}
|
||||
|
||||
if (f.frameActive) fboClearTracker.materializeAllPendingClears(s());
|
||||
awaitUploadFlush();
|
||||
if (SystemProperties.FFP_TRACE) ffpTrace.frameEnd(f);
|
||||
frameManager.endFrame();
|
||||
@@ -740,6 +755,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
@Override public void onPreSwapchainInvalidatingChange(Object change) {
|
||||
super.onPreSwapchainInvalidatingChange(change);
|
||||
if (presenter != null) presenter.drain();
|
||||
if (frameManager.isFrameActive()) fboClearTracker.materializeAllPendingClears(s());
|
||||
endFrameUploadFlush();
|
||||
if (frameManager.isFrameActive()) {
|
||||
frameManager.endFrame();
|
||||
@@ -833,6 +849,13 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
st.scissorEnabled = on;
|
||||
st.scissorDirty = true;
|
||||
}
|
||||
case GL11.GL_COLOR_LOGIC_OP -> {
|
||||
if (cs.logicOpEnabled != on) {
|
||||
cs.logicOpEnabled = on;
|
||||
cs.pipeline.logicOpEnabled = on;
|
||||
cs.pipeline.markOutputDirty();
|
||||
}
|
||||
}
|
||||
case GL31.GL_PRIMITIVE_RESTART -> cs.primitiveRestartEnabled = on;
|
||||
case GL30.GL_RASTERIZER_DISCARD -> cs.rasterizerDiscard = on;
|
||||
}
|
||||
@@ -1134,7 +1157,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
|
||||
@Override public void lineWidth(float width) { /* no-op */ }
|
||||
@Override public void pointSize(float size) { /* no-op */ }
|
||||
@Override public void logicOp(int opcode) { /* no-op */ }
|
||||
@Override public void logicOp(int opcode) { s().logicOpMode = opcode; }
|
||||
@Override public void hint(int target, int mode) { /* no-op */ }
|
||||
|
||||
@Override public void flush() { /* no-op */ }
|
||||
@@ -1156,7 +1179,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
return;
|
||||
}
|
||||
drawDispatch.setPrimitiveTypeForDraw(st, FormatMap.mapPrimitiveType(mode));
|
||||
pipelineApplier.ensureRenderPass(st, f);
|
||||
pipelineApplier.ensureDrawRenderPass(st, f);
|
||||
if (f.renderPass == 0) { f.droppedDrawsThisFrame++; drawDispatch.warnDrawArraysNoRenderPass(mode, st.boundFboId); return; }
|
||||
if (!pipelineApplier.applyPipelineAndState(st, f)) { f.droppedDrawsThisFrame++; return; }
|
||||
if (SystemProperties.FFP_TRACE) ffpTrace.trace(st, "arrays", mode, count, 0, first, 0);
|
||||
@@ -1521,7 +1544,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
if (cp == 0) return;
|
||||
final ByteBuffer unpacked = PixelOps.applyUnpackPixelStore(pixels, width, height * depth, format, type, st.pixelStore);
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(glId);
|
||||
final ByteBuffer prepped = PixelOps.prepareUploadBuffer(format, meta.sdlFormat(), unpacked, width, height * depth);
|
||||
final ByteBuffer prepped = PixelOps.prepareUploadBuffer(format, type, meta.sdlFormat(), unpacked, width, height * depth);
|
||||
try {
|
||||
resourceManager.uploadToTexture3D(cp, prepped, texHandle, 0, 0, 0, width, height, depth, level);
|
||||
} finally {
|
||||
@@ -1675,11 +1698,15 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
final ContextState.PixelStoreState ps = s().pixelStore;
|
||||
switch (pname) {
|
||||
case GL11.GL_UNPACK_ALIGNMENT -> ps.unpackAlignment = param;
|
||||
case GL11.GL_PACK_ALIGNMENT -> ps.packAlignment = param;
|
||||
case GL11.GL_PACK_ALIGNMENT -> { if (param == 1 || param == 2 || param == 4 || param == 8) ps.packAlignment = param; }
|
||||
case GL11.GL_PACK_ROW_LENGTH -> { if (param >= 0) ps.packRowLength = param; }
|
||||
case GL11.GL_PACK_SKIP_PIXELS -> { if (param >= 0) ps.packSkipPixels = param; }
|
||||
case GL11.GL_PACK_SKIP_ROWS -> { if (param >= 0) ps.packSkipRows = param; }
|
||||
case GL11.GL_PACK_SWAP_BYTES -> ps.packSwapBytes = param != 0;
|
||||
case GL11.GL_UNPACK_ROW_LENGTH -> ps.unpackRowLength = param;
|
||||
case GL11.GL_UNPACK_SKIP_PIXELS -> ps.unpackSkipPixels = param;
|
||||
case GL11.GL_UNPACK_SKIP_ROWS -> ps.unpackSkipRows = param;
|
||||
default -> { /* GL_PACK_* and GL_UNPACK_SWAP_BYTES etc are ignored */ }
|
||||
default -> { /* GL_UNPACK_SWAP_BYTES etc are ignored */ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1944,123 +1971,217 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
|
||||
private static final int READBACK_COLOR = 0;
|
||||
private static final int READBACK_DEPTH = 1;
|
||||
private static final int READBACK_STENCIL = 2;
|
||||
private static final int READBACK_DEPTH_STENCIL = 3;
|
||||
|
||||
@Override public void readPixels(int x, int y, int width, int height, int format, int type, ByteBuffer pixels) {
|
||||
if (pixels == null || width <= 0 || height <= 0) return;
|
||||
if (pixels == null) return;
|
||||
readPixelsInto(x, y, width, height, format, type, MemoryUtil.memAddress(pixels), pixels.remaining(), 0, 0L);
|
||||
}
|
||||
@Override public void readPixels(int x, int y, int width, int height, int format, int type, FloatBuffer pixels) {
|
||||
if (pixels == null) return;
|
||||
readPixelsInto(x, y, width, height, format, type, MemoryUtil.memAddress(pixels), (long) pixels.remaining() << 2, 0, 0L);
|
||||
}
|
||||
@Override public void readPixels(int x, int y, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
if (pixels == null) return;
|
||||
readPixelsInto(x, y, width, height, format, type, MemoryUtil.memAddress(pixels), (long) pixels.remaining() << 2, 0, 0L);
|
||||
}
|
||||
@Override public void readPixels(int x, int y, int width, int height, int format, int type, long pixelBufferOffset) {
|
||||
final int pbo = s().boundPixelPackBuffer;
|
||||
if (pbo == 0) return;
|
||||
readPixelsInto(x, y, width, height, format, type, 0L, resourceManager.getBufferSize(pbo) - pixelBufferOffset, pbo, pixelBufferOffset);
|
||||
}
|
||||
@Override public void getTexImage(int target, int level, int format, int type, ByteBuffer pixels) {
|
||||
if (pixels == null) return;
|
||||
getTexImageInto(target, level, format, type, MemoryUtil.memAddress(pixels), pixels.remaining(), 0, 0L);
|
||||
}
|
||||
@Override public void getTexImage(int target, int level, int format, int type, IntBuffer pixels) {
|
||||
if (pixels == null) return;
|
||||
getTexImageInto(target, level, format, type, MemoryUtil.memAddress(pixels), (long) pixels.remaining() << 2, 0, 0L);
|
||||
}
|
||||
@Override public void getTexImage(int target, int level, int format, int type, long pixelBufferOffset) {
|
||||
final int pbo = s().boundPixelPackBuffer;
|
||||
if (pbo == 0) return;
|
||||
getTexImageInto(target, level, format, type, 0L, resourceManager.getBufferSize(pbo) - pixelBufferOffset, pbo, pixelBufferOffset);
|
||||
}
|
||||
|
||||
private static int readbackKind(int format) {
|
||||
if (PixelPack.isDepthStencilFormat(format)) return READBACK_DEPTH_STENCIL;
|
||||
if (PixelPack.isDepthFormat(format)) return READBACK_DEPTH;
|
||||
if (PixelPack.isStencilFormat(format)) return READBACK_STENCIL;
|
||||
return READBACK_COLOR;
|
||||
}
|
||||
|
||||
private static PackState loadPackState(ContextState cs) {
|
||||
final ContextState.PixelStoreState store = cs.pixelStore;
|
||||
final PackState ps = cs.packState;
|
||||
ps.alignment = store.packAlignment;
|
||||
ps.rowLength = store.packRowLength;
|
||||
ps.skipPixels = store.packSkipPixels;
|
||||
ps.skipRows = store.packSkipRows;
|
||||
ps.swapBytes = store.packSwapBytes;
|
||||
return ps;
|
||||
}
|
||||
|
||||
private static void checkReadbackCapacity(int format, int type, int width, int height, PackState ps, long capacity) {
|
||||
final long needed = PixelPack.imageSize(format, type, width, height, ps);
|
||||
if (capacity < needed) throw new IllegalArgumentException("readback of " + width + "x" + height + " format 0x" + Integer.toHexString(format) + " type 0x" + Integer.toHexString(type) + " needs " + needed + " bytes, destination has " + capacity);
|
||||
}
|
||||
|
||||
private void readPixelsInto(int x, int y, int width, int height, int format, int type, long dst, long capacity, int pbo, long pboOffset) {
|
||||
PixelPack.validate(format, type);
|
||||
if (width <= 0 || height <= 0) return;
|
||||
final ContextState cs = s();
|
||||
checkReadbackCapacity(format, type, width, height, loadPackState(cs), capacity);
|
||||
final int kind = readbackKind(format);
|
||||
final boolean fromFbo0 = cs.boundReadFboId == 0;
|
||||
final long texHandle;
|
||||
final int srcSdlFormat;
|
||||
long colorTex = 0L;
|
||||
int colorFormat = 0;
|
||||
int colorBase = GL11.GL_RGBA;
|
||||
long depthTex = 0L;
|
||||
int depthFormat = 0;
|
||||
final int srcWidth;
|
||||
final int srcHeight;
|
||||
final int srcY;
|
||||
if (fromFbo0) {
|
||||
texHandle = frameManager.getFbo0Texture();
|
||||
srcSdlFormat = frameManager.getSwapchainFormat();
|
||||
srcWidth = frameManager.getFbo0Width();
|
||||
srcHeight = frameManager.getFbo0Height();
|
||||
srcY = srcHeight - y - height;
|
||||
if (kind == READBACK_COLOR) {
|
||||
colorTex = frameManager.getFbo0Texture();
|
||||
colorFormat = frameManager.getSwapchainFormat();
|
||||
} else {
|
||||
depthFormat = resourceManager.getSwapchainDepthStencilFormat();
|
||||
if (depthFormat != 0) depthTex = resourceManager.getSwapchainDepthStencil();
|
||||
}
|
||||
} else {
|
||||
final FboState fbo = resourceManager.getFbo(cs.boundReadFboId);
|
||||
if (fbo == null) return;
|
||||
final int colorGlId = fbo.colorGlIds[fbo.readBufferIndex];
|
||||
texHandle = resourceManager.getTextureHandle(colorGlId);
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(colorGlId);
|
||||
srcSdlFormat = meta != null ? meta.sdlFormat() : SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
||||
srcWidth = meta != null ? meta.width() : fbo.width;
|
||||
srcHeight = meta != null ? meta.height() : fbo.height;
|
||||
srcY = y;
|
||||
if (frameManager.isFrameActive()) fboClearTracker.materializePendingClearForTexture(cs, texHandle);
|
||||
if (kind == READBACK_COLOR) {
|
||||
final int colorGlId = fbo.colorGlIds[fbo.readBufferIndex];
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(colorGlId);
|
||||
colorTex = resourceManager.getTextureHandle(colorGlId);
|
||||
colorFormat = meta != null ? meta.sdlFormat() : SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
||||
if (meta != null) colorBase = PixelPack.glBaseFormat(meta.glFormat());
|
||||
srcWidth = meta != null ? meta.width() : fbo.width;
|
||||
srcHeight = meta != null ? meta.height() : fbo.height;
|
||||
} else {
|
||||
depthTex = fbo.depthTexture;
|
||||
depthFormat = fbo.depthFormat;
|
||||
srcWidth = fbo.width;
|
||||
srcHeight = fbo.height;
|
||||
}
|
||||
}
|
||||
if (texHandle == 0 || srcWidth <= 0 || srcHeight <= 0) return;
|
||||
|
||||
if (srcWidth <= 0 || srcHeight <= 0) return;
|
||||
final int srcY = fromFbo0 ? srcHeight - y - height : y;
|
||||
final long clip = CopyRectClip.clipCopyRect(x, srcY, 0, 0, width, height, srcWidth, srcHeight, width, height);
|
||||
if (clip == CopyRectClip.EMPTY) return;
|
||||
final int sx = CopyRectClip.srcX(clip);
|
||||
final int sy = CopyRectClip.srcY(clip);
|
||||
final int w = CopyRectClip.width(clip);
|
||||
final int h = CopyRectClip.height(clip);
|
||||
final int colOffset = CopyRectClip.dstX(clip, x, 0);
|
||||
final int dy = CopyRectClip.dstY(clip, srcY, 0);
|
||||
final int rowOffset = fromFbo0 ? (height - h - dy) : dy;
|
||||
readback(cs, true, kind, colorTex, colorFormat, colorBase, depthTex, depthFormat, 0, srcWidth, srcHeight,
|
||||
CopyRectClip.srcX(clip), CopyRectClip.srcY(clip), w, h, fromFbo0,
|
||||
width, CopyRectClip.dstX(clip, x, 0), fromFbo0 ? height - h - dy : dy, format, type, dst, pbo, pboOffset);
|
||||
}
|
||||
|
||||
final int basePos = pixels.position();
|
||||
final int baseLimit = pixels.limit();
|
||||
final int stride = width * 4;
|
||||
if (baseLimit - basePos < height * stride) return;
|
||||
try {
|
||||
if (w == width) {
|
||||
final int start = basePos + rowOffset * stride;
|
||||
pixels.position(0);
|
||||
pixels.limit(start + h * stride);
|
||||
pixels.position(start);
|
||||
flushAndSubmitMidFrame();
|
||||
textureOps.readbackTexture(texHandle, sx, sy, w, h, 0, pixels);
|
||||
pixels.position(0);
|
||||
pixels.limit(baseLimit);
|
||||
pixels.position(start);
|
||||
PixelOps.postProcessReadback(pixels, width, h, format, srcSdlFormat, fromFbo0);
|
||||
} else {
|
||||
final ByteBuffer staging = MemoryUtil.memAlloc(w * h * 4);
|
||||
try {
|
||||
flushAndSubmitMidFrame();
|
||||
textureOps.readbackTexture(texHandle, sx, sy, w, h, 0, staging);
|
||||
staging.rewind();
|
||||
PixelOps.postProcessReadback(staging, w, h, format, srcSdlFormat, fromFbo0);
|
||||
for (int row = 0; row < h; row++) {
|
||||
staging.position(0);
|
||||
staging.limit((row + 1) * w * 4);
|
||||
staging.position(row * w * 4);
|
||||
pixels.position(basePos + (rowOffset + row) * stride + colOffset * 4);
|
||||
pixels.put(staging);
|
||||
}
|
||||
} finally {
|
||||
MemoryUtil.memFree(staging);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
pixels.limit(baseLimit);
|
||||
pixels.position(basePos);
|
||||
}
|
||||
}
|
||||
@Override public void readPixels(int x, int y, int width, int height, int format, int type, FloatBuffer pixels) {
|
||||
readPixels(x, y, width, height, format, type, MemoryUtil.memByteBuffer(pixels));
|
||||
}
|
||||
@Override public void readPixels(int x, int y, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
readPixels(x, y, width, height, format, type, MemoryUtil.memByteBuffer(pixels));
|
||||
}
|
||||
@Override public void getTexImage(int target, int level, int format, int type, ByteBuffer pixels) {
|
||||
private void getTexImageInto(int target, int level, int format, int type, long dst, long capacity, int pbo, long pboOffset) {
|
||||
PixelPack.validate(format, type);
|
||||
final ContextState cs = s();
|
||||
final int glId = boundTextureFor(cs, target);
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(glId);
|
||||
if (meta == null || level < 0 || level >= meta.levels()) return;
|
||||
final int width = PixelOps.mipLevelSize(meta.width(), level);
|
||||
final int height = PixelOps.mipLevelSize(meta.height(), level);
|
||||
if (width <= 0 || height <= 0) return;
|
||||
checkReadbackCapacity(format, type, width, height, loadPackState(cs), capacity);
|
||||
final int kind = readbackKind(format);
|
||||
final long texHandle = resourceManager.getTextureHandle(glId);
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(glId);
|
||||
if (meta == null) return;
|
||||
flushAndSubmitMidFrame();
|
||||
textureOps.readbackTexture(texHandle, 0, 0, meta.width(), meta.height(), level, pixels);
|
||||
pixels.rewind();
|
||||
PixelOps.postProcessReadback(pixels, meta.width(), meta.height(), format, meta.sdlFormat(), false);
|
||||
final int sdlFormat = meta.sdlFormat();
|
||||
if ((kind == READBACK_COLOR) == PixelOps.isDepthFormat(sdlFormat)) return;
|
||||
if (kind != READBACK_COLOR && level != 0) throw new UnsupportedOperationException("glGetTexImage of depth/stencil level " + level + " is not supported on SDL-GPU");
|
||||
readback(cs, false, kind, kind == READBACK_COLOR ? texHandle : 0L, sdlFormat, PixelPack.glBaseFormat(meta.glFormat()), kind == READBACK_COLOR ? 0L : texHandle, sdlFormat, level, width, height,
|
||||
0, 0, width, height, false, width, 0, 0, format, type, dst, pbo, pboOffset);
|
||||
}
|
||||
@Override public void getTexImage(int target, int level, int format, int type, IntBuffer pixels) {
|
||||
getTexImage(target, level, format, type, MemoryUtil.memByteBuffer(pixels));
|
||||
}
|
||||
@Override public void getTexImage(int target, int level, int format, int type, long pixelBufferOffset) {
|
||||
final ContextState cs = s();
|
||||
final int pbo = cs.boundPixelPackBuffer;
|
||||
if (pbo == 0) return;
|
||||
final int glId = boundTextureFor(cs, target);
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(glId);
|
||||
if (meta == null) return;
|
||||
final ByteBuffer tmp = MemoryUtil.memAlloc(meta.width() * meta.height() * 4);
|
||||
try {
|
||||
getTexImage(target, level, format, type, tmp);
|
||||
final long handle = resourceManager.getBufferHandle(pbo);
|
||||
if (handle != 0 && frameManager.getCommandBuffer() != 0) {
|
||||
tmp.rewind();
|
||||
resourceManager.uploadToBuffer(frameManager.ensureCopyPass(), tmp, handle, pixelBufferOffset, false);
|
||||
resourceManager.markBufferContentsDefined(pbo);
|
||||
|
||||
private void readback(ContextState cs, boolean readPixels, int kind, long colorTex, int colorFormat, int colorBase, long depthTex, int depthFormat, int level, int srcWidth, int srcHeight,
|
||||
int sx, int sy, int w, int h, boolean flipRows, int width, int colOffset, int rowOffset, int format, int type, long dst, int pbo, long pboOffset) {
|
||||
final boolean wantsStencil = kind == READBACK_STENCIL || kind == READBACK_DEPTH_STENCIL;
|
||||
if (kind == READBACK_COLOR ? colorTex == 0L : depthTex == 0L) return;
|
||||
if (wantsStencil && !PixelOps.isDepthStencilFormat(depthFormat)) return;
|
||||
|
||||
final int texelBytes = switch (kind) {
|
||||
case READBACK_COLOR -> PixelOps.sdlFormatTexelBytes(colorFormat);
|
||||
case READBACK_STENCIL -> 1;
|
||||
default -> 4;
|
||||
};
|
||||
final int primaryBytes = w * h * texelBytes;
|
||||
final int stencilOffset = alignReadback(primaryBytes);
|
||||
final int downloadEnd = kind == READBACK_DEPTH_STENCIL ? stencilOffset + alignReadback(w * h) : stencilOffset;
|
||||
|
||||
final PackState ps = cs.packState;
|
||||
final ContextState.PixelStoreState store = cs.pixelStore;
|
||||
final int pixelBytes = PixelOps.glPixelSize(format, type);
|
||||
ps.alignment = store.packAlignment;
|
||||
ps.rowLength = store.packRowLength > 0 ? store.packRowLength : width;
|
||||
ps.skipPixels = store.packSkipPixels + colOffset;
|
||||
ps.skipRows = store.packSkipRows + rowOffset;
|
||||
final long stride = PixelPack.rowStride(format, type, width, ps);
|
||||
final long firstByte = ps.skipRows * stride + (long) ps.skipPixels * pixelBytes;
|
||||
final long endByte = (long) (ps.skipRows + h - 1) * stride + (long) (ps.skipPixels + w) * pixelBytes;
|
||||
|
||||
final long base = textureOps.ensureReadbackStaging(pbo != 0 ? downloadEnd + endByte : downloadEnd);
|
||||
final long packDst = pbo != 0 ? base + downloadEnd : dst;
|
||||
switch (kind) {
|
||||
case READBACK_COLOR -> {
|
||||
fboClearTracker.materializePendingClearForRead(cs, frameManager.frame(), colorTex);
|
||||
flushAndSubmitMidFrame();
|
||||
textureOps.readbackTextureToStaging(colorTex, sx, sy, w, h, level, 0, primaryBytes);
|
||||
PixelPack.packColor(base, colorFormat, colorBase, w * texelBytes, w, h, flipRows, readPixels, format, type, ps, packDst);
|
||||
}
|
||||
case READBACK_DEPTH -> {
|
||||
final long depth = requireReadbackScratch(depthStencilReadback.renderDepth(cs, depthTex, depthFormat, srcWidth, srcHeight), "depth");
|
||||
flushAndSubmitMidFrame();
|
||||
textureOps.readbackTextureToStaging(depth, sx, sy, w, h, 0, 0, primaryBytes);
|
||||
PixelPack.packDepth(base, w * 4, w, h, flipRows, type, ps, packDst);
|
||||
}
|
||||
case READBACK_STENCIL -> {
|
||||
final long stencil = requireReadbackScratch(depthStencilReadback.renderStencil(cs, depthTex, depthFormat, srcWidth, srcHeight), "stencil");
|
||||
flushAndSubmitMidFrame();
|
||||
textureOps.readbackTextureToStaging(stencil, sx, sy, w, h, 0, 0, primaryBytes);
|
||||
PixelPack.packStencil(base, w, w, h, flipRows, type, ps, packDst);
|
||||
}
|
||||
default -> {
|
||||
final long depth = requireReadbackScratch(depthStencilReadback.renderDepth(cs, depthTex, depthFormat, srcWidth, srcHeight), "depth");
|
||||
final long stencil = requireReadbackScratch(depthStencilReadback.renderStencil(cs, depthTex, depthFormat, srcWidth, srcHeight), "stencil");
|
||||
flushAndSubmitMidFrame();
|
||||
textureOps.readbackTextureToStaging(depth, sx, sy, w, h, 0, 0, primaryBytes);
|
||||
textureOps.readbackTextureToStaging(stencil, sx, sy, w, h, 0, stencilOffset, w * h);
|
||||
PixelPack.packDepthStencil(base, base + stencilOffset, w * 4, w, w, h, flipRows, type, ps, packDst);
|
||||
}
|
||||
} finally {
|
||||
MemoryUtil.memFree(tmp);
|
||||
}
|
||||
if (pbo != 0) uploadReadbackToPixelPackBuffer(pbo, pboOffset, (int) (downloadEnd + firstByte), (int) (endByte - firstByte), firstByte);
|
||||
}
|
||||
|
||||
private static long requireReadbackScratch(long scratch, String what) {
|
||||
if (scratch == 0L) throw new IllegalStateException("SDL-GPU " + what + " readback pass unavailable (no active frame, or the attachment cannot be sampled)");
|
||||
return scratch;
|
||||
}
|
||||
|
||||
private static int alignReadback(int bytes) {
|
||||
return (bytes + 15) & ~15;
|
||||
}
|
||||
|
||||
private void uploadReadbackToPixelPackBuffer(int pbo, long pboOffset, int stagingOffset, int size, long firstByte) {
|
||||
final long handle = resourceManager.getBufferHandle(pbo);
|
||||
if (handle == 0 || frameManager.getCommandBuffer() == 0) return;
|
||||
final ByteBuffer region = textureOps.readbackStagingRegion(stagingOffset, size);
|
||||
try {
|
||||
resourceManager.uploadToBuffer(frameManager.ensureCopyPass(), region, handle, pboOffset + firstByte, false);
|
||||
} finally {
|
||||
region.clear();
|
||||
}
|
||||
resourceManager.markBufferContentsDefined(pbo);
|
||||
flushAndSubmitMidFrame();
|
||||
}
|
||||
|
||||
@Override public int getFramebufferAttachmentParameteri(int target, int attachment, int pname) {
|
||||
@@ -2161,6 +2282,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
final ShaderManager.ProgramObject prog = shaderManager.getProgram(program);
|
||||
if (prog == null) {
|
||||
cs.pipeline.invalidateShaderHandles();
|
||||
cs.appliedLogicOpKey = 0;
|
||||
cs.activeLogicOpVariant = null;
|
||||
return;
|
||||
}
|
||||
if (prog.sdlVertexShader == 0 && prog.vertexSpirv != null) {
|
||||
@@ -2178,6 +2301,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
cs.pipeline.setVertexInputs(prog.vertexInputMask, prog.vertexInputVecSize, prog.vertexInputBaseType, prog.vertexInputName, prog.vertexInputSetId);
|
||||
cs.pipeline.markInputDirty();
|
||||
cs.pipeline.markShaderDirty();
|
||||
cs.appliedLogicOpKey = 0;
|
||||
cs.activeLogicOpVariant = null;
|
||||
}
|
||||
@Override public void useProgram(int program) {
|
||||
final ContextState cs = s();
|
||||
@@ -2193,6 +2318,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
}
|
||||
st.boundProgram = program;
|
||||
st.appliedLogicOpKey = 0;
|
||||
st.activeLogicOpVariant = null;
|
||||
if (program == 0) {
|
||||
st.boundProgramObj = null;
|
||||
cs.pipeline.vertexShader = 0;
|
||||
@@ -3618,7 +3745,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
final int flipMode = (srcIsFbo0 != dstIsFbo0) ? SDL_FLIP_VERTICAL : SDL_FLIP_NONE;
|
||||
|
||||
if (blitColor) {
|
||||
resolveBlitClears(cs, colorSrcTex, colorDstTex, colorDstGlId, dstX0, dstY, dstW, dstH);
|
||||
resolveBlitClears(cs, colorSrcTex, colorDstTex, colorDstGlId, dstX0, dstY, dstW, dstH, true);
|
||||
final ResourceManager.TextureMeta srcMeta = resourceManager.getTextureMeta(colorSrcGlId);
|
||||
final ResourceManager.TextureMeta dstMeta = resourceManager.getTextureMeta(colorDstGlId);
|
||||
if (flipMode == SDL_FLIP_NONE && TextureOps.canCopyInsteadOfBlit(srcMeta, dstMeta, srcW, srcH, dstW, dstH)) {
|
||||
@@ -3628,14 +3755,15 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
}
|
||||
if (blitDepth) {
|
||||
resolveBlitClears(cs, depthSrcTex, depthDstTex, dstFbo.depthGlId, dstX0, dstY, dstW, dstH);
|
||||
final boolean allAspects = !PixelOps.isDepthStencilFormat(dstFbo.depthFormat) || (mask & (GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT)) == (GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
|
||||
resolveBlitClears(cs, depthSrcTex, depthDstTex, dstFbo.depthGlId, dstX0, dstY, dstW, dstH, allAspects);
|
||||
textureOps.copyTexture(depthSrcTex, srcX0, srcY, depthDstTex, 0, dstX0, dstY, srcW, srcH);
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveBlitClears(ContextState cs, long srcTex, long dstTex, int dstGlId, int dstX, int dstY, int dstW, int dstH) {
|
||||
private void resolveBlitClears(ContextState cs, long srcTex, long dstTex, int dstGlId, int dstX, int dstY, int dstW, int dstH, boolean allAspects) {
|
||||
fboClearTracker.materializePendingClearForTexture(cs, srcTex);
|
||||
fboClearTracker.resolveDestinationForWrite(cs, dstTex, resourceManager.getTextureMeta(dstGlId), 0, dstX, dstY, 0, dstW, dstH);
|
||||
fboClearTracker.resolveDestinationForWrite(cs, dstTex, resourceManager.getTextureMeta(dstGlId), 0, dstX, dstY, 0, dstW, dstH, allAspects);
|
||||
}
|
||||
@Override public int createBuffers() { return resourceManager.genBuffer(); }
|
||||
@Override public void namedBufferData(int buffer, long size, int usage) {
|
||||
@@ -3752,6 +3880,11 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
case GL11.GL_TEXTURE_BINDING_2D -> boundTextureOf(cs.activeTextureUnit, cs.boundTextures);
|
||||
case GL11.GL_DEPTH_BITS -> getFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL11.GL_DEPTH, GL30.GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE);
|
||||
case GL11.GL_STENCIL_BITS -> getFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL11.GL_STENCIL, GL30.GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE);
|
||||
case GL11.GL_PACK_ALIGNMENT -> cs.pixelStore.packAlignment;
|
||||
case GL11.GL_PACK_ROW_LENGTH -> cs.pixelStore.packRowLength;
|
||||
case GL11.GL_PACK_SKIP_PIXELS -> cs.pixelStore.packSkipPixels;
|
||||
case GL11.GL_PACK_SKIP_ROWS -> cs.pixelStore.packSkipRows;
|
||||
case GL11.GL_PACK_SWAP_BYTES -> cs.pixelStore.packSwapBytes ? GL11.GL_TRUE : GL11.GL_FALSE;
|
||||
default -> {
|
||||
final int limit = deviceLimit(pname);
|
||||
yield limit >= 0 ? limit : unknownGetInteger(pname);
|
||||
@@ -3854,6 +3987,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
case GL11.GL_STENCIL_TEST -> cs.pipeline.stencilTestEnabled;
|
||||
case GL11.GL_DEPTH_WRITEMASK -> cs.pipeline.depthWriteEnabled;
|
||||
case GL30.GL_RASTERIZER_DISCARD -> cs.rasterizerDiscard;
|
||||
case GL11.GL_COLOR_LOGIC_OP -> cs.logicOpEnabled;
|
||||
case GL11.GL_PACK_SWAP_BYTES -> cs.pixelStore.packSwapBytes;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
@@ -3991,7 +4126,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
if (srcTex == 0 || dstTex == 0) return;
|
||||
final ContextState copySt = s();
|
||||
fboClearTracker.materializePendingClearForTexture(copySt, srcTex);
|
||||
fboClearTracker.resolveDestinationForWrite(copySt, dstTex, resourceManager.getTextureMeta(dstName), dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight);
|
||||
fboClearTracker.resolveDestinationForWrite(copySt, dstTex, resourceManager.getTextureMeta(dstName), dstLevel, dstX, dstY, dstZ, srcWidth, srcHeight, true);
|
||||
final long cp = frameManager.ensureCopyPass();
|
||||
if (cp == 0) return;
|
||||
try (var stack = MemoryStack.stackPush()) {
|
||||
@@ -4135,7 +4270,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
if (st.rasterizerDiscard) return;
|
||||
if (!frameManager.isFrameActive()) return;
|
||||
drawDispatch.setPrimitiveTypeForDraw(st, FormatMap.mapPrimitiveType(mode));
|
||||
pipelineApplier.ensureRenderPass(st);
|
||||
pipelineApplier.ensureDrawRenderPass(st);
|
||||
if (!frameManager.isRenderPassActive()) return;
|
||||
if (!pipelineApplier.applyPipelineAndState(st)) return;
|
||||
if (SystemProperties.FFP_TRACE) ffpTrace.trace(st, "arraysInstanced", mode, count, 0, first, 0);
|
||||
|
||||
@@ -3,6 +3,8 @@ 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;
|
||||
@@ -13,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;
|
||||
@@ -49,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;
|
||||
@@ -126,6 +134,20 @@ 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 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;
|
||||
@@ -305,6 +327,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);
|
||||
@@ -332,9 +355,6 @@ public final class ContextState {
|
||||
public int defaultFboId;
|
||||
public GLContextState mirroredContext;
|
||||
|
||||
public final LongOpenHashSet clearedTexturesThisFrame = new LongOpenHashSet();
|
||||
public final LongOpenHashSet clearedStencilTexturesThisFrame = new LongOpenHashSet();
|
||||
|
||||
public final LongOpenHashSet pendingColorTextures = new LongOpenHashSet();
|
||||
public final Long2ObjectOpenHashMap<float[]> pendingColorValues = new Long2ObjectOpenHashMap<>();
|
||||
public final LongOpenHashSet pendingDepthTextures = new LongOpenHashSet();
|
||||
@@ -419,6 +439,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();
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
+9
-118
@@ -7,21 +7,9 @@ import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager.FrameState;
|
||||
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;
|
||||
|
||||
@@ -34,9 +22,6 @@ 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;
|
||||
@@ -58,13 +43,11 @@ public final class AttachmentClear {
|
||||
|
||||
private final FrameManager frameManager;
|
||||
private final PipelineStore pipelineStore;
|
||||
private final ShaderManager shaderManager;
|
||||
|
||||
private final Target fboTarget = new Target();
|
||||
private final Target fbo0Target = new Target();
|
||||
|
||||
private int vertexShaderId;
|
||||
private long sdlVertexShader;
|
||||
private final FullscreenPass fullscreen;
|
||||
private final long[] sdlFragmentShaders = new long[MAX_COLOR_TARGETS + 1];
|
||||
private final boolean[] fragmentFailed = new boolean[MAX_COLOR_TARGETS + 1];
|
||||
|
||||
@@ -85,7 +68,7 @@ public final class AttachmentClear {
|
||||
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 {
|
||||
@@ -160,21 +143,8 @@ public final class AttachmentClear {
|
||||
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, t.width);
|
||||
MemoryAccess.putFloat(vpAddr + SDL_GPUViewport.H, t.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);
|
||||
@@ -194,16 +164,7 @@ public final class AttachmentClear {
|
||||
|
||||
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 (t.depthTexture != 0) {
|
||||
if (depth) st.clearedTexturesThisFrame.add(t.depthTexture);
|
||||
if (stencil) st.clearedStencilTexturesThisFrame.add(t.depthTexture);
|
||||
}
|
||||
FullscreenPass.markStateClobbered(st);
|
||||
|
||||
frameManager.noteInPassClear();
|
||||
return true;
|
||||
@@ -258,7 +219,6 @@ public final class AttachmentClear {
|
||||
.enable_color_write_mask(true);
|
||||
}
|
||||
|
||||
final boolean hasDepthTarget = t.depthFormat != 0;
|
||||
final SDL_GPUDepthStencilState depthStencil = SDL_GPUDepthStencilState.calloc(stack)
|
||||
.enable_depth_test(buildDepthFlag)
|
||||
.enable_depth_write(buildDepthFlag)
|
||||
@@ -279,38 +239,7 @@ 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(t.depthFormat)
|
||||
.has_depth_stencil_target(hasDepthTarget);
|
||||
|
||||
final SDL_GPUVertexInputState vertexInput = SDL_GPUVertexInputState.calloc(stack);
|
||||
|
||||
final SDL_GPUGraphicsPipelineCreateInfo ci = SDL_GPUGraphicsPipelineCreateInfo.calloc(stack)
|
||||
.vertex_shader(sdlVertexShader)
|
||||
.fragment_shader(sdlFragmentShaders[buildColorTargets])
|
||||
.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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,47 +249,10 @@ public final class AttachmentClear {
|
||||
if (fragmentFailed[colorTargets]) return false;
|
||||
fragmentFailed[colorTargets] = true;
|
||||
|
||||
if (vertexShaderId == 0) {
|
||||
vertexShaderId = shaderManager.createShader(GL20.GL_VERTEX_SHADER);
|
||||
shaderManager.shaderSource(vertexShaderId, ShaderLoader.getShaderSource(VERTEX_SOURCE));
|
||||
shaderManager.compileShader(vertexShaderId);
|
||||
}
|
||||
|
||||
final String baseSource = ShaderLoader.getShaderSource(FRAGMENT_SOURCE);
|
||||
final String fragmentSource = colorTargets == 0 ? baseSource : withColorOutputs(baseSource, colorTargets);
|
||||
|
||||
final int fs = shaderManager.createShader(GL20.GL_FRAGMENT_SHADER);
|
||||
shaderManager.shaderSource(fs, fragmentSource);
|
||||
shaderManager.compileShader(fs);
|
||||
|
||||
final int prog = shaderManager.createProgram();
|
||||
shaderManager.attachShader(prog, vertexShaderId);
|
||||
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 (COLOR_TARGETS={}) failed to link: {}", colorTargets, po == null ? "no program object" : po.infoLog);
|
||||
return false;
|
||||
}
|
||||
if (colorTargets > 0 && po.fragmentUboSize != 16) {
|
||||
LOG.error("AttachmentClear: clear_attachment fragment UBO size {} != 16 for COLOR_TARGETS={}", po.fragmentUboSize, colorTargets);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sdlVertexShader == 0) {
|
||||
sdlVertexShader = shaderManager.createSDLShader(po.vertexSpirv, SDL_GPU_SHADERSTAGE_VERTEX, po.vertexResources.numSamplers(), po.vertexResources.numUBOs(), po.vertexResources.numStorageBuffers(), po.vertexResources.numStorageTextures());
|
||||
if (sdlVertexShader == 0) {
|
||||
LOG.error("AttachmentClear: clear_attachment vertex SDL shader creation failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
final long fragmentShader = shaderManager.createSDLShader(po.fragmentSpirv, SDL_GPU_SHADERSTAGE_FRAGMENT, po.fragmentResources.numSamplers(), po.fragmentResources.numUBOs(), po.fragmentResources.numStorageBuffers(), po.fragmentResources.numStorageTextures());
|
||||
if (fragmentShader == 0) {
|
||||
LOG.error("AttachmentClear: clear_attachment fragment SDL shader creation failed (COLOR_TARGETS={})", colorTargets);
|
||||
return false;
|
||||
}
|
||||
final long fragmentShader = fullscreen.compileFragment(fragmentSource, colorTargets > 0 ? 16 : -1, "AttachmentClear: clear_attachment COLOR_TARGETS=" + colorTargets);
|
||||
if (fragmentShader == 0) return false;
|
||||
|
||||
sdlFragmentShaders[colorTargets] = fragmentShader;
|
||||
fragmentFailed[colorTargets] = false;
|
||||
@@ -382,8 +274,7 @@ public final class AttachmentClear {
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
vertexShaderId = 0;
|
||||
sdlVertexShader = 0;
|
||||
fullscreen.shutdown();
|
||||
Arrays.fill(sdlFragmentShaders, 0L);
|
||||
Arrays.fill(fragmentFailed, false);
|
||||
Arrays.fill(cacheKeys, 0L);
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ public final class DrawDispatch {
|
||||
return false;
|
||||
}
|
||||
setPrimitiveTypeForDraw(st, FormatMap.mapPrimitiveType(mode));
|
||||
pipelineApplier.ensureRenderPass(st, f);
|
||||
pipelineApplier.ensureDrawRenderPass(st, f);
|
||||
if (!frameManager.isRenderPassActive(f)) {
|
||||
f.droppedDrawsThisFrame++;
|
||||
if (!prepareRpInactiveWarned) {
|
||||
@@ -180,7 +180,7 @@ public final class DrawDispatch {
|
||||
st.fanIndexBufferOffset += indexDataSize;
|
||||
|
||||
setPrimitiveTypeForDraw(st, SDL_GPU_PRIMITIVETYPE_TRIANGLELIST);
|
||||
pipelineApplier.ensureRenderPass(st);
|
||||
pipelineApplier.ensureDrawRenderPass(st);
|
||||
if (!frameManager.isRenderPassActive()) return false;
|
||||
if (!pipelineApplier.applyPipelineAndState(st)) return false;
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.pipeline;
|
||||
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
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 static org.lwjgl.sdl.SDLGPU.*;
|
||||
|
||||
final class FullscreenPass {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger("Angelica-SDLGPU");
|
||||
|
||||
private static final String VERTEX_SOURCE = "angelica:sdlgpu/clear_attachment.vsh";
|
||||
|
||||
private final ShaderManager shaderManager;
|
||||
private int vertexShaderId;
|
||||
private long sdlVertexShader;
|
||||
|
||||
FullscreenPass(ShaderManager shaderManager) {
|
||||
this.shaderManager = shaderManager;
|
||||
}
|
||||
|
||||
long vertexShader() {
|
||||
return sdlVertexShader;
|
||||
}
|
||||
|
||||
long compileFragment(String fragmentSource, int requiredUboSize, String label) {
|
||||
if (vertexShaderId == 0) {
|
||||
vertexShaderId = shaderManager.createShader(GL20.GL_VERTEX_SHADER);
|
||||
shaderManager.shaderSource(vertexShaderId, ShaderLoader.getShaderSource(VERTEX_SOURCE));
|
||||
shaderManager.compileShader(vertexShaderId);
|
||||
}
|
||||
|
||||
final int fs = shaderManager.createShader(GL20.GL_FRAGMENT_SHADER);
|
||||
shaderManager.shaderSource(fs, fragmentSource);
|
||||
shaderManager.compileShader(fs);
|
||||
|
||||
final int prog = shaderManager.createProgram();
|
||||
shaderManager.attachShader(prog, vertexShaderId);
|
||||
shaderManager.attachShader(prog, fs);
|
||||
shaderManager.linkProgram(prog);
|
||||
|
||||
final ShaderManager.ProgramObject po = shaderManager.getProgram(prog);
|
||||
if (po == null || !po.linked) {
|
||||
LOG.error("{}: program failed to link: {}", label, po == null ? "no program object" : po.infoLog);
|
||||
return 0;
|
||||
}
|
||||
if (requiredUboSize >= 0 && po.fragmentUboSize != requiredUboSize) {
|
||||
LOG.error("{}: fragment UBO size {} != {}", label, po.fragmentUboSize, requiredUboSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sdlVertexShader == 0) {
|
||||
sdlVertexShader = shaderManager.createSDLShader(po.vertexSpirv, SDL_GPU_SHADERSTAGE_VERTEX, po.vertexResources.numSamplers(), po.vertexResources.numUBOs(), po.vertexResources.numStorageBuffers(), po.vertexResources.numStorageTextures());
|
||||
if (sdlVertexShader == 0) {
|
||||
LOG.error("{}: vertex SDL shader creation failed", label);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
final long fragmentShader = shaderManager.createSDLShader(po.fragmentSpirv, SDL_GPU_SHADERSTAGE_FRAGMENT, po.fragmentResources.numSamplers(), po.fragmentResources.numUBOs(), po.fragmentResources.numStorageBuffers(), po.fragmentResources.numStorageTextures());
|
||||
if (fragmentShader == 0) {
|
||||
LOG.error("{}: fragment SDL shader creation failed", label);
|
||||
return 0;
|
||||
}
|
||||
return fragmentShader;
|
||||
}
|
||||
|
||||
long createPipeline(PipelineStore pipelineStore, MemoryStack stack, long fragmentShader, SDL_GPUColorTargetDescription.Buffer colorDesc, int numColorTargets, SDL_GPUDepthStencilState depthStencil, int depthFormat, String label) {
|
||||
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(depthFormat)
|
||||
.has_depth_stencil_target(depthFormat != 0);
|
||||
|
||||
final SDL_GPUVertexInputState vertexInput = SDL_GPUVertexInputState.calloc(stack);
|
||||
|
||||
final SDL_GPUGraphicsPipelineCreateInfo ci = SDL_GPUGraphicsPipelineCreateInfo.calloc(stack)
|
||||
.vertex_shader(sdlVertexShader)
|
||||
.fragment_shader(fragmentShader)
|
||||
.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("{}: pipeline creation failed: {}", label, SDLError.SDL_GetError());
|
||||
return PipelineStore.BAD_PIPELINE_SENTINEL;
|
||||
}
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
static void setViewport(long renderPass, ContextState st, int width, int height, float depth) {
|
||||
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, width);
|
||||
MemoryAccess.putFloat(vpAddr + SDL_GPUViewport.H, height);
|
||||
MemoryAccess.putFloat(vpAddr + SDL_GPUViewport.MIN_DEPTH, depth);
|
||||
MemoryAccess.putFloat(vpAddr + SDL_GPUViewport.MAX_DEPTH, depth);
|
||||
SDL_SetGPUViewport(renderPass, st.cachedViewport);
|
||||
}
|
||||
|
||||
static void setScissor(long renderPass, ContextState st, int x, int y, int width, int height) {
|
||||
final long scAddr = st.cachedScissor.address();
|
||||
MemoryAccess.putInt(scAddr + SDL_Rect.X, x);
|
||||
MemoryAccess.putInt(scAddr + SDL_Rect.Y, y);
|
||||
MemoryAccess.putInt(scAddr + SDL_Rect.W, width);
|
||||
MemoryAccess.putInt(scAddr + SDL_Rect.H, height);
|
||||
SDL_SetGPUScissor(renderPass, st.cachedScissor);
|
||||
}
|
||||
|
||||
static void markStateClobbered(ContextState st) {
|
||||
st.lastBoundPipeline = 0;
|
||||
st.viewportDirty = true;
|
||||
st.scissorDirty = true;
|
||||
st.lastAppliedStencilRef = Integer.MIN_VALUE;
|
||||
st.lastAppliedVboBindCb = 0;
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
vertexShaderId = 0;
|
||||
sdlVertexShader = 0;
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
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.FboState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.TextureOps;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.LogicOpFormats;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.LogicOpVariant;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
|
||||
|
||||
public final class LogicOpEmulator {
|
||||
private final FrameManager frameManager;
|
||||
private final ResourceManager resourceManager;
|
||||
private final ShaderManager shaderManager;
|
||||
private final PipelineApplier pipelineApplier;
|
||||
|
||||
LogicOpEmulator(FrameManager frameManager, ResourceManager resourceManager, ShaderManager shaderManager, PipelineApplier pipelineApplier) {
|
||||
this.frameManager = frameManager;
|
||||
this.resourceManager = resourceManager;
|
||||
this.shaderManager = shaderManager;
|
||||
this.pipelineApplier = pipelineApplier;
|
||||
}
|
||||
|
||||
public void beforeDraw(ContextState st, FrameState f) {
|
||||
if (!st.logicOpEnabled && st.appliedLogicOpKey == 0) return;
|
||||
final FboState fbo = st.boundFboId != 0 ? resourceManager.getFbo(st.boundFboId) : null;
|
||||
final long key = st.logicOpEnabled ? computeKey(st, fbo) : 0L;
|
||||
if (key != 0L) copyDestinations(st, f, fbo, key);
|
||||
if (key != st.appliedLogicOpKey) applyVariant(st, key);
|
||||
}
|
||||
|
||||
private long computeKey(ContextState st, FboState fbo) {
|
||||
final int op = LogicOpFormats.mesaOp(st.logicOpMode);
|
||||
if (op == LogicOpFormats.OP_COPY || st.boundProgram == 0) return 0L;
|
||||
if (st.boundFboId != 0 && fbo == null) return 0L;
|
||||
final ShaderManager.ProgramObject prog = shaderManager.getProgram(st.boundProgram);
|
||||
if (prog == null || !prog.linked) return 0L;
|
||||
final int limit = Math.min(targetCount(fbo), prog.maxFragOutputLocation + 1);
|
||||
long key = op;
|
||||
boolean any = false;
|
||||
for (int loc = 0; loc < limit; loc++) {
|
||||
if (targetTexture(fbo, loc) == 0L) continue;
|
||||
final int cls = LogicOpFormats.classOf(targetFormat(fbo, loc));
|
||||
if (cls == 0) continue;
|
||||
key = LogicOpFormats.withClass(key, loc, cls);
|
||||
any = true;
|
||||
}
|
||||
return any ? key : 0L;
|
||||
}
|
||||
|
||||
private void copyDestinations(ContextState st, FrameState f, FboState fbo, long key) {
|
||||
pipelineApplier.ensureRenderPass(st, f);
|
||||
if (f.renderPass == 0) return;
|
||||
long cp = 0L;
|
||||
for (int loc = 0; loc < ContextState.MAX_COLOR_ATTACHMENTS; loc++) {
|
||||
if (LogicOpFormats.classAt(key, loc) == 0) continue;
|
||||
final long tex = targetTexture(fbo, loc);
|
||||
final int w = targetWidth(fbo, loc);
|
||||
final int h = targetHeight(fbo, loc);
|
||||
final int fmt = targetFormat(fbo, loc);
|
||||
long scratch = st.logicOpScratch[loc];
|
||||
if (scratch == 0L || st.logicOpScratchFormat[loc] != fmt || st.logicOpScratchWidth[loc] != w || st.logicOpScratchHeight[loc] != h) {
|
||||
if (scratch != 0L) resourceManager.releaseTextureDeferred(scratch);
|
||||
scratch = resourceManager.createLogicOpScratch(fmt, w, h);
|
||||
st.logicOpScratch[loc] = scratch;
|
||||
st.logicOpScratchFormat[loc] = fmt;
|
||||
st.logicOpScratchWidth[loc] = w;
|
||||
st.logicOpScratchHeight[loc] = h;
|
||||
st.samplerBindGen++;
|
||||
}
|
||||
if (cp == 0L) cp = frameManager.ensureCopyPass(FrameManager.PASS_END_LOGIC_OP);
|
||||
TextureOps.copyTexture(cp, tex, 0, 0, scratch, 0, 0, 0, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyVariant(ContextState st, long key) {
|
||||
if (key == 0L) {
|
||||
final ShaderManager.ProgramObject prog = shaderManager.getProgram(st.boundProgram);
|
||||
st.pipeline.fragmentShader = prog != null ? prog.sdlFragmentShader : 0L;
|
||||
st.activeLogicOpVariant = null;
|
||||
} else {
|
||||
final LogicOpVariant v = shaderManager.getOrBuildLogicOpVariant(st.boundProgram, key);
|
||||
st.pipeline.fragmentShader = v.sdlShader;
|
||||
st.activeLogicOpVariant = v;
|
||||
}
|
||||
st.pipeline.markShaderDirty();
|
||||
st.appliedLogicOpKey = key;
|
||||
st.samplerBindGen++;
|
||||
}
|
||||
|
||||
private static int targetCount(FboState fbo) {
|
||||
return fbo == null ? 1 : Math.min(fbo.drawBuffers.length, ContextState.MAX_COLOR_ATTACHMENTS);
|
||||
}
|
||||
|
||||
private static int drawBuffer(FboState fbo, int loc) {
|
||||
final int db = fbo.drawBuffers[loc];
|
||||
return db >= 0 && db < ContextState.MAX_COLOR_ATTACHMENTS && fbo.colorTextures[db] != 0L ? db : -1;
|
||||
}
|
||||
|
||||
private long targetTexture(FboState fbo, int loc) {
|
||||
if (fbo == null) return frameManager.getFbo0Texture();
|
||||
final int db = drawBuffer(fbo, loc);
|
||||
return db >= 0 ? fbo.colorTextures[db] : 0L;
|
||||
}
|
||||
|
||||
private int targetFormat(FboState fbo, int loc) {
|
||||
return fbo == null ? frameManager.getSwapchainFormat() : fbo.colorFormats[drawBuffer(fbo, loc)];
|
||||
}
|
||||
|
||||
private int targetWidth(FboState fbo, int loc) {
|
||||
if (fbo == null) return frameManager.getFbo0Width();
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(fbo.colorGlIds[drawBuffer(fbo, loc)]);
|
||||
return meta != null ? meta.width() : fbo.width;
|
||||
}
|
||||
|
||||
private int targetHeight(FboState fbo, int loc) {
|
||||
if (fbo == null) return frameManager.getFbo0Height();
|
||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(fbo.colorGlIds[drawBuffer(fbo, loc)]);
|
||||
return meta != null ? meta.height() : fbo.height;
|
||||
}
|
||||
}
|
||||
+16
-13
@@ -58,6 +58,7 @@ public final class PipelineApplier {
|
||||
private final SamplerBinder samplerBinder;
|
||||
private final StorageTextureBinder storageTextureBinder;
|
||||
private final StorageBufferBinder storageBufferBinder;
|
||||
private final LogicOpEmulator logicOpEmulator;
|
||||
|
||||
private DrawDispatch.FanUploadSink deferredUploadSink;
|
||||
public void setDeferredUploadSink(DrawDispatch.FanUploadSink sink) { this.deferredUploadSink = sink; }
|
||||
@@ -84,6 +85,16 @@ public final class PipelineApplier {
|
||||
this.samplerBinder = samplerBinder;
|
||||
this.storageTextureBinder = storageTextureBinder;
|
||||
this.storageBufferBinder = storageBufferBinder;
|
||||
this.logicOpEmulator = new LogicOpEmulator(frameManager, resourceManager, shaderManager, this);
|
||||
}
|
||||
|
||||
public void ensureDrawRenderPass(ContextState st) {
|
||||
ensureDrawRenderPass(st, frameManager.frame());
|
||||
}
|
||||
|
||||
public void ensureDrawRenderPass(ContextState st, FrameState f) {
|
||||
logicOpEmulator.beforeDraw(st, f);
|
||||
ensureRenderPass(st, f);
|
||||
}
|
||||
|
||||
public void ensureRenderPass(ContextState st) {
|
||||
@@ -160,16 +171,11 @@ public final class PipelineApplier {
|
||||
final boolean proposedStencilClear;
|
||||
final boolean depthHasStencil = fbo.depthTexture != 0 && PixelOps.isDepthStencilFormat(fbo.depthFormat);
|
||||
if (fbo.depthTexture != 0) {
|
||||
final boolean defined = resourceManager.isTextureContentDefined(fbo.depthTexture);
|
||||
final boolean pendingClear = st.pendingDepthTextures.contains(fbo.depthTexture);
|
||||
final boolean firstUse = !pendingClear && !st.clearedTexturesThisFrame.contains(fbo.depthTexture);
|
||||
proposedDepthClear = pendingClear || firstUse;
|
||||
proposedDepthClear = pendingClear || !defined;
|
||||
final boolean pendingStencil = depthHasStencil && st.pendingStencilTextures.contains(fbo.depthTexture);
|
||||
if (depthHasStencil) {
|
||||
final boolean firstStencilUse = !pendingStencil && !st.clearedStencilTexturesThisFrame.contains(fbo.depthTexture);
|
||||
proposedStencilClear = pendingStencil || firstStencilUse;
|
||||
} else {
|
||||
proposedStencilClear = false;
|
||||
}
|
||||
proposedStencilClear = depthHasStencil && (pendingStencil || !defined);
|
||||
layoutHash = Hashing.fmix64(layoutHash, fbo.depthTexture);
|
||||
if (proposedDepthClear) {
|
||||
layoutHash = foldClearDepth(layoutHash, pendingClear ? st.pendingDepthValues.get(fbo.depthTexture) : st.depthClearValue);
|
||||
@@ -274,11 +280,8 @@ public final class PipelineApplier {
|
||||
}
|
||||
if (fbo.depthTexture == 0) return;
|
||||
st.pendingDepthTextures.remove(fbo.depthTexture);
|
||||
if (depthClear) st.clearedTexturesThisFrame.add(fbo.depthTexture);
|
||||
if (depthHasStencil) {
|
||||
st.pendingStencilTextures.remove(fbo.depthTexture);
|
||||
if (stencilClear) st.clearedStencilTexturesThisFrame.add(fbo.depthTexture);
|
||||
}
|
||||
if (depthHasStencil) st.pendingStencilTextures.remove(fbo.depthTexture);
|
||||
if (depthClear || stencilClear) rm.markTextureContentDefined(fbo.depthTexture);
|
||||
}
|
||||
|
||||
static long foldClearColor(long h, float r, float g, float b, float a) {
|
||||
|
||||
@@ -61,6 +61,7 @@ public final class PipelineCache {
|
||||
public int primitiveType = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
|
||||
|
||||
public final boolean[] blendEnabledPerDrawBuffer = new boolean[ContextState.MAX_COLOR_ATTACHMENTS];
|
||||
public boolean logicOpEnabled;
|
||||
public final int[] srcColorFactors = new int[ContextState.MAX_COLOR_ATTACHMENTS];
|
||||
public final int[] dstColorFactors = new int[ContextState.MAX_COLOR_ATTACHMENTS];
|
||||
public final int[] srcAlphaFactors = new int[ContextState.MAX_COLOR_ATTACHMENTS];
|
||||
@@ -162,7 +163,7 @@ public final class PipelineCache {
|
||||
}
|
||||
|
||||
private boolean blendEnabledForSlot(int slot) {
|
||||
return slot >= 0 && slot < ContextState.MAX_COLOR_ATTACHMENTS && blendEnabledPerDrawBuffer[slot];
|
||||
return !logicOpEnabled && slot >= 0 && slot < ContextState.MAX_COLOR_ATTACHMENTS && blendEnabledPerDrawBuffer[slot];
|
||||
}
|
||||
|
||||
public boolean setBlendFactors(int sc, int dc, int sa, int da) {
|
||||
@@ -259,7 +260,7 @@ public final class PipelineCache {
|
||||
|
||||
private boolean computeUsesBlendConstants() {
|
||||
for (int i = 0; i < ContextState.MAX_COLOR_ATTACHMENTS; i++) {
|
||||
if (!blendEnabledPerDrawBuffer[i]) continue;
|
||||
if (!blendEnabledForSlot(i)) continue;
|
||||
if (isBlendConstantFactor(srcColorFactors[i]) || isBlendConstantFactor(dstColorFactors[i])
|
||||
|| isBlendConstantFactor(srcAlphaFactors[i]) || isBlendConstantFactor(dstAlphaFactors[i])) {
|
||||
return true;
|
||||
|
||||
+42
-25
@@ -38,7 +38,6 @@ public final class FBOClearTracker {
|
||||
if (st.pendingColorTextures.add(tex)) {
|
||||
st.pendingMutationGen++;
|
||||
}
|
||||
st.clearedTexturesThisFrame.remove(tex);
|
||||
}
|
||||
|
||||
public static void recordPendingDepthClear(ContextState st, long tex, float value) {
|
||||
@@ -46,7 +45,6 @@ public final class FBOClearTracker {
|
||||
if (st.pendingDepthTextures.add(tex)) {
|
||||
st.pendingMutationGen++;
|
||||
}
|
||||
st.clearedTexturesThisFrame.remove(tex);
|
||||
}
|
||||
|
||||
public static void recordPendingStencilClear(ContextState st, long tex, int value) {
|
||||
@@ -54,7 +52,6 @@ public final class FBOClearTracker {
|
||||
if (st.pendingStencilTextures.add(tex)) {
|
||||
st.pendingMutationGen++;
|
||||
}
|
||||
st.clearedStencilTexturesThisFrame.remove(tex);
|
||||
}
|
||||
|
||||
public static boolean fboHasPendingClear(ContextState st, FboState fbo) {
|
||||
@@ -179,7 +176,6 @@ public final class FBOClearTracker {
|
||||
MemoryAccess.putFloat(ccAddr + SDL_FColor.G, color[1]);
|
||||
MemoryAccess.putFloat(ccAddr + SDL_FColor.B, color[2]);
|
||||
MemoryAccess.putFloat(ccAddr + SDL_FColor.A, color[3]);
|
||||
st.clearedTexturesThisFrame.add(handle);
|
||||
resourceManager.markTextureContentDefined(handle);
|
||||
consumed[idx] = true;
|
||||
}
|
||||
@@ -217,35 +213,46 @@ public final class FBOClearTracker {
|
||||
frameManager.beginRenderPass(null, dt);
|
||||
frameManager.endRenderPassIfActive(FrameManager.PASS_END_CLEAR);
|
||||
}
|
||||
if (clearDepth) {
|
||||
st.pendingDepthValues.remove(handle);
|
||||
st.clearedTexturesThisFrame.add(handle);
|
||||
}
|
||||
if (clearStencil) {
|
||||
st.pendingStencilValues.remove(handle);
|
||||
st.clearedStencilTexturesThisFrame.add(handle);
|
||||
}
|
||||
if (clearDepth) st.pendingDepthValues.remove(handle);
|
||||
if (clearStencil) st.pendingStencilValues.remove(handle);
|
||||
if (clearDepth && clearStencil) resourceManager.markTextureContentDefined(handle);
|
||||
}
|
||||
|
||||
public boolean discardPendingClearIfFullyCovered(ContextState st, long handle, int dstX, int dstY, int dstLevel, int width, int height, ResourceManager.TextureMeta meta) {
|
||||
public boolean discardPendingClearIfFullyCovered(ContextState st, long handle, int dstX, int dstY, int dstLevel, int width, int height, ResourceManager.TextureMeta meta, boolean allAspects) {
|
||||
if (handle == 0 || meta == null) return false;
|
||||
if (dstLevel != 0 || dstX != 0 || dstY != 0) return false;
|
||||
if (width != meta.width() || height != meta.height() || meta.depth() > 1) return false;
|
||||
final boolean depth = st.pendingDepthTextures.remove(handle);
|
||||
final boolean color = !depth && st.pendingColorTextures.remove(handle);
|
||||
if (!depth && !color) return false;
|
||||
if (depth) st.pendingDepthValues.remove(handle);
|
||||
else st.pendingColorValues.remove(handle);
|
||||
st.clearedTexturesThisFrame.add(handle);
|
||||
resourceManager.markTextureContentDefined(handle);
|
||||
if (st.pendingDepthTextures.contains(handle) || st.pendingStencilTextures.contains(handle)) {
|
||||
if (!allAspects) return false;
|
||||
st.pendingDepthTextures.remove(handle);
|
||||
st.pendingDepthValues.remove(handle);
|
||||
st.pendingStencilTextures.remove(handle);
|
||||
st.pendingStencilValues.remove(handle);
|
||||
} else if (st.pendingColorTextures.remove(handle)) {
|
||||
st.pendingColorValues.remove(handle);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
st.pendingMutationGen++;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void resolveDestinationForWrite(ContextState st, long destTex, ResourceManager.TextureMeta destMeta, int level, int dx, int dy, int dz, int w, int h) {
|
||||
if (dz != 0 || !discardPendingClearIfFullyCovered(st, destTex, dx, dy, level, w, h, destMeta)) {
|
||||
public void resolveDestinationForWrite(ContextState st, long destTex, ResourceManager.TextureMeta destMeta, int level, int dx, int dy, int dz, int w, int h, boolean allAspects) {
|
||||
if (dz != 0 || !discardPendingClearIfFullyCovered(st, destTex, dx, dy, level, w, h, destMeta, allAspects)) {
|
||||
materializePendingClearForTexture(st, destTex);
|
||||
}
|
||||
resourceManager.markTextureContentDefined(destTex);
|
||||
}
|
||||
|
||||
public void materializePendingClearForRead(ContextState st, FrameManager.FrameState f, long texture) {
|
||||
if (texture == 0L || !f.frameActive) return;
|
||||
if (texture == frameManager.getFbo0Texture()) {
|
||||
if (st.pendingSwapchainClear || !f.clearedThisFrame) frameManager.ensureFbo0RenderPass(f, st);
|
||||
} else if (texture == resourceManager.getSwapchainDepthStencil()) {
|
||||
if (st.pendingSwapchainDepthClear || st.pendingSwapchainStencilClear || !f.depthClearedThisFrame) frameManager.ensureFbo0RenderPass(f, st);
|
||||
} else {
|
||||
materializePendingClearForTexture(st, texture);
|
||||
}
|
||||
}
|
||||
|
||||
public void materializePendingClearForTexture(ContextState st, long handle) {
|
||||
@@ -276,13 +283,25 @@ public final class FBOClearTracker {
|
||||
frameManager.noteMaterializedClearPass();
|
||||
frameManager.beginRenderPass(targets, null);
|
||||
frameManager.endRenderPassIfActive(FrameManager.PASS_END_CLEAR);
|
||||
st.clearedTexturesThisFrame.add(handle);
|
||||
resourceManager.markTextureContentDefined(handle);
|
||||
}
|
||||
st.pendingMutationGen++;
|
||||
}
|
||||
}
|
||||
|
||||
public void materializeAllPendingClears(ContextState st) {
|
||||
if (st.pendingColorTextures.isEmpty() && st.pendingDepthTextures.isEmpty() && st.pendingStencilTextures.isEmpty()) return;
|
||||
final LongArrayList handles = st.samplerFlushColorHandles;
|
||||
handles.clear();
|
||||
handles.addAll(st.pendingColorTextures);
|
||||
handles.addAll(st.pendingDepthTextures);
|
||||
handles.addAll(st.pendingStencilTextures);
|
||||
for (int i = 0; i < handles.size(); i++) {
|
||||
materializePendingClearForTexture(st, handles.getLong(i));
|
||||
}
|
||||
handles.clear();
|
||||
}
|
||||
|
||||
public void scrubPendingClearsForTexture(ContextState st, int glId) {
|
||||
final long handle = resourceManager.getTextureHandle(glId);
|
||||
if (handle == 0) return;
|
||||
@@ -292,8 +311,6 @@ public final class FBOClearTracker {
|
||||
st.pendingDepthValues.remove(handle);
|
||||
st.pendingStencilTextures.remove(handle);
|
||||
st.pendingStencilValues.remove(handle);
|
||||
st.clearedTexturesThisFrame.remove(handle);
|
||||
st.clearedStencilTexturesThisFrame.remove(handle);
|
||||
}
|
||||
|
||||
public boolean fbosHaveSameAttachments(ContextState st, int a, int b) {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.resource;
|
||||
|
||||
public final class PackState {
|
||||
public int alignment = 4;
|
||||
public int rowLength;
|
||||
public int skipPixels;
|
||||
public int skipRows;
|
||||
public boolean swapBytes;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.gtnewhorizons.angelica.glsm.GLTypes;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -131,15 +132,22 @@ public final class PixelOps {
|
||||
final int packed = GLTypes.packedTexelBytes(type);
|
||||
if (packed != 0) return packed;
|
||||
final int components = switch (format) {
|
||||
case GL11.GL_RED, GL11.GL_ALPHA, GL11.GL_LUMINANCE -> 1;
|
||||
case GL11.GL_LUMINANCE_ALPHA -> 2;
|
||||
case GL11.GL_RGB -> 3;
|
||||
case GL11.GL_RGBA, GL12.GL_BGRA -> 4;
|
||||
case GL11.GL_RED, GL11.GL_GREEN, GL11.GL_BLUE, GL11.GL_ALPHA, GL11.GL_LUMINANCE,
|
||||
GL30.GL_RED_INTEGER, GL30.GL_GREEN_INTEGER, GL30.GL_BLUE_INTEGER, GL30.GL_ALPHA_INTEGER,
|
||||
GL11.GL_DEPTH_COMPONENT, GL11.GL_STENCIL_INDEX -> 1;
|
||||
case GL11.GL_LUMINANCE_ALPHA, GL30.GL_RG, GL30.GL_RG_INTEGER -> 2;
|
||||
case GL11.GL_RGB, GL12.GL_BGR, GL30.GL_RGB_INTEGER, GL30.GL_BGR_INTEGER -> 3;
|
||||
default -> 4;
|
||||
};
|
||||
return components * GLTypes.sizeBytes(type);
|
||||
}
|
||||
|
||||
public static int alignedRowStride(int rowPixels, int pixelBytes, int alignment) {
|
||||
final int unaligned = rowPixels * pixelBytes;
|
||||
final int align = Math.max(1, alignment);
|
||||
return ((unaligned + align - 1) / align) * align;
|
||||
}
|
||||
|
||||
public static int sdlFormatTexelBytes(int sdlFormat) {
|
||||
return switch (sdlFormat) {
|
||||
case SDL_GPU_TEXTUREFORMAT_R8_UNORM, SDL_GPU_TEXTUREFORMAT_R8_SNORM,
|
||||
@@ -258,8 +266,9 @@ public final class PixelOps {
|
||||
pixels.position(pos);
|
||||
}
|
||||
|
||||
public static ByteBuffer prepareUploadBuffer(int format, int dstSdlFormat, ByteBuffer src, int width, int height) {
|
||||
public static ByteBuffer prepareUploadBuffer(int format, int type, int dstSdlFormat, ByteBuffer src, int width, int height) {
|
||||
if (src == null) return null;
|
||||
if (dstSdlFormat == SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM) return prepareRgb10A2Upload(format, type, src, width, height);
|
||||
final boolean storageBgra = isBgraSdlFormat(dstSdlFormat);
|
||||
final ByteBuffer expanded = expandToRGBA(format, src, width, height);
|
||||
if (expanded != null) {
|
||||
@@ -273,14 +282,37 @@ public final class PixelOps {
|
||||
return src;
|
||||
}
|
||||
|
||||
private static ByteBuffer prepareRgb10A2Upload(int format, int type, ByteBuffer src, int width, int height) {
|
||||
if (format == GL11.GL_RGBA && type == GL12.GL_UNSIGNED_INT_2_10_10_10_REV) return src;
|
||||
if ((format == GL11.GL_RGBA || format == GL12.GL_BGRA) && type == GL11.GL_UNSIGNED_BYTE) {
|
||||
final int pixelCount = width * height;
|
||||
final ByteBuffer dst = MemoryUtil.memAlloc(pixelCount * 4);
|
||||
final int srcPos = src.position();
|
||||
final int rOff = format == GL12.GL_BGRA ? 2 : 0;
|
||||
final int bOff = 2 - rOff;
|
||||
for (int i = 0; i < pixelCount; i++) {
|
||||
final int o = i * 4;
|
||||
final int r = src.get(srcPos + o + rOff) & 0xFF;
|
||||
final int g = src.get(srcPos + o + 1) & 0xFF;
|
||||
final int b = src.get(srcPos + o + bOff) & 0xFF;
|
||||
final int a = src.get(srcPos + o + 3) & 0xFF;
|
||||
dst.putInt(o, unorm8ToBits(r, 1023) | unorm8ToBits(g, 1023) << 10 | unorm8ToBits(b, 1023) << 20 | unorm8ToBits(a, 3) << 30);
|
||||
}
|
||||
dst.position(0).limit(pixelCount * 4);
|
||||
return dst;
|
||||
}
|
||||
throw new UnsupportedOperationException("GL_RGB10_A2 upload from format 0x" + Integer.toHexString(format) + " type 0x" + Integer.toHexString(type) + " is not supported on SDL-GPU");
|
||||
}
|
||||
|
||||
private static int unorm8ToBits(int v, int max) {
|
||||
return (v * max + 127) / 255;
|
||||
}
|
||||
|
||||
public static ByteBuffer applyUnpackPixelStore(ByteBuffer src, int width, int height, int format, int type, ContextState.PixelStoreState ps) {
|
||||
if (src == null || ps == null || ps.isDefault()) return src;
|
||||
final int pixelBytes = glPixelSize(format, type);
|
||||
if (pixelBytes <= 0) return src;
|
||||
final int rowPixels = ps.unpackRowLength > 0 ? ps.unpackRowLength : width;
|
||||
final int unalignedRowBytes = rowPixels * pixelBytes;
|
||||
final int align = Math.max(1, ps.unpackAlignment);
|
||||
final int srcStride = ((unalignedRowBytes + align - 1) / align) * align;
|
||||
final int srcStride = alignedRowStride(ps.unpackRowLength > 0 ? ps.unpackRowLength : width, pixelBytes, ps.unpackAlignment);
|
||||
final int dstStride = width * pixelBytes;
|
||||
final int totalDst = dstStride * height;
|
||||
final ByteBuffer dst = MemoryUtil.memAlloc(totalDst);
|
||||
@@ -296,31 +328,4 @@ public final class PixelOps {
|
||||
dst.position(0).limit(totalDst);
|
||||
return dst;
|
||||
}
|
||||
|
||||
public static void postProcessReadback(ByteBuffer pixels, int width, int height, int format, int srcSdlFormat, boolean flipRows) {
|
||||
final int rowBytes = width * 4;
|
||||
final int pos = pixels.position();
|
||||
|
||||
if (flipRows) {
|
||||
final byte[] rowA = new byte[rowBytes];
|
||||
final byte[] rowB = new byte[rowBytes];
|
||||
for (int top = 0, bot = height - 1; top < bot; top++, bot--) {
|
||||
pixels.position(pos + top * rowBytes);
|
||||
pixels.get(rowA);
|
||||
pixels.position(pos + bot * rowBytes);
|
||||
pixels.get(rowB);
|
||||
pixels.position(pos + top * rowBytes);
|
||||
pixels.put(rowB);
|
||||
pixels.position(pos + bot * rowBytes);
|
||||
pixels.put(rowA);
|
||||
}
|
||||
}
|
||||
|
||||
pixels.position(pos);
|
||||
final boolean storageBgra = isBgraSdlFormat(srcSdlFormat);
|
||||
final boolean requestBgra = (format == GL12.GL_BGRA);
|
||||
swapRedBlueIfNeeded(pixels, width, height, storageBgra != requestBgra);
|
||||
|
||||
pixels.position(pos);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,956 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.resource;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLTypes;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
import org.lwjgl.opengl.GL41;
|
||||
import org.lwjgl.opengl.ARBTextureFloat;
|
||||
import org.lwjgl.opengl.EXTTextureInteger;
|
||||
import org.lwjgl.opengl.EXTTextureSRGBR8;
|
||||
import org.lwjgl.opengl.EXTTextureSRGBRG8;
|
||||
import org.lwjgl.opengl.EXTTextureSnorm;
|
||||
import org.lwjgl.opengl.EXTTextureStorage;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static org.lwjgl.sdl.SDLGPU.*;
|
||||
|
||||
public final class PixelPack {
|
||||
private PixelPack() {}
|
||||
|
||||
private static final boolean LITTLE_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
|
||||
|
||||
private static final int K_UNORM = 0;
|
||||
private static final int K_SNORM = 1;
|
||||
private static final int K_FLOAT = 2;
|
||||
private static final int K_UINT = 3;
|
||||
private static final int K_SINT = 4;
|
||||
|
||||
private static final int CH_R = 0;
|
||||
private static final int CH_G = 1;
|
||||
private static final int CH_B = 2;
|
||||
private static final int CH_A = 3;
|
||||
private static final int CH_L = 4;
|
||||
private static final int CH_ZERO = 5;
|
||||
private static final int CH_ONE = 6;
|
||||
private static final int CH_L_RG = 7;
|
||||
private static final int REBASE_IDENTITY = CH_R | (CH_G << 4) | (CH_B << 8) | (CH_A << 12);
|
||||
|
||||
private static final int OK = 0;
|
||||
private static final int INVALID_ENUM = 1;
|
||||
private static final int INVALID_OPERATION = 2;
|
||||
|
||||
private static final float MAX_RGB9E5 = 65408.0f;
|
||||
|
||||
public static void validate(int format, int type) {
|
||||
final int err = checkFormatAndType(format, type);
|
||||
if (err == INVALID_ENUM) {
|
||||
throw new IllegalArgumentException("GL_INVALID_ENUM: pack format 0x" + Integer.toHexString(format) + " type 0x" + Integer.toHexString(type));
|
||||
}
|
||||
if (err == INVALID_OPERATION) {
|
||||
throw new IllegalArgumentException("GL_INVALID_OPERATION: pack format 0x" + Integer.toHexString(format) + " type 0x" + Integer.toHexString(type));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isDepthFormat(int glFormat) {
|
||||
return glFormat == GL11.GL_DEPTH_COMPONENT;
|
||||
}
|
||||
|
||||
public static boolean isStencilFormat(int glFormat) {
|
||||
return glFormat == GL11.GL_STENCIL_INDEX;
|
||||
}
|
||||
|
||||
public static boolean isDepthStencilFormat(int glFormat) {
|
||||
return glFormat == GL30.GL_DEPTH_STENCIL;
|
||||
}
|
||||
|
||||
public static int rowStride(int format, int type, int width, PackState ps) {
|
||||
return PixelOps.alignedRowStride(ps.rowLength > 0 ? ps.rowLength : width, PixelOps.glPixelSize(format, type), ps.alignment);
|
||||
}
|
||||
|
||||
public static long imageSize(int format, int type, int width, int height, PackState ps) {
|
||||
if (width <= 0 || height <= 0) return 0L;
|
||||
final long stride = rowStride(format, type, width, ps);
|
||||
final long pixelBytes = PixelOps.glPixelSize(format, type);
|
||||
return (ps.skipRows + height - 1L) * stride + (ps.skipPixels + (long) width) * pixelBytes;
|
||||
}
|
||||
|
||||
public static void packColor(long src, int srcSdlFormat, int srcBaseFormat, int srcRowBytes, int width, int height, boolean flipRows, boolean readPixels, int format, int type, PackState ps, long dst) {
|
||||
validate(format, type);
|
||||
if (!isColorFormat(format)) {
|
||||
throw new IllegalArgumentException("GL_INVALID_OPERATION: packColor with non-color format 0x" + Integer.toHexString(format));
|
||||
}
|
||||
final int kind = sourceKind(srcSdlFormat);
|
||||
final boolean srcInteger = kind == K_UINT || kind == K_SINT;
|
||||
final boolean dstInteger = isIntegerFormat(format);
|
||||
if (srcInteger != dstInteger) {
|
||||
throw new IllegalArgumentException("GL_INVALID_OPERATION: integer / non-integer mismatch, source 0x" + Integer.toHexString(srcSdlFormat) + " format 0x" + Integer.toHexString(format));
|
||||
}
|
||||
final int rebase = rebaseSwizzle(srcBaseFormat);
|
||||
if (width <= 0 || height <= 0) return;
|
||||
|
||||
final int texelBytes = PixelOps.sdlFormatTexelBytes(srcSdlFormat);
|
||||
final int pixelBytes = PixelOps.glPixelSize(format, type);
|
||||
final int stride = rowStride(format, type, width, ps);
|
||||
final long base = dst + (long) ps.skipRows * stride + (long) ps.skipPixels * pixelBytes;
|
||||
final int rowBytes = width * pixelBytes;
|
||||
final int swapSize = ps.swapBytes ? swapUnit(type) : 0;
|
||||
final boolean lumConversion = isLuminanceFormat(format)
|
||||
&& (srcBaseFormat == GL30.GL_RG || srcBaseFormat == GL11.GL_RGB || srcBaseFormat == GL11.GL_RGBA);
|
||||
final int n = formatComponents(format);
|
||||
final int requested = formatSwizzle(format, false);
|
||||
final boolean rebaseChanges = rebaseChangesRequested(requested, n, rebase);
|
||||
final boolean clamp = !dstInteger && clampNeeded(kind, readPixels, format, type, lumConversion);
|
||||
|
||||
if (!clamp && !lumConversion && !rebaseChanges && layoutMatches(srcSdlFormat, format, type)) {
|
||||
for (int j = 0; j < height; j++) {
|
||||
final long d = base + (long) j * stride;
|
||||
MemoryUtil.memCopy(src + (long) srcRow(j, height, flipRows) * srcRowBytes, d, rowBytes);
|
||||
if (swapSize != 0) swapRow(d, rowBytes, swapSize);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final int swizzle = applyRebase(formatSwizzle(format, lumConversion && readPixels), n, rebase, srcBaseFormat == GL30.GL_RG);
|
||||
|
||||
if (LITTLE_ENDIAN && !clamp && !lumConversion && !rebaseChanges && isUnorm8x4(srcSdlFormat) && byteShuffleType(format, type)) {
|
||||
final boolean reversed = type == GL12.GL_UNSIGNED_INT_8_8_8_8;
|
||||
final int o0 = shuffleOffset(srcSdlFormat, swizzle, n, reversed, 0);
|
||||
final int o1 = shuffleOffset(srcSdlFormat, swizzle, n, reversed, 1);
|
||||
final int o2 = shuffleOffset(srcSdlFormat, swizzle, n, reversed, 2);
|
||||
final int o3 = shuffleOffset(srcSdlFormat, swizzle, n, reversed, 3);
|
||||
for (int j = 0; j < height; j++) {
|
||||
final long s = src + (long) srcRow(j, height, flipRows) * srcRowBytes;
|
||||
final long d = base + (long) j * stride;
|
||||
for (int x = 0; x < width; x++) {
|
||||
final long t = s + ((long) x << 2);
|
||||
final long o = d + (long) x * n;
|
||||
MemoryUtil.memPutByte(o, MemoryUtil.memGetByte(t + o0));
|
||||
if (n > 1) MemoryUtil.memPutByte(o + 1, MemoryUtil.memGetByte(t + o1));
|
||||
if (n > 2) MemoryUtil.memPutByte(o + 2, MemoryUtil.memGetByte(t + o2));
|
||||
if (n > 3) MemoryUtil.memPutByte(o + 3, MemoryUtil.memGetByte(t + o3));
|
||||
}
|
||||
if (swapSize != 0) swapRow(d, rowBytes, swapSize);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean packed = GLTypes.packedTexelBytes(type) != 0;
|
||||
final int compBytes = packed ? 0 : GLTypes.sizeBytes(type);
|
||||
final boolean direct = (kind == K_UNORM || kind == K_SNORM) && !clamp && !lumConversion;
|
||||
for (int j = 0; j < height; j++) {
|
||||
final long s = src + (long) srcRow(j, height, flipRows) * srcRowBytes;
|
||||
final long d = base + (long) j * stride;
|
||||
for (int x = 0; x < width; x++) {
|
||||
final long t = s + (long) x * texelBytes;
|
||||
final long o = d + (long) x * pixelBytes;
|
||||
if (packed) {
|
||||
putSized(o, pixelBytes, packedTexel(t, srcSdlFormat, kind, swizzle, n, type, direct, clamp, dstInteger));
|
||||
} else if (dstInteger) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
putSized(o + (long) i * compBytes, compBytes, clampInteger(integerChannel(t, srcSdlFormat, (swizzle >> (i << 2)) & 15), type));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < n; i++) {
|
||||
putSized(o + (long) i * compBytes, compBytes, normComponent(t, srcSdlFormat, kind, (swizzle >> (i << 2)) & 15, type, direct, clamp));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (swapSize != 0) swapRow(d, rowBytes, swapSize);
|
||||
}
|
||||
}
|
||||
|
||||
public static void packDepth(long srcR32f, int srcRowBytes, int width, int height, boolean flipRows, int type, PackState ps, long dst) {
|
||||
validate(GL11.GL_DEPTH_COMPONENT, type);
|
||||
if (width <= 0 || height <= 0) return;
|
||||
final int size = GLTypes.sizeBytes(type);
|
||||
final int stride = rowStride(GL11.GL_DEPTH_COMPONENT, type, width, ps);
|
||||
final long base = dst + (long) ps.skipRows * stride + (long) ps.skipPixels * size;
|
||||
for (int j = 0; j < height; j++) {
|
||||
final long s = srcR32f + (long) srcRow(j, height, flipRows) * srcRowBytes;
|
||||
final long d = base + (long) j * stride;
|
||||
for (int x = 0; x < width; x++) {
|
||||
putSized(d + (long) x * size, size, encodeDepth(MemoryUtil.memGetFloat(s + ((long) x << 2)), type));
|
||||
}
|
||||
if (ps.swapBytes && size > 1) swapRow(d, width * size, size);
|
||||
}
|
||||
}
|
||||
|
||||
public static void packStencil(long srcR8, int srcRowBytes, int width, int height, boolean flipRows, int type, PackState ps, long dst) {
|
||||
validate(GL11.GL_STENCIL_INDEX, type);
|
||||
if (width <= 0 || height <= 0) return;
|
||||
final int size = GLTypes.sizeBytes(type);
|
||||
final int stride = rowStride(GL11.GL_STENCIL_INDEX, type, width, ps);
|
||||
final long base = dst + (long) ps.skipRows * stride + (long) ps.skipPixels * size;
|
||||
for (int j = 0; j < height; j++) {
|
||||
final long s = srcR8 + (long) srcRow(j, height, flipRows) * srcRowBytes;
|
||||
final long d = base + (long) j * stride;
|
||||
for (int x = 0; x < width; x++) {
|
||||
putSized(d + (long) x * size, size, encodeStencil(MemoryUtil.memGetByte(s + x) & 0xFF, type));
|
||||
}
|
||||
if (ps.swapBytes && size > 1) swapRow(d, width * size, size);
|
||||
}
|
||||
}
|
||||
|
||||
public static void packDepthStencil(long srcR32f, long srcR8, int srcRowBytes32, int srcRowBytes8, int width, int height, boolean flipRows, int type, PackState ps, long dst) {
|
||||
validate(GL30.GL_DEPTH_STENCIL, type);
|
||||
if (width <= 0 || height <= 0) return;
|
||||
final int size = GLTypes.packedTexelBytes(type);
|
||||
final int stride = rowStride(GL30.GL_DEPTH_STENCIL, type, width, ps);
|
||||
final long base = dst + (long) ps.skipRows * stride + (long) ps.skipPixels * size;
|
||||
final boolean float32 = type == GL30.GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
|
||||
for (int j = 0; j < height; j++) {
|
||||
final int row = srcRow(j, height, flipRows);
|
||||
final long sd = srcR32f + (long) row * srcRowBytes32;
|
||||
final long ss = srcR8 + (long) row * srcRowBytes8;
|
||||
final long d = base + (long) j * stride;
|
||||
for (int x = 0; x < width; x++) {
|
||||
final float depth = MemoryUtil.memGetFloat(sd + ((long) x << 2));
|
||||
final int stencil = MemoryUtil.memGetByte(ss + x) & 0xFF;
|
||||
final long o = d + (long) x * size;
|
||||
if (float32) {
|
||||
MemoryUtil.memPutFloat(o, depth);
|
||||
MemoryUtil.memPutInt(o + 4, stencil);
|
||||
} else {
|
||||
MemoryUtil.memPutInt(o, ((int) (depth * 16777215.0f) << 8) | stencil);
|
||||
}
|
||||
}
|
||||
if (ps.swapBytes) swapRow(d, width * size, 4);
|
||||
}
|
||||
}
|
||||
|
||||
private static float halfToFloat(int h) {
|
||||
final int sign = (h & 0x8000) << 16;
|
||||
final int exp = (h >>> 10) & 0x1F;
|
||||
final int mant = h & 0x3FF;
|
||||
if (exp == 0) {
|
||||
final float v = mant * (1.0f / 16777216.0f);
|
||||
return sign != 0 ? -v : v;
|
||||
}
|
||||
if (exp == 31) return Float.intBitsToFloat(sign | 0x7F800000 | (mant << 13));
|
||||
return Float.intBitsToFloat(sign | ((exp - 15 + 127) << 23) | (mant << 13));
|
||||
}
|
||||
|
||||
private static int floatToHalf(float f) {
|
||||
final int x = Float.floatToRawIntBits(f);
|
||||
final int sign = (x >>> 16) & 0x8000;
|
||||
final int exp = (x >>> 23) & 0xFF;
|
||||
int mant = x & 0x7FFFFF;
|
||||
if (exp == 0xFF) return sign | 0x7C00 | (mant != 0 ? 0x200 | (mant >>> 13) : 0);
|
||||
final int he = exp - 127 + 15;
|
||||
if (he >= 0x1F) return sign | 0x7C00;
|
||||
if (he <= 0) {
|
||||
if (he < -10) return sign;
|
||||
mant |= 0x800000;
|
||||
final int shift = 14 - he;
|
||||
int hm = mant >>> shift;
|
||||
final int rem = mant & ((1 << shift) - 1);
|
||||
final int half = 1 << (shift - 1);
|
||||
if (rem > half || (rem == half && (hm & 1) != 0)) hm++;
|
||||
return sign | hm;
|
||||
}
|
||||
int out = sign | (he << 10) | (mant >>> 13);
|
||||
final int rem = mant & 0x1FFF;
|
||||
if (rem > 0x1000 || (rem == 0x1000 && (out & 1) != 0)) out++;
|
||||
return out;
|
||||
}
|
||||
|
||||
private static float uf11ToFloat(int v) {
|
||||
return packedUfloatToFloat(v, 6);
|
||||
}
|
||||
|
||||
private static float uf10ToFloat(int v) {
|
||||
return packedUfloatToFloat(v, 5);
|
||||
}
|
||||
|
||||
private static float packedUfloatToFloat(int v, int mantBits) {
|
||||
final int exp = (v >>> mantBits) & 0x1F;
|
||||
final int mant = v & ((1 << mantBits) - 1);
|
||||
if (exp == 0) return mant == 0 ? 0.0f : mant * (1.0f / (1 << 14)) / (1 << mantBits);
|
||||
if (exp == 31) return Float.intBitsToFloat(0x7F800000 | mant);
|
||||
final int e = exp - 15;
|
||||
final float scale = e < 0 ? 1.0f / (1 << -e) : (float) (1 << e);
|
||||
return scale * (1.0f + (float) mant / (1 << mantBits));
|
||||
}
|
||||
|
||||
private static int floatToUf11(float val) {
|
||||
return floatToPackedUfloat(val, 6, 65024.0f);
|
||||
}
|
||||
|
||||
private static int floatToUf10(float val) {
|
||||
return floatToPackedUfloat(val, 5, 64512.0f);
|
||||
}
|
||||
|
||||
private static int floatToPackedUfloat(float val, int mantBits, float maxFinite) {
|
||||
final int bits = Float.floatToRawIntBits(val);
|
||||
final boolean negative = bits < 0;
|
||||
int exponent = ((bits >>> 23) & 0xFF) - 127;
|
||||
final int f32Mant = bits & 0x7FFFFF;
|
||||
final int maxExponent = 0x1F << mantBits;
|
||||
if (exponent == 128) {
|
||||
if (f32Mant != 0) return maxExponent | 1;
|
||||
return negative ? 0 : maxExponent;
|
||||
}
|
||||
if (negative) return 0;
|
||||
if (val > maxFinite) return (30 << mantBits) | ((1 << mantBits) - 1);
|
||||
if (exponent > -15) {
|
||||
int mant = (int) Math.rint(Math.scalb((double) val, mantBits - exponent));
|
||||
if (mant >= 2 << mantBits) {
|
||||
mant >>= 1;
|
||||
exponent++;
|
||||
}
|
||||
mant &= (1 << mantBits) - 1;
|
||||
return ((exponent + 15) << mantBits) | mant;
|
||||
}
|
||||
final int mant = (int) Math.rint(Math.scalb((double) val, mantBits + 14));
|
||||
if ((mant >> mantBits) != 0) return 1 << mantBits;
|
||||
return mant;
|
||||
}
|
||||
|
||||
private static int floatToRgb9e5(float r, float g, float b) {
|
||||
final int rc = rgb9e5ClampRange(r);
|
||||
final int gc = rgb9e5ClampRange(g);
|
||||
final int bc = rgb9e5ClampRange(b);
|
||||
int maxrgb = Math.max(rc, Math.max(gc, bc));
|
||||
maxrgb += maxrgb & (1 << (23 - 9));
|
||||
final int expShared = Math.max(maxrgb >>> 23, -15 - 1 + 127) + 1 + 15 - 127;
|
||||
final int revdenomBiasedExp = 127 - (expShared - 15 - 9) + 1;
|
||||
final float revdenom = Float.intBitsToFloat(revdenomBiasedExp << 23);
|
||||
int rm = (int) (Float.intBitsToFloat(rc) * revdenom);
|
||||
int gm = (int) (Float.intBitsToFloat(gc) * revdenom);
|
||||
int bm = (int) (Float.intBitsToFloat(bc) * revdenom);
|
||||
rm = (rm & 1) + (rm >> 1);
|
||||
gm = (gm & 1) + (gm >> 1);
|
||||
bm = (bm & 1) + (bm >> 1);
|
||||
return (expShared << 27) | (bm << 18) | (gm << 9) | rm;
|
||||
}
|
||||
|
||||
private static int rgb9e5ClampRange(float x) {
|
||||
final int u = Float.floatToRawIntBits(x);
|
||||
final int max = Float.floatToRawIntBits(MAX_RGB9E5);
|
||||
if (Integer.compareUnsigned(u, 0x7F800000) > 0) return 0;
|
||||
if (Integer.compareUnsigned(u, max) >= 0) return max;
|
||||
return u;
|
||||
}
|
||||
|
||||
private static int srcRow(int j, int height, boolean flipRows) {
|
||||
return flipRows ? height - 1 - j : j;
|
||||
}
|
||||
|
||||
private static int swapUnit(int type) {
|
||||
final int packed = GLTypes.packedTexelBytes(type);
|
||||
final int size = packed != 0 ? packed : GLTypes.sizeBytes(type);
|
||||
return (size == 2 || size == 4) ? size : 0;
|
||||
}
|
||||
|
||||
private static void swapRow(long p, int bytes, int size) {
|
||||
if (size == 2) {
|
||||
for (int i = 0; i + 1 < bytes; i += 2) {
|
||||
MemoryUtil.memPutShort(p + i, Short.reverseBytes(MemoryUtil.memGetShort(p + i)));
|
||||
}
|
||||
} else if (size == 4) {
|
||||
for (int i = 0; i + 3 < bytes; i += 4) {
|
||||
MemoryUtil.memPutInt(p + i, Integer.reverseBytes(MemoryUtil.memGetInt(p + i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void putSized(long p, int size, int v) {
|
||||
switch (size) {
|
||||
case 1 -> MemoryUtil.memPutByte(p, (byte) v);
|
||||
case 2 -> MemoryUtil.memPutShort(p, (short) v);
|
||||
default -> MemoryUtil.memPutInt(p, v);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean clampNeeded(int kind, boolean readPixels, int format, int type, boolean lumConversion) {
|
||||
if (readPixels) {
|
||||
final boolean clampRead = kind == K_UNORM || kind == K_SNORM;
|
||||
final boolean floatType = type == GL11.GL_FLOAT || type == GL30.GL_HALF_FLOAT || type == GL30.GL_UNSIGNED_INT_10F_11F_11F_REV;
|
||||
final boolean clamp = clampRead || !floatType;
|
||||
return clamp && !(kind == K_UNORM && !lumConversion);
|
||||
}
|
||||
final boolean typeNeedsClamping = switch (type) {
|
||||
case GL11.GL_BYTE, GL11.GL_SHORT, GL11.GL_INT, GL11.GL_FLOAT, GL30.GL_HALF_FLOAT,
|
||||
GL30.GL_UNSIGNED_INT_10F_11F_11F_REV, GL30.GL_UNSIGNED_INT_5_9_9_9_REV -> false;
|
||||
default -> true;
|
||||
};
|
||||
return typeNeedsClamping && (kind == K_FLOAT || kind == K_SNORM || isLuminanceFormat(format));
|
||||
}
|
||||
|
||||
private static int packedTexel(long t, int fmt, int kind, int swizzle, int n, int type, boolean direct, boolean clamp, boolean dstInteger) {
|
||||
if (type == GL30.GL_UNSIGNED_INT_10F_11F_11F_REV) {
|
||||
return floatToUf11(channelFloat(t, fmt, kind, swizzle & 15, clamp))
|
||||
| (floatToUf11(channelFloat(t, fmt, kind, (swizzle >> 4) & 15, clamp)) << 11)
|
||||
| (floatToUf10(channelFloat(t, fmt, kind, (swizzle >> 8) & 15, clamp)) << 22);
|
||||
}
|
||||
if (type == GL30.GL_UNSIGNED_INT_5_9_9_9_REV) {
|
||||
return floatToRgb9e5(channelFloat(t, fmt, kind, swizzle & 15, clamp),
|
||||
channelFloat(t, fmt, kind, (swizzle >> 4) & 15, clamp),
|
||||
channelFloat(t, fmt, kind, (swizzle >> 8) & 15, clamp));
|
||||
}
|
||||
int word = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
final int ch = (swizzle >> (i << 2)) & 15;
|
||||
final int bits = packedBits(type, i);
|
||||
final int v;
|
||||
if (dstInteger) {
|
||||
v = (int) clampRange(integerChannel(t, fmt, ch), 0L, (1L << bits) - 1L);
|
||||
} else {
|
||||
v = normField(t, fmt, kind, ch, bits, direct, clamp);
|
||||
}
|
||||
word |= (v & ((1 << bits) - 1)) << packedShift(type, i);
|
||||
}
|
||||
return word;
|
||||
}
|
||||
|
||||
private static int normField(long t, int fmt, int kind, int ch, int dstBits, boolean direct, boolean clamp) {
|
||||
final int bits = PixelOps.colorChannelBits(fmt, ch);
|
||||
if (bits == 0) return floatToUnorm((ch == CH_A || ch == CH_ONE) ? 1.0f : 0.0f, dstBits);
|
||||
if (direct) {
|
||||
final long raw = rawChannel(t, fmt, ch);
|
||||
return (int) (kind == K_SNORM ? snormToUnorm(raw, bits, dstBits) : unormToUnorm(raw, bits, dstBits));
|
||||
}
|
||||
float f = floatChannel(t, fmt, kind, ch, bits);
|
||||
if (clamp) f = clamp01(f);
|
||||
return floatToUnorm(f, dstBits);
|
||||
}
|
||||
|
||||
private static int normComponent(long t, int fmt, int kind, int ch, int type, boolean direct, boolean clamp) {
|
||||
if (ch == CH_L || ch == CH_L_RG) return encodeFloat(channelFloat(t, fmt, kind, ch, clamp), type);
|
||||
final int bits = PixelOps.colorChannelBits(fmt, ch);
|
||||
if (bits == 0) return encodeFloat((ch == CH_A || ch == CH_ONE) ? 1.0f : 0.0f, type);
|
||||
if (direct) {
|
||||
return encodeDirect(rawChannel(t, fmt, ch), bits, kind == K_SNORM, type);
|
||||
}
|
||||
float f = floatChannel(t, fmt, kind, ch, bits);
|
||||
if (clamp) f = clamp01(f);
|
||||
return encodeFloat(f, type);
|
||||
}
|
||||
|
||||
private static float channelFloat(long t, int fmt, int kind, int ch, boolean clamp) {
|
||||
if (ch == CH_L || ch == CH_L_RG) {
|
||||
float sum = channelFloat(t, fmt, kind, CH_R, clamp) + channelFloat(t, fmt, kind, CH_G, clamp);
|
||||
if (ch == CH_L) sum += channelFloat(t, fmt, kind, CH_B, clamp);
|
||||
return clamp ? clamp01(sum) : sum;
|
||||
}
|
||||
final int bits = PixelOps.colorChannelBits(fmt, ch);
|
||||
if (bits == 0) return (ch == CH_A || ch == CH_ONE) ? 1.0f : 0.0f;
|
||||
final float f = floatChannel(t, fmt, kind, ch, bits);
|
||||
return clamp ? clamp01(f) : f;
|
||||
}
|
||||
|
||||
private static float clamp01(float f) {
|
||||
return f < 0.0f ? 0.0f : (f > 1.0f ? 1.0f : f);
|
||||
}
|
||||
|
||||
private static long integerChannel(long t, int fmt, int ch) {
|
||||
if (PixelOps.colorChannelBits(fmt, ch) == 0) return (ch == CH_A || ch == CH_ONE) ? 1L : 0L;
|
||||
return rawChannel(t, fmt, ch);
|
||||
}
|
||||
|
||||
private static int clampInteger(long v, int type) {
|
||||
return switch (type) {
|
||||
case GL11.GL_UNSIGNED_BYTE -> (int) clampRange(v, 0L, 0xFFL);
|
||||
case GL11.GL_BYTE -> (int) clampRange(v, Byte.MIN_VALUE, Byte.MAX_VALUE);
|
||||
case GL11.GL_UNSIGNED_SHORT -> (int) clampRange(v, 0L, 0xFFFFL);
|
||||
case GL11.GL_SHORT -> (int) clampRange(v, Short.MIN_VALUE, Short.MAX_VALUE);
|
||||
case GL11.GL_UNSIGNED_INT -> (int) clampRange(v, 0L, 0xFFFFFFFFL);
|
||||
default -> (int) clampRange(v, Integer.MIN_VALUE, Integer.MAX_VALUE);
|
||||
};
|
||||
}
|
||||
|
||||
private static long clampRange(long v, long lo, long hi) {
|
||||
return v < lo ? lo : (v > hi ? hi : v);
|
||||
}
|
||||
|
||||
private static int encodeFloat(float f, int type) {
|
||||
return switch (type) {
|
||||
case GL11.GL_UNSIGNED_BYTE -> floatToUnorm(f, 8);
|
||||
case GL11.GL_UNSIGNED_SHORT -> floatToUnorm(f, 16);
|
||||
case GL11.GL_UNSIGNED_INT -> floatToUnorm(f, 32);
|
||||
case GL11.GL_BYTE -> floatToSnorm(f, 8);
|
||||
case GL11.GL_SHORT -> floatToSnorm(f, 16);
|
||||
case GL11.GL_INT -> floatToSnorm(f, 32);
|
||||
case GL30.GL_HALF_FLOAT -> floatToHalf(f);
|
||||
default -> Float.floatToRawIntBits(f);
|
||||
};
|
||||
}
|
||||
|
||||
private static int encodeDirect(long raw, int bits, boolean signed, int type) {
|
||||
return switch (type) {
|
||||
case GL11.GL_UNSIGNED_BYTE -> (int) (signed ? snormToUnorm(raw, bits, 8) : unormToUnorm(raw, bits, 8));
|
||||
case GL11.GL_UNSIGNED_SHORT -> (int) (signed ? snormToUnorm(raw, bits, 16) : unormToUnorm(raw, bits, 16));
|
||||
case GL11.GL_UNSIGNED_INT -> (int) (signed ? snormToUnorm(raw, bits, 32) : unormToUnorm(raw, bits, 32));
|
||||
case GL11.GL_BYTE -> (int) (signed ? snormToSnorm(raw, bits, 8) : unormToUnorm(raw, bits, 7));
|
||||
case GL11.GL_SHORT -> (int) (signed ? snormToSnorm(raw, bits, 16) : unormToUnorm(raw, bits, 15));
|
||||
case GL11.GL_INT -> (int) (signed ? snormToSnorm(raw, bits, 32) : unormToUnorm(raw, bits, 31));
|
||||
default -> encodeFloat(signed ? snormToFloat(raw, bits) : unormToFloat(raw, bits), type);
|
||||
};
|
||||
}
|
||||
|
||||
private static long maxUnsigned(int bits) {
|
||||
return (1L << bits) - 1L;
|
||||
}
|
||||
|
||||
private static long unormToUnorm(long x, int srcBits, int dstBits) {
|
||||
if (srcBits == dstBits) return x;
|
||||
final long srcMax = maxUnsigned(srcBits);
|
||||
return (x * maxUnsigned(dstBits) * 2L + srcMax) / (srcMax * 2L);
|
||||
}
|
||||
|
||||
private static long snormToUnorm(long x, int srcBits, int dstBits) {
|
||||
return x < 0 ? 0L : unormToUnorm(x, srcBits - 1, dstBits);
|
||||
}
|
||||
|
||||
private static long snormToSnorm(long x, int srcBits, int dstBits) {
|
||||
if (srcBits == dstBits) return x;
|
||||
final long srcMax = maxUnsigned(srcBits - 1);
|
||||
final long dstMax = maxUnsigned(dstBits - 1);
|
||||
if (x <= -srcMax) return -dstMax;
|
||||
final long magnitude = ((x < 0 ? -x : x) * dstMax * 2L + srcMax) / (srcMax * 2L);
|
||||
return x < 0 ? -magnitude : magnitude;
|
||||
}
|
||||
|
||||
private static float unormToFloat(long x, int bits) {
|
||||
return x * (1.0f / (float) maxUnsigned(bits));
|
||||
}
|
||||
|
||||
private static float snormToFloat(long x, int bits) {
|
||||
final long max = maxUnsigned(bits - 1);
|
||||
if (x <= -max) return -1.0f;
|
||||
return x * (1.0f / (float) max);
|
||||
}
|
||||
|
||||
private static int floatToUnorm(float x, int bits) {
|
||||
if (x < 0.0f) return 0;
|
||||
if (bits >= 32) {
|
||||
if (x > 1.0f) return -1;
|
||||
return (int) (long) Math.rint(x * 4294967295.0);
|
||||
}
|
||||
final long max = maxUnsigned(bits);
|
||||
if (x > 1.0f) return (int) max;
|
||||
return (int) (long) Math.rint(x * (float) max);
|
||||
}
|
||||
|
||||
private static int floatToSnorm(float x, int bits) {
|
||||
final long max = maxUnsigned(bits - 1);
|
||||
if (x < -1.0f) return (int) -max;
|
||||
if (x > 1.0f) return (int) max;
|
||||
if (bits >= 32) return (int) (long) Math.rint(x * 2147483647.0);
|
||||
return (int) (long) Math.rint(x * (float) max);
|
||||
}
|
||||
|
||||
private static int encodeDepth(float z, int type) {
|
||||
return switch (type) {
|
||||
case GL11.GL_UNSIGNED_BYTE -> (int) (z * 255.0f);
|
||||
case GL11.GL_BYTE -> (((int) (255.0f * z)) - 1) / 2;
|
||||
case GL11.GL_UNSIGNED_SHORT -> (int) (long) Math.rint(z * 65535.0f);
|
||||
case GL11.GL_SHORT -> (((int) (65535.0f * z)) - 1) / 2;
|
||||
case GL11.GL_UNSIGNED_INT -> (int) (long) (z * 4294967295.0);
|
||||
case GL11.GL_INT -> (int) (2147483647.0 * z);
|
||||
case GL30.GL_HALF_FLOAT -> floatToHalf(z);
|
||||
default -> Float.floatToRawIntBits(z);
|
||||
};
|
||||
}
|
||||
|
||||
private static int encodeStencil(int s, int type) {
|
||||
return switch (type) {
|
||||
case GL11.GL_BYTE -> s & 0x7F;
|
||||
case GL11.GL_FLOAT -> Float.floatToRawIntBits((float) s);
|
||||
case GL30.GL_HALF_FLOAT -> floatToHalf((float) s);
|
||||
default -> s;
|
||||
};
|
||||
}
|
||||
|
||||
private static int sourceKind(int fmt) {
|
||||
return switch (fmt) {
|
||||
case SDL_GPU_TEXTUREFORMAT_R8_UNORM, SDL_GPU_TEXTUREFORMAT_R8G8_UNORM, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB, SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB, SDL_GPU_TEXTUREFORMAT_A8_UNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_R16_UNORM, SDL_GPU_TEXTUREFORMAT_R16G16_UNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM, SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM, SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM -> K_UNORM;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8_SNORM, SDL_GPU_TEXTUREFORMAT_R8G8_SNORM, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_R16_SNORM, SDL_GPU_TEXTUREFORMAT_R16G16_SNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_SNORM -> K_SNORM;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16_FLOAT, SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT, SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT,
|
||||
SDL_GPU_TEXTUREFORMAT_R32_FLOAT, SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT, SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT,
|
||||
SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT -> K_FLOAT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8_UINT, SDL_GPU_TEXTUREFORMAT_R8G8_UINT, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT,
|
||||
SDL_GPU_TEXTUREFORMAT_R16_UINT, SDL_GPU_TEXTUREFORMAT_R16G16_UINT, SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UINT,
|
||||
SDL_GPU_TEXTUREFORMAT_R32_UINT, SDL_GPU_TEXTUREFORMAT_R32G32_UINT, SDL_GPU_TEXTUREFORMAT_R32G32B32A32_UINT -> K_UINT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8_INT, SDL_GPU_TEXTUREFORMAT_R8G8_INT, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_INT,
|
||||
SDL_GPU_TEXTUREFORMAT_R16_INT, SDL_GPU_TEXTUREFORMAT_R16G16_INT, SDL_GPU_TEXTUREFORMAT_R16G16B16A16_INT,
|
||||
SDL_GPU_TEXTUREFORMAT_R32_INT, SDL_GPU_TEXTUREFORMAT_R32G32_INT, SDL_GPU_TEXTUREFORMAT_R32G32B32A32_INT -> K_SINT;
|
||||
default -> throw new IllegalArgumentException("Unsupported color readback source format " + fmt);
|
||||
};
|
||||
}
|
||||
|
||||
private static long rawChannel(long t, int fmt, int ch) {
|
||||
return switch (fmt) {
|
||||
case SDL_GPU_TEXTUREFORMAT_R8_UNORM, SDL_GPU_TEXTUREFORMAT_R8G8_UNORM, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB, SDL_GPU_TEXTUREFORMAT_R8_UINT, SDL_GPU_TEXTUREFORMAT_R8G8_UINT,
|
||||
SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT -> MemoryUtil.memGetByte(t + ch) & 0xFFL;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8_SNORM, SDL_GPU_TEXTUREFORMAT_R8G8_SNORM, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_R8_INT, SDL_GPU_TEXTUREFORMAT_R8G8_INT, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_INT -> MemoryUtil.memGetByte(t + ch);
|
||||
case SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM, SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB -> MemoryUtil.memGetByte(t + bgraOffset(ch)) & 0xFFL;
|
||||
case SDL_GPU_TEXTUREFORMAT_A8_UNORM -> MemoryUtil.memGetByte(t) & 0xFFL;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16_UNORM, SDL_GPU_TEXTUREFORMAT_R16G16_UNORM, SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_R16_UINT, SDL_GPU_TEXTUREFORMAT_R16G16_UINT,
|
||||
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UINT -> MemoryUtil.memGetShort(t + ((long) ch << 1)) & 0xFFFFL;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16_SNORM, SDL_GPU_TEXTUREFORMAT_R16G16_SNORM, SDL_GPU_TEXTUREFORMAT_R16G16B16A16_SNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_R16_INT, SDL_GPU_TEXTUREFORMAT_R16G16_INT,
|
||||
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_INT -> MemoryUtil.memGetShort(t + ((long) ch << 1));
|
||||
case SDL_GPU_TEXTUREFORMAT_R32_UINT, SDL_GPU_TEXTUREFORMAT_R32G32_UINT,
|
||||
SDL_GPU_TEXTUREFORMAT_R32G32B32A32_UINT -> MemoryUtil.memGetInt(t + ((long) ch << 2)) & 0xFFFFFFFFL;
|
||||
case SDL_GPU_TEXTUREFORMAT_R32_INT, SDL_GPU_TEXTUREFORMAT_R32G32_INT,
|
||||
SDL_GPU_TEXTUREFORMAT_R32G32B32A32_INT -> MemoryUtil.memGetInt(t + ((long) ch << 2));
|
||||
case SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM -> {
|
||||
final int w = MemoryUtil.memGetInt(t);
|
||||
yield ch == CH_A ? (w >>> 30) : ((w >>> (10 * ch)) & 0x3FF);
|
||||
}
|
||||
case SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM -> {
|
||||
final int w = MemoryUtil.memGetShort(t) & 0xFFFF;
|
||||
yield switch (ch) {
|
||||
case CH_R -> (w >>> 11) & 0x1F;
|
||||
case CH_G -> (w >>> 5) & 0x3F;
|
||||
default -> w & 0x1F;
|
||||
};
|
||||
}
|
||||
case SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM -> {
|
||||
final int w = MemoryUtil.memGetShort(t) & 0xFFFF;
|
||||
yield switch (ch) {
|
||||
case CH_R -> (w >>> 10) & 0x1F;
|
||||
case CH_G -> (w >>> 5) & 0x1F;
|
||||
case CH_B -> w & 0x1F;
|
||||
default -> (w >>> 15) & 1;
|
||||
};
|
||||
}
|
||||
case SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM -> {
|
||||
final int w = MemoryUtil.memGetShort(t) & 0xFFFF;
|
||||
yield switch (ch) {
|
||||
case CH_R -> (w >>> 8) & 0xF;
|
||||
case CH_G -> (w >>> 4) & 0xF;
|
||||
case CH_B -> w & 0xF;
|
||||
default -> (w >>> 12) & 0xF;
|
||||
};
|
||||
}
|
||||
default -> throw new IllegalArgumentException("Unsupported color readback source format " + fmt);
|
||||
};
|
||||
}
|
||||
|
||||
private static float floatChannel(long t, int fmt, int kind, int ch, int bits) {
|
||||
if (kind == K_UNORM) return unormToFloat(rawChannel(t, fmt, ch), bits);
|
||||
if (kind == K_SNORM) return snormToFloat(rawChannel(t, fmt, ch), bits);
|
||||
return switch (fmt) {
|
||||
case SDL_GPU_TEXTUREFORMAT_R16_FLOAT, SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT,
|
||||
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT -> halfToFloat(MemoryUtil.memGetShort(t + ((long) ch << 1)) & 0xFFFF);
|
||||
case SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT -> {
|
||||
final int w = MemoryUtil.memGetInt(t);
|
||||
yield switch (ch) {
|
||||
case CH_R -> uf11ToFloat(w & 0x7FF);
|
||||
case CH_G -> uf11ToFloat((w >>> 11) & 0x7FF);
|
||||
default -> uf10ToFloat((w >>> 22) & 0x3FF);
|
||||
};
|
||||
}
|
||||
default -> MemoryUtil.memGetFloat(t + ((long) ch << 2));
|
||||
};
|
||||
}
|
||||
|
||||
private static int bgraOffset(int ch) {
|
||||
return ch == CH_R ? 2 : (ch == CH_B ? 0 : ch);
|
||||
}
|
||||
|
||||
private static boolean isUnorm8x4(int fmt) {
|
||||
return fmt == SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM || fmt == SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB
|
||||
|| fmt == SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM || fmt == SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB;
|
||||
}
|
||||
|
||||
private static boolean byteShuffleType(int format, int type) {
|
||||
if (type == GL11.GL_UNSIGNED_BYTE) return !isLuminanceFormat(format);
|
||||
return (type == GL12.GL_UNSIGNED_INT_8_8_8_8 || type == GL12.GL_UNSIGNED_INT_8_8_8_8_REV)
|
||||
&& (format == GL11.GL_RGBA || format == GL12.GL_BGRA);
|
||||
}
|
||||
|
||||
private static int shuffleOffset(int fmt, int swizzle, int n, boolean reversed, int outByte) {
|
||||
if (outByte >= n) return 0;
|
||||
final int comp = reversed ? 3 - outByte : outByte;
|
||||
final int ch = (swizzle >> (comp << 2)) & 15;
|
||||
final boolean bgra = fmt == SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM || fmt == SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB;
|
||||
return bgra ? bgraOffset(ch) : ch;
|
||||
}
|
||||
|
||||
private static boolean layoutMatches(int fmt, int format, int type) {
|
||||
return switch (fmt) {
|
||||
case SDL_GPU_TEXTUREFORMAT_R8_UNORM -> format == GL11.GL_RED && type == GL11.GL_UNSIGNED_BYTE;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8G8_UNORM -> format == GL30.GL_RG && type == GL11.GL_UNSIGNED_BYTE;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB ->
|
||||
format == GL11.GL_RGBA && (type == GL11.GL_UNSIGNED_BYTE || (LITTLE_ENDIAN && type == GL12.GL_UNSIGNED_INT_8_8_8_8_REV));
|
||||
case SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM, SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB ->
|
||||
format == GL12.GL_BGRA && (type == GL11.GL_UNSIGNED_BYTE || (LITTLE_ENDIAN && type == GL12.GL_UNSIGNED_INT_8_8_8_8_REV));
|
||||
case SDL_GPU_TEXTUREFORMAT_A8_UNORM -> format == GL11.GL_ALPHA && type == GL11.GL_UNSIGNED_BYTE;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16_UNORM -> format == GL11.GL_RED && type == GL11.GL_UNSIGNED_SHORT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16G16_UNORM -> format == GL30.GL_RG && type == GL11.GL_UNSIGNED_SHORT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM -> format == GL11.GL_RGBA && type == GL11.GL_UNSIGNED_SHORT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8_SNORM -> format == GL11.GL_RED && type == GL11.GL_BYTE;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8G8_SNORM -> format == GL30.GL_RG && type == GL11.GL_BYTE;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM -> format == GL11.GL_RGBA && type == GL11.GL_BYTE;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16_SNORM -> format == GL11.GL_RED && type == GL11.GL_SHORT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16G16_SNORM -> format == GL30.GL_RG && type == GL11.GL_SHORT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16G16B16A16_SNORM -> format == GL11.GL_RGBA && type == GL11.GL_SHORT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16_FLOAT -> format == GL11.GL_RED && type == GL30.GL_HALF_FLOAT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT -> format == GL30.GL_RG && type == GL30.GL_HALF_FLOAT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT -> format == GL11.GL_RGBA && type == GL30.GL_HALF_FLOAT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R32_FLOAT -> format == GL11.GL_RED && type == GL11.GL_FLOAT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT -> format == GL30.GL_RG && type == GL11.GL_FLOAT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT -> format == GL11.GL_RGBA && type == GL11.GL_FLOAT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT -> format == GL11.GL_RGB && type == GL30.GL_UNSIGNED_INT_10F_11F_11F_REV;
|
||||
case SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM -> format == GL11.GL_RGBA && type == GL12.GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
case SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM -> format == GL11.GL_RGB && type == GL12.GL_UNSIGNED_SHORT_5_6_5;
|
||||
case SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM -> format == GL12.GL_BGRA && type == GL12.GL_UNSIGNED_SHORT_1_5_5_5_REV;
|
||||
case SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM -> format == GL12.GL_BGRA && type == GL12.GL_UNSIGNED_SHORT_4_4_4_4_REV;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8_UINT -> format == GL30.GL_RED_INTEGER && type == GL11.GL_UNSIGNED_BYTE;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8G8_UINT -> format == GL30.GL_RG_INTEGER && type == GL11.GL_UNSIGNED_BYTE;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT -> format == GL30.GL_RGBA_INTEGER && type == GL11.GL_UNSIGNED_BYTE;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16_UINT -> format == GL30.GL_RED_INTEGER && type == GL11.GL_UNSIGNED_SHORT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16G16_UINT -> format == GL30.GL_RG_INTEGER && type == GL11.GL_UNSIGNED_SHORT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UINT -> format == GL30.GL_RGBA_INTEGER && type == GL11.GL_UNSIGNED_SHORT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R32_UINT -> format == GL30.GL_RED_INTEGER && type == GL11.GL_UNSIGNED_INT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R32G32_UINT -> format == GL30.GL_RG_INTEGER && type == GL11.GL_UNSIGNED_INT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R32G32B32A32_UINT -> format == GL30.GL_RGBA_INTEGER && type == GL11.GL_UNSIGNED_INT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8_INT -> format == GL30.GL_RED_INTEGER && type == GL11.GL_BYTE;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8G8_INT -> format == GL30.GL_RG_INTEGER && type == GL11.GL_BYTE;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8G8B8A8_INT -> format == GL30.GL_RGBA_INTEGER && type == GL11.GL_BYTE;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16_INT -> format == GL30.GL_RED_INTEGER && type == GL11.GL_SHORT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16G16_INT -> format == GL30.GL_RG_INTEGER && type == GL11.GL_SHORT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16G16B16A16_INT -> format == GL30.GL_RGBA_INTEGER && type == GL11.GL_SHORT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R32_INT -> format == GL30.GL_RED_INTEGER && type == GL11.GL_INT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R32G32_INT -> format == GL30.GL_RG_INTEGER && type == GL11.GL_INT;
|
||||
case SDL_GPU_TEXTUREFORMAT_R32G32B32A32_INT -> format == GL30.GL_RGBA_INTEGER && type == GL11.GL_INT;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private static int packedBits(int type, int i) {
|
||||
return switch (type) {
|
||||
case GL12.GL_UNSIGNED_BYTE_3_3_2 -> i == 2 ? 2 : 3;
|
||||
case GL12.GL_UNSIGNED_BYTE_2_3_3_REV -> i == 2 ? 2 : 3;
|
||||
case GL12.GL_UNSIGNED_SHORT_5_6_5, GL12.GL_UNSIGNED_SHORT_5_6_5_REV -> i == 1 ? 6 : 5;
|
||||
case GL12.GL_UNSIGNED_SHORT_4_4_4_4, GL12.GL_UNSIGNED_SHORT_4_4_4_4_REV -> 4;
|
||||
case GL12.GL_UNSIGNED_SHORT_5_5_5_1, GL12.GL_UNSIGNED_SHORT_1_5_5_5_REV -> i == 3 ? 1 : 5;
|
||||
case GL12.GL_UNSIGNED_INT_8_8_8_8, GL12.GL_UNSIGNED_INT_8_8_8_8_REV -> 8;
|
||||
default -> i == 3 ? 2 : 10;
|
||||
};
|
||||
}
|
||||
|
||||
private static int packedShift(int type, int i) {
|
||||
return switch (type) {
|
||||
case GL12.GL_UNSIGNED_BYTE_3_3_2 -> i == 0 ? 5 : (i == 1 ? 2 : 0);
|
||||
case GL12.GL_UNSIGNED_BYTE_2_3_3_REV -> i * 3;
|
||||
case GL12.GL_UNSIGNED_SHORT_5_6_5 -> i == 0 ? 11 : (i == 1 ? 5 : 0);
|
||||
case GL12.GL_UNSIGNED_SHORT_5_6_5_REV -> i == 0 ? 0 : (i == 1 ? 5 : 11);
|
||||
case GL12.GL_UNSIGNED_SHORT_4_4_4_4 -> 12 - 4 * i;
|
||||
case GL12.GL_UNSIGNED_SHORT_4_4_4_4_REV -> 4 * i;
|
||||
case GL12.GL_UNSIGNED_SHORT_5_5_5_1 -> i == 3 ? 0 : 11 - 5 * i;
|
||||
case GL12.GL_UNSIGNED_SHORT_1_5_5_5_REV -> 5 * i;
|
||||
case GL12.GL_UNSIGNED_INT_8_8_8_8 -> 24 - 8 * i;
|
||||
case GL12.GL_UNSIGNED_INT_8_8_8_8_REV -> 8 * i;
|
||||
case GL12.GL_UNSIGNED_INT_10_10_10_2 -> i == 3 ? 0 : 22 - 10 * i;
|
||||
default -> 10 * i;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isIntegerFormat(int format) {
|
||||
return switch (format) {
|
||||
case GL30.GL_RED_INTEGER, GL30.GL_GREEN_INTEGER, GL30.GL_BLUE_INTEGER, GL30.GL_ALPHA_INTEGER,
|
||||
GL30.GL_RG_INTEGER, GL30.GL_RGB_INTEGER, GL30.GL_BGR_INTEGER, GL30.GL_RGBA_INTEGER,
|
||||
GL30.GL_BGRA_INTEGER -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isLuminanceFormat(int format) {
|
||||
return format == GL11.GL_LUMINANCE || format == GL11.GL_LUMINANCE_ALPHA;
|
||||
}
|
||||
|
||||
private static boolean isColorFormat(int format) {
|
||||
return format != GL11.GL_DEPTH_COMPONENT && format != GL11.GL_STENCIL_INDEX && format != GL30.GL_DEPTH_STENCIL;
|
||||
}
|
||||
|
||||
private static int formatComponents(int format) {
|
||||
return switch (format) {
|
||||
case GL30.GL_RG, GL30.GL_RG_INTEGER, GL11.GL_LUMINANCE_ALPHA -> 2;
|
||||
case GL11.GL_RGB, GL12.GL_BGR, GL30.GL_RGB_INTEGER, GL30.GL_BGR_INTEGER -> 3;
|
||||
case GL11.GL_RGBA, GL12.GL_BGRA, GL30.GL_RGBA_INTEGER, GL30.GL_BGRA_INTEGER -> 4;
|
||||
default -> 1;
|
||||
};
|
||||
}
|
||||
|
||||
public static int glBaseFormat(int internalFormat) {
|
||||
return switch (internalFormat) {
|
||||
case GL11.GL_ALPHA, GL11.GL_ALPHA4, GL11.GL_ALPHA8, GL11.GL_ALPHA12, GL11.GL_ALPHA16,
|
||||
GL13.GL_COMPRESSED_ALPHA, ARBTextureFloat.GL_ALPHA16F_ARB, ARBTextureFloat.GL_ALPHA32F_ARB,
|
||||
EXTTextureSnorm.GL_ALPHA_SNORM, EXTTextureSnorm.GL_ALPHA8_SNORM, EXTTextureSnorm.GL_ALPHA16_SNORM,
|
||||
EXTTextureInteger.GL_ALPHA8UI_EXT, EXTTextureInteger.GL_ALPHA16UI_EXT, EXTTextureInteger.GL_ALPHA32UI_EXT,
|
||||
EXTTextureInteger.GL_ALPHA8I_EXT, EXTTextureInteger.GL_ALPHA16I_EXT, EXTTextureInteger.GL_ALPHA32I_EXT -> GL11.GL_ALPHA;
|
||||
case 1, GL11.GL_LUMINANCE, GL11.GL_LUMINANCE4, GL11.GL_LUMINANCE8, GL11.GL_LUMINANCE12, GL11.GL_LUMINANCE16,
|
||||
GL13.GL_COMPRESSED_LUMINANCE, ARBTextureFloat.GL_LUMINANCE16F_ARB, ARBTextureFloat.GL_LUMINANCE32F_ARB,
|
||||
EXTTextureSnorm.GL_LUMINANCE_SNORM, EXTTextureSnorm.GL_LUMINANCE8_SNORM, EXTTextureSnorm.GL_LUMINANCE16_SNORM,
|
||||
GL21.GL_SLUMINANCE, GL21.GL_SLUMINANCE8, GL21.GL_COMPRESSED_SLUMINANCE,
|
||||
EXTTextureInteger.GL_LUMINANCE8UI_EXT, EXTTextureInteger.GL_LUMINANCE16UI_EXT, EXTTextureInteger.GL_LUMINANCE32UI_EXT,
|
||||
EXTTextureInteger.GL_LUMINANCE8I_EXT, EXTTextureInteger.GL_LUMINANCE16I_EXT, EXTTextureInteger.GL_LUMINANCE32I_EXT -> GL11.GL_LUMINANCE;
|
||||
case 2, GL11.GL_LUMINANCE_ALPHA, GL11.GL_LUMINANCE4_ALPHA4, GL11.GL_LUMINANCE6_ALPHA2, GL11.GL_LUMINANCE8_ALPHA8,
|
||||
GL11.GL_LUMINANCE12_ALPHA4, GL11.GL_LUMINANCE12_ALPHA12, GL11.GL_LUMINANCE16_ALPHA16,
|
||||
GL13.GL_COMPRESSED_LUMINANCE_ALPHA, ARBTextureFloat.GL_LUMINANCE_ALPHA16F_ARB, ARBTextureFloat.GL_LUMINANCE_ALPHA32F_ARB,
|
||||
EXTTextureSnorm.GL_LUMINANCE_ALPHA_SNORM, EXTTextureSnorm.GL_LUMINANCE8_ALPHA8_SNORM, EXTTextureSnorm.GL_LUMINANCE16_ALPHA16_SNORM,
|
||||
GL21.GL_SLUMINANCE_ALPHA, GL21.GL_SLUMINANCE8_ALPHA8, GL21.GL_COMPRESSED_SLUMINANCE_ALPHA,
|
||||
EXTTextureInteger.GL_LUMINANCE_ALPHA8UI_EXT, EXTTextureInteger.GL_LUMINANCE_ALPHA16UI_EXT, EXTTextureInteger.GL_LUMINANCE_ALPHA32UI_EXT,
|
||||
EXTTextureInteger.GL_LUMINANCE_ALPHA8I_EXT, EXTTextureInteger.GL_LUMINANCE_ALPHA16I_EXT, EXTTextureInteger.GL_LUMINANCE_ALPHA32I_EXT -> GL11.GL_LUMINANCE_ALPHA;
|
||||
case GL11.GL_INTENSITY, GL11.GL_INTENSITY4, GL11.GL_INTENSITY8, GL11.GL_INTENSITY12, GL11.GL_INTENSITY16,
|
||||
GL13.GL_COMPRESSED_INTENSITY, ARBTextureFloat.GL_INTENSITY16F_ARB, ARBTextureFloat.GL_INTENSITY32F_ARB,
|
||||
EXTTextureSnorm.GL_INTENSITY_SNORM, EXTTextureSnorm.GL_INTENSITY8_SNORM, EXTTextureSnorm.GL_INTENSITY16_SNORM,
|
||||
EXTTextureInteger.GL_INTENSITY8UI_EXT, EXTTextureInteger.GL_INTENSITY16UI_EXT, EXTTextureInteger.GL_INTENSITY32UI_EXT,
|
||||
EXTTextureInteger.GL_INTENSITY8I_EXT, EXTTextureInteger.GL_INTENSITY16I_EXT, EXTTextureInteger.GL_INTENSITY32I_EXT -> GL11.GL_INTENSITY;
|
||||
case 3, GL11.GL_RGB, GL11.GL_R3_G3_B2, GL11.GL_RGB4, GL11.GL_RGB5, GL11.GL_RGB8, GL11.GL_RGB10, GL11.GL_RGB12, GL11.GL_RGB16,
|
||||
GL41.GL_RGB565, GL13.GL_COMPRESSED_RGB, GL21.GL_SRGB, GL21.GL_SRGB8, GL21.GL_COMPRESSED_SRGB,
|
||||
GL30.GL_RGB16F, GL30.GL_RGB32F, EXTTextureSnorm.GL_RGB_SNORM, GL31.GL_RGB8_SNORM, GL31.GL_RGB16_SNORM,
|
||||
GL30.GL_RGB8UI, GL30.GL_RGB16UI, GL30.GL_RGB32UI, GL30.GL_RGB8I, GL30.GL_RGB16I, GL30.GL_RGB32I,
|
||||
GL30.GL_RGB9_E5, GL30.GL_R11F_G11F_B10F -> GL11.GL_RGB;
|
||||
case 4, GL11.GL_RGBA, GL11.GL_RGBA2, GL11.GL_RGBA4, GL11.GL_RGB5_A1, GL11.GL_RGBA8, GL11.GL_RGB10_A2, GL11.GL_RGBA12, GL11.GL_RGBA16,
|
||||
GL12.GL_BGRA, EXTTextureStorage.GL_BGRA8_EXT, GL13.GL_COMPRESSED_RGBA,
|
||||
GL21.GL_SRGB_ALPHA, GL21.GL_SRGB8_ALPHA8, GL21.GL_COMPRESSED_SRGB_ALPHA,
|
||||
GL30.GL_RGBA16F, GL30.GL_RGBA32F, EXTTextureSnorm.GL_RGBA_SNORM, GL31.GL_RGBA8_SNORM, GL31.GL_RGBA16_SNORM,
|
||||
GL30.GL_RGBA8UI, GL30.GL_RGBA16UI, GL30.GL_RGBA32UI, GL30.GL_RGBA8I, GL30.GL_RGBA16I, GL30.GL_RGBA32I,
|
||||
GL33.GL_RGB10_A2UI -> GL11.GL_RGBA;
|
||||
case GL11.GL_RED, GL30.GL_R8, GL30.GL_R16, GL30.GL_R16F, GL30.GL_R32F, GL30.GL_COMPRESSED_RED,
|
||||
GL30.GL_R8I, GL30.GL_R8UI, GL30.GL_R16I, GL30.GL_R16UI, GL30.GL_R32I, GL30.GL_R32UI,
|
||||
EXTTextureSnorm.GL_RED_SNORM, GL31.GL_R8_SNORM, GL31.GL_R16_SNORM, EXTTextureSRGBR8.GL_SR8_EXT -> GL11.GL_RED;
|
||||
case GL30.GL_RG, GL30.GL_RG8, GL30.GL_RG16, GL30.GL_RG16F, GL30.GL_RG32F, GL30.GL_COMPRESSED_RG,
|
||||
GL30.GL_RG8I, GL30.GL_RG8UI, GL30.GL_RG16I, GL30.GL_RG16UI, GL30.GL_RG32I, GL30.GL_RG32UI,
|
||||
EXTTextureSnorm.GL_RG_SNORM, GL31.GL_RG8_SNORM, GL31.GL_RG16_SNORM, EXTTextureSRGBRG8.GL_SRG8_EXT -> GL30.GL_RG;
|
||||
case GL11.GL_DEPTH_COMPONENT, GL14.GL_DEPTH_COMPONENT16, GL14.GL_DEPTH_COMPONENT24, GL14.GL_DEPTH_COMPONENT32,
|
||||
GL30.GL_DEPTH_COMPONENT32F -> GL11.GL_DEPTH_COMPONENT;
|
||||
case GL30.GL_DEPTH_STENCIL, GL30.GL_DEPTH24_STENCIL8, GL30.GL_DEPTH32F_STENCIL8 -> GL30.GL_DEPTH_STENCIL;
|
||||
case GL11.GL_STENCIL_INDEX, GL30.GL_STENCIL_INDEX1, GL30.GL_STENCIL_INDEX4, GL30.GL_STENCIL_INDEX8,
|
||||
GL30.GL_STENCIL_INDEX16 -> GL11.GL_STENCIL_INDEX;
|
||||
default -> -1;
|
||||
};
|
||||
}
|
||||
|
||||
private static int rebaseSwizzle(int baseFormat) {
|
||||
return switch (baseFormat) {
|
||||
case GL11.GL_RGBA -> REBASE_IDENTITY;
|
||||
case GL11.GL_RGB -> CH_R | (CH_G << 4) | (CH_B << 8) | (CH_ONE << 12);
|
||||
case GL30.GL_RG -> CH_R | (CH_G << 4) | (CH_ZERO << 8) | (CH_ONE << 12);
|
||||
case GL11.GL_RED, GL11.GL_LUMINANCE, GL11.GL_INTENSITY -> CH_R | (CH_ZERO << 4) | (CH_ZERO << 8) | (CH_ONE << 12);
|
||||
case GL11.GL_GREEN -> CH_ZERO | (CH_G << 4) | (CH_ZERO << 8) | (CH_ONE << 12);
|
||||
case GL11.GL_BLUE -> CH_ZERO | (CH_ZERO << 4) | (CH_B << 8) | (CH_ONE << 12);
|
||||
case GL11.GL_ALPHA -> CH_ZERO | (CH_ZERO << 4) | (CH_ZERO << 8) | (CH_A << 12);
|
||||
case GL11.GL_LUMINANCE_ALPHA -> CH_R | (CH_ZERO << 4) | (CH_ZERO << 8) | (CH_A << 12);
|
||||
default -> throw new IllegalArgumentException("GL_INVALID_OPERATION: color readback from base format 0x" + Integer.toHexString(baseFormat));
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean rebaseChangesRequested(int swizzle, int n, int rebase) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
final int ch = (swizzle >> (i << 2)) & 15;
|
||||
if (ch <= CH_A && ((rebase >> (ch << 2)) & 15) != ch) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int applyRebase(int swizzle, int n, int rebase, boolean rgBase) {
|
||||
int out = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int ch = (swizzle >> (i << 2)) & 15;
|
||||
if (ch <= CH_A) ch = (rebase >> (ch << 2)) & 15;
|
||||
else if (ch == CH_L && rgBase) ch = CH_L_RG;
|
||||
out |= ch << (i << 2);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static int formatSwizzle(int format, boolean luminanceSum) {
|
||||
final int l = luminanceSum ? CH_L : CH_R;
|
||||
return switch (format) {
|
||||
case GL11.GL_GREEN, GL30.GL_GREEN_INTEGER -> CH_G;
|
||||
case GL11.GL_BLUE, GL30.GL_BLUE_INTEGER -> CH_B;
|
||||
case GL11.GL_ALPHA, GL30.GL_ALPHA_INTEGER -> CH_A;
|
||||
case GL11.GL_LUMINANCE -> l;
|
||||
case GL11.GL_LUMINANCE_ALPHA -> l | (CH_A << 4);
|
||||
case GL30.GL_RG, GL30.GL_RG_INTEGER -> CH_R | (CH_G << 4);
|
||||
case GL11.GL_RGB, GL30.GL_RGB_INTEGER -> CH_R | (CH_G << 4) | (CH_B << 8);
|
||||
case GL12.GL_BGR, GL30.GL_BGR_INTEGER -> CH_B | (CH_G << 4) | (CH_R << 8);
|
||||
case GL11.GL_RGBA, GL30.GL_RGBA_INTEGER -> CH_R | (CH_G << 4) | (CH_B << 8) | (CH_A << 12);
|
||||
case GL12.GL_BGRA, GL30.GL_BGRA_INTEGER -> CH_B | (CH_G << 4) | (CH_R << 8) | (CH_A << 12);
|
||||
default -> CH_R;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isBaseType(int type) {
|
||||
return switch (type) {
|
||||
case GL11.GL_BYTE, GL11.GL_UNSIGNED_BYTE, GL11.GL_SHORT, GL11.GL_UNSIGNED_SHORT,
|
||||
GL11.GL_INT, GL11.GL_UNSIGNED_INT, GL11.GL_FLOAT, GL30.GL_HALF_FLOAT -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isIntegerBaseType(int type) {
|
||||
return isBaseType(type) && type != GL11.GL_FLOAT && type != GL30.GL_HALF_FLOAT;
|
||||
}
|
||||
|
||||
private static boolean isKnownFormat(int format) {
|
||||
return switch (format) {
|
||||
case GL11.GL_RED, GL11.GL_GREEN, GL11.GL_BLUE, GL11.GL_ALPHA, GL30.GL_RG, GL11.GL_RGB, GL12.GL_BGR,
|
||||
GL11.GL_RGBA, GL12.GL_BGRA, GL11.GL_LUMINANCE, GL11.GL_LUMINANCE_ALPHA,
|
||||
GL11.GL_DEPTH_COMPONENT, GL11.GL_STENCIL_INDEX, GL30.GL_DEPTH_STENCIL -> true;
|
||||
default -> isIntegerFormat(format);
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isKnownType(int type) {
|
||||
return isBaseType(type) || GLTypes.packedTexelBytes(type) != 0;
|
||||
}
|
||||
|
||||
private static int checkFormatAndType(int format, int type) {
|
||||
if (!isKnownFormat(format) || !isKnownType(type)) return INVALID_ENUM;
|
||||
if (format == GL30.GL_DEPTH_STENCIL && type != GL30.GL_UNSIGNED_INT_24_8 && type != GL30.GL_FLOAT_32_UNSIGNED_INT_24_8_REV) {
|
||||
return INVALID_ENUM;
|
||||
}
|
||||
switch (type) {
|
||||
case GL12.GL_UNSIGNED_BYTE_3_3_2, GL12.GL_UNSIGNED_BYTE_2_3_3_REV,
|
||||
GL12.GL_UNSIGNED_SHORT_5_6_5, GL12.GL_UNSIGNED_SHORT_5_6_5_REV -> {
|
||||
return (format == GL11.GL_RGB || format == GL30.GL_RGB_INTEGER) ? OK : INVALID_OPERATION;
|
||||
}
|
||||
case GL12.GL_UNSIGNED_SHORT_4_4_4_4, GL12.GL_UNSIGNED_SHORT_4_4_4_4_REV,
|
||||
GL12.GL_UNSIGNED_INT_8_8_8_8, GL12.GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
GL12.GL_UNSIGNED_SHORT_5_5_5_1, GL12.GL_UNSIGNED_SHORT_1_5_5_5_REV,
|
||||
GL12.GL_UNSIGNED_INT_10_10_10_2, GL12.GL_UNSIGNED_INT_2_10_10_10_REV -> {
|
||||
return (format == GL11.GL_RGBA || format == GL12.GL_BGRA
|
||||
|| format == GL30.GL_RGBA_INTEGER || format == GL30.GL_BGRA_INTEGER) ? OK : INVALID_OPERATION;
|
||||
}
|
||||
case GL30.GL_UNSIGNED_INT_24_8, GL30.GL_FLOAT_32_UNSIGNED_INT_24_8_REV -> {
|
||||
return format == GL30.GL_DEPTH_STENCIL ? OK : INVALID_OPERATION;
|
||||
}
|
||||
case GL30.GL_UNSIGNED_INT_10F_11F_11F_REV -> {
|
||||
return format == GL11.GL_RGB ? OK : INVALID_OPERATION;
|
||||
}
|
||||
case GL30.GL_UNSIGNED_INT_5_9_9_9_REV -> {
|
||||
return format == GL11.GL_RGB ? OK : INVALID_ENUM;
|
||||
}
|
||||
default -> {
|
||||
if (isIntegerFormat(format)) return isIntegerBaseType(type) ? OK : INVALID_ENUM;
|
||||
return isBaseType(type) ? OK : INVALID_ENUM;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
-7
@@ -129,6 +129,7 @@ public final class ResourceManager {
|
||||
private int preferredD24S8 = SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT;
|
||||
private volatile int swapchainDepthStencilFormat = SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT;
|
||||
private long swapchainDepthStencil;
|
||||
private int swapchainDepthStencilUsage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
|
||||
private int swapchainDepthStencilWidth;
|
||||
private int swapchainDepthStencilHeight;
|
||||
|
||||
@@ -159,18 +160,34 @@ public final class ResourceManager {
|
||||
LOG.info("D24_UNORM_S8_UINT not supported for depth+sampler, using D32_FLOAT_S8_UINT");
|
||||
}
|
||||
|
||||
swapchainDepthStencilFormat = preferredD24S8;
|
||||
if (!SDL_GPUTextureSupportsFormat(dev, swapchainDepthStencilFormat, texType, SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)) {
|
||||
final int alt = swapchainDepthStencilFormat == SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT ? SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT : SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT;
|
||||
if (SDL_GPUTextureSupportsFormat(dev, alt, texType, SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)) {
|
||||
swapchainDepthStencilFormat = alt;
|
||||
final int alt = preferredD24S8 == SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT ? SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT : SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT;
|
||||
swapchainDepthStencilUsage = depthUsage;
|
||||
swapchainDepthStencilFormat = pickDepthStencilFormat(dev, texType, preferredD24S8, alt, depthUsage);
|
||||
if (swapchainDepthStencilFormat == 0) {
|
||||
swapchainDepthStencilUsage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
|
||||
swapchainDepthStencilFormat = pickDepthStencilFormat(dev, texType, preferredD24S8, alt, swapchainDepthStencilUsage);
|
||||
if (swapchainDepthStencilFormat != 0) {
|
||||
LOG.warn("No depth+stencil format is both sampleable and a render target; default framebuffer depth/stencil cannot be read back");
|
||||
} else {
|
||||
swapchainDepthStencilFormat = 0;
|
||||
LOG.warn("No depth+stencil format usable as a render target; default framebuffer will have no depth or stencil");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int pickDepthStencilFormat(long dev, int texType, int preferred, int alt, int usage) {
|
||||
if (SDL_GPUTextureSupportsFormat(dev, preferred, texType, usage)) return preferred;
|
||||
if (SDL_GPUTextureSupportsFormat(dev, alt, texType, usage)) return alt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getSwapchainDepthStencil() {
|
||||
return swapchainDepthStencil;
|
||||
}
|
||||
|
||||
public boolean isSwapchainDepthStencilSampleable() {
|
||||
return (swapchainDepthStencilUsage & SDL_GPU_TEXTUREUSAGE_SAMPLER) != 0;
|
||||
}
|
||||
|
||||
public int getSwapchainDepthStencilFormat() {
|
||||
return swapchainDepthStencilFormat;
|
||||
}
|
||||
@@ -364,6 +381,7 @@ public final class ResourceManager {
|
||||
|
||||
public void markTextureContentDefined(long handle) {
|
||||
if (handle == 0) return;
|
||||
if (isTextureContentDefined(handle)) return;
|
||||
wLock.lock();
|
||||
try { definedContentTextures.add(handle); } finally { wLock.unlock(); }
|
||||
}
|
||||
@@ -2134,7 +2152,7 @@ public final class ResourceManager {
|
||||
final SDL_GPUTextureCreateInfo ci = SDL_GPUTextureCreateInfo.calloc(stack)
|
||||
.type(SDL_GPU_TEXTURETYPE_2D)
|
||||
.format(swapchainDepthStencilFormat)
|
||||
.usage(SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)
|
||||
.usage(swapchainDepthStencilUsage)
|
||||
.width(width).height(height).layer_count_or_depth(1).num_levels(1);
|
||||
swapchainDepthStencil = SDL_CreateGPUTexture(device.getDevice(), ci);
|
||||
}
|
||||
@@ -2149,6 +2167,27 @@ public final class ResourceManager {
|
||||
return swapchainDepthStencil;
|
||||
}
|
||||
|
||||
public long createLogicOpScratch(int sdlFormat, int width, int height) {
|
||||
return createScratchTexture(sdlFormat, width, height, SDL_GPU_TEXTUREUSAGE_SAMPLER);
|
||||
}
|
||||
|
||||
public long createScratchTexture(int sdlFormat, int width, int height, int usage) {
|
||||
final long handle;
|
||||
try (var stack = stackPush()) {
|
||||
final SDL_GPUTextureCreateInfo ci = SDL_GPUTextureCreateInfo.calloc(stack)
|
||||
.type(SDL_GPU_TEXTURETYPE_2D)
|
||||
.format(sdlFormat)
|
||||
.usage(usage)
|
||||
.width(width).height(height).layer_count_or_depth(1).num_levels(1);
|
||||
handle = SDL_CreateGPUTexture(device.getDevice(), ci);
|
||||
}
|
||||
if (handle == 0) {
|
||||
throw new IllegalStateException("Failed to create scratch texture (" + width + "x" + height + " format " + sdlFormat + " usage " + usage + "): " + SDLError.SDL_GetError());
|
||||
}
|
||||
trackTextureHandle(handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
private static final int DUMMY_VBO_SIZE = 16 * 16;
|
||||
public static final int ATTRIB_RING_BLOCKS = 2048 * Device.MAX_FRAMES_IN_FLIGHT;
|
||||
public static final int ATTRIB_RING_CHUNK_BLOCKS = 64;
|
||||
@@ -2308,6 +2347,7 @@ public final class ResourceManager {
|
||||
case GL30.GL_RGB16I -> SDL_GPU_TEXTUREFORMAT_R16G16B16A16_INT;
|
||||
case GL30.GL_RGB32I -> SDL_GPU_TEXTUREFORMAT_R32G32B32A32_INT;
|
||||
case GL30.GL_R11F_G11F_B10F -> SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT;
|
||||
case GL11.GL_RGB10_A2 -> SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM;
|
||||
case GL31.GL_R8_SNORM -> SDL_GPU_TEXTUREFORMAT_R8_SNORM;
|
||||
case GL31.GL_RG8_SNORM -> SDL_GPU_TEXTUREFORMAT_R8G8_SNORM;
|
||||
case GL31.GL_RGB8_SNORM -> SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM; // RGB SNORM -> RGBA SNORM promotion
|
||||
|
||||
@@ -29,6 +29,8 @@ public final class TextureOps {
|
||||
private final FrameManager frameManager;
|
||||
private final ResourceManager resourceManager;
|
||||
private final FBOClearTracker fboClearTracker;
|
||||
private ByteBuffer readbackStaging;
|
||||
private long readbackStagingAddress;
|
||||
|
||||
public TextureOps(Device device, FrameManager frameManager, ResourceManager resourceManager, FBOClearTracker fboClearTracker) {
|
||||
this.device = device;
|
||||
@@ -49,7 +51,7 @@ public final class TextureOps {
|
||||
final long cp = defer ? 0L : frameManager.ensureCopyPass();
|
||||
if (!defer && cp == 0) return false;
|
||||
final ByteBuffer unpacked = PixelOps.applyUnpackPixelStore(src, w, h, srcFormat, srcType, st.pixelStore);
|
||||
final ByteBuffer prepped = PixelOps.prepareUploadBuffer(srcFormat, meta.sdlFormat(), unpacked, w, h);
|
||||
final ByteBuffer prepped = PixelOps.prepareUploadBuffer(srcFormat, srcType, meta.sdlFormat(), unpacked, w, h);
|
||||
try {
|
||||
final boolean handled = defer && resourceManager.enqueueDeferredTextureUpload(st, prepped, texHandle, x, y, w, h, level);
|
||||
if (!handled) {
|
||||
@@ -111,6 +113,35 @@ public final class TextureOps {
|
||||
resourceManager.downloadFromTexture(cb, texHandle, x, y, w, h, level, output);
|
||||
}
|
||||
|
||||
public long ensureReadbackStaging(long bytes) {
|
||||
if (bytes > Integer.MAX_VALUE) throw new IllegalArgumentException("readback of " + bytes + " bytes exceeds the staging limit");
|
||||
if (readbackStaging == null || readbackStaging.capacity() < bytes) {
|
||||
if (readbackStaging != null) MemoryUtil.memFree(readbackStaging);
|
||||
readbackStaging = MemoryUtil.memAlloc((int) Math.max(bytes, 64 * 1024));
|
||||
readbackStagingAddress = MemoryUtil.memAddress(readbackStaging);
|
||||
}
|
||||
return readbackStagingAddress;
|
||||
}
|
||||
|
||||
public ByteBuffer readbackStagingRegion(int offset, int size) {
|
||||
readbackStaging.limit(offset + size);
|
||||
readbackStaging.position(offset);
|
||||
return readbackStaging;
|
||||
}
|
||||
|
||||
public void readbackTextureToStaging(long texHandle, int x, int y, int w, int h, int level, int offset, int size) {
|
||||
readbackTexture(texHandle, x, y, w, h, level, readbackStagingRegion(offset, size));
|
||||
readbackStaging.clear();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
if (readbackStaging != null) {
|
||||
MemoryUtil.memFree(readbackStaging);
|
||||
readbackStaging = null;
|
||||
readbackStagingAddress = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
public void copyTexSubImageImpl(ContextState st, int destGlId, int level, int xoffset, int yoffset, int x, int y, int width, int height) {
|
||||
if (destGlId == 0) return;
|
||||
final long destTex = resourceManager.getTextureHandle(destGlId);
|
||||
@@ -141,7 +172,7 @@ public final class TextureOps {
|
||||
final int h = CopyRectClip.height(clip);
|
||||
|
||||
fboClearTracker.materializePendingClearForTexture(st, srcTex);
|
||||
fboClearTracker.resolveDestinationForWrite(st, destTex, destMeta, level, dx, dy, 0, w, h);
|
||||
fboClearTracker.resolveDestinationForWrite(st, destTex, destMeta, level, dx, dy, 0, w, h, true);
|
||||
|
||||
if (isDepthDest || (srcMeta != null && destMeta != null && srcMeta.sdlFormat() == destMeta.sdlFormat())) {
|
||||
copyTexture(srcTex, sx, sy, destTex, level, dx, dy, w, h);
|
||||
@@ -180,7 +211,7 @@ public final class TextureOps {
|
||||
if (st.pendingSwapchainClear || st.pendingSwapchainDepthClear || st.pendingSwapchainStencilClear) {
|
||||
frameManager.ensureFbo0RenderPass(frameManager.frame(), st);
|
||||
}
|
||||
fboClearTracker.resolveDestinationForWrite(st, destTex, destMeta, level, dx, dy, 0, w, h);
|
||||
fboClearTracker.resolveDestinationForWrite(st, destTex, destMeta, level, dx, dy, 0, w, h, true);
|
||||
|
||||
blitTexture(srcTex, sx, srcFullH - sy - h, w, h, destTex, level, dx, dy, w, h, GL11.GL_NEAREST, SDL_FLIP_VERTICAL);
|
||||
}
|
||||
@@ -190,7 +221,10 @@ public final class TextureOps {
|
||||
}
|
||||
|
||||
public void copyTexture(long srcTex, int srcX, int srcY, long dstTex, int dstLevel, int dstX, int dstY, int w, int h) {
|
||||
final long cp = frameManager.ensureCopyPass();
|
||||
copyTexture(frameManager.ensureCopyPass(), srcTex, srcX, srcY, dstTex, dstLevel, dstX, dstY, w, h);
|
||||
}
|
||||
|
||||
public static void copyTexture(long cp, long srcTex, int srcX, int srcY, long dstTex, int dstLevel, int dstX, int dstY, int w, int h) {
|
||||
if (cp == 0) return;
|
||||
try (var stack = MemoryStack.stackPush()) {
|
||||
final var src = SDL_GPUTextureLocation.calloc(stack).texture(srcTex).x(srcX).y(srcY);
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.FormatMap;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.TextureSamplerState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.LogicOpVariant;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.util.MemoryAccess;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
@@ -112,7 +113,9 @@ public final class SamplerBinder {
|
||||
}
|
||||
|
||||
private void bindStageSamplers(long renderPass, ContextState st, ShaderManager.ProgramObject prog, long fallbackTexture, boolean fragment) {
|
||||
final int raw = fragment ? prog.fragmentResources.numSamplers() : prog.vertexResources.numSamplers();
|
||||
final LogicOpVariant variant = fragment ? st.activeLogicOpVariant : null;
|
||||
final int raw = variant != null ? variant.numSamplers : fragment ? prog.fragmentResources.numSamplers() : prog.vertexResources.numSamplers();
|
||||
final int baseCount = variant != null ? variant.baseSamplers : raw;
|
||||
if (raw > ContextState.MAX_SAMPLERS) {
|
||||
throw new IllegalStateException("program " + st.boundProgram + " has " + raw + " " + (fragment ? "fragment" : "vertex") + " samplers; MAX=" + ContextState.MAX_SAMPLERS);
|
||||
}
|
||||
@@ -123,9 +126,22 @@ public final class SamplerBinder {
|
||||
final long[] lastTex = fragment ? st.lastFragSamplerTex : st.lastVertSamplerTex;
|
||||
final long[] lastSmp = fragment ? st.lastFragSamplerSmp : st.lastVertSamplerSmp;
|
||||
final int lastProg = fragment ? st.lastFragSamplerProgram : st.lastVertSamplerProgram;
|
||||
final boolean programChanged = st.boundProgram != lastProg;
|
||||
final boolean programChanged = st.boundProgram != lastProg || fragment && variant != st.lastFragSamplerVariant;
|
||||
boolean anyChanged = programChanged;
|
||||
for (int i = 0; i < raw; i++) {
|
||||
if (i >= baseCount) {
|
||||
final long dst = st.logicOpScratch[variant.dstLocation[i - baseCount]];
|
||||
final long dstSampler = resourceManager.getOrCreateDefaultSampler();
|
||||
if (programChanged || dst != lastTex[i] || dstSampler != lastSmp[i]) {
|
||||
anyChanged = true;
|
||||
final long elemAddr = bindingsBase + (long) i * SDL_GPUTextureSamplerBinding.SIZEOF;
|
||||
MemoryAccess.putAddress(elemAddr + SDL_GPUTextureSamplerBinding.TEXTURE, dst);
|
||||
MemoryAccess.putAddress(elemAddr + SDL_GPUTextureSamplerBinding.SAMPLER, dstSampler);
|
||||
lastTex[i] = dst;
|
||||
lastSmp[i] = dstSampler;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
final int glUnit = samplerUnits[i];
|
||||
final int glTexId = (glUnit >= 0 && glUnit < st.boundTextures.length) ? st.boundTextures[glUnit] : 0;
|
||||
long texHandle = (glTexId != 0) ? resourceManager.getTextureHandle(glTexId) : 0;
|
||||
@@ -149,6 +165,7 @@ public final class SamplerBinder {
|
||||
if (fragment) {
|
||||
SDL_BindGPUFragmentSamplers(renderPass, 0, bindings);
|
||||
st.lastFragSamplerProgram = st.boundProgram;
|
||||
st.lastFragSamplerVariant = variant;
|
||||
} else {
|
||||
SDL_BindGPUVertexSamplers(renderPass, 0, bindings);
|
||||
st.lastVertSamplerProgram = st.boundProgram;
|
||||
@@ -173,6 +190,7 @@ public final class SamplerBinder {
|
||||
if (fragment) {
|
||||
SDL_BindGPUFragmentSamplers(renderPass, 0, bindings);
|
||||
st.lastFragSamplerProgram = 0;
|
||||
st.lastFragSamplerVariant = null;
|
||||
} else {
|
||||
SDL_BindGPUVertexSamplers(renderPass, 0, bindings);
|
||||
st.lastVertSamplerProgram = 0;
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.shader;
|
||||
|
||||
import static org.lwjgl.sdl.SDLGPU.*;
|
||||
|
||||
public final class LogicOpFormats {
|
||||
|
||||
public static final int OP_COPY = 12;
|
||||
|
||||
private static final int CLASS_RGBA8 = 1;
|
||||
private static final int CLASS_R8 = 2;
|
||||
private static final int CLASS_RG8 = 3;
|
||||
private static final int CLASS_RGBA16 = 4;
|
||||
private static final int CLASS_R16 = 5;
|
||||
private static final int CLASS_RG16 = 6;
|
||||
private static final int CLASS_RGB10A2 = 7;
|
||||
private static final int CLASS_R5G6B5 = 8;
|
||||
private static final int CLASS_RGB5A1 = 9;
|
||||
private static final int CLASS_RGBA4 = 10;
|
||||
private static final int CLASS_A8 = 11;
|
||||
|
||||
private static final int[] MESA_OP = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
|
||||
|
||||
private static final byte[][] BITS = {
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 8, 8, 8, 8 },
|
||||
{ 8, 0, 0, 0 },
|
||||
{ 8, 8, 0, 0 },
|
||||
{ 16, 16, 16, 16 },
|
||||
{ 16, 0, 0, 0 },
|
||||
{ 16, 16, 0, 0 },
|
||||
{ 10, 10, 10, 2 },
|
||||
{ 5, 6, 5, 0 },
|
||||
{ 5, 5, 5, 1 },
|
||||
{ 4, 4, 4, 4 },
|
||||
{ 0, 0, 0, 8 },
|
||||
};
|
||||
|
||||
private LogicOpFormats() {}
|
||||
|
||||
public static int mesaOp(int glOpcode) {
|
||||
return MESA_OP[glOpcode & 0xF];
|
||||
}
|
||||
|
||||
public static int classOf(int sdlTextureFormat) {
|
||||
return switch (sdlTextureFormat) {
|
||||
case SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM -> CLASS_RGBA8;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8_UNORM -> CLASS_R8;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8G8_UNORM -> CLASS_RG8;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM -> CLASS_RGBA16;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16_UNORM -> CLASS_R16;
|
||||
case SDL_GPU_TEXTUREFORMAT_R16G16_UNORM -> CLASS_RG16;
|
||||
case SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM -> CLASS_RGB10A2;
|
||||
case SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM -> CLASS_R5G6B5;
|
||||
case SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM -> CLASS_RGB5A1;
|
||||
case SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM -> CLASS_RGBA4;
|
||||
case SDL_GPU_TEXTUREFORMAT_A8_UNORM -> CLASS_A8;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8_SNORM, SDL_GPU_TEXTUREFORMAT_R8G8_SNORM, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_R16_SNORM, SDL_GPU_TEXTUREFORMAT_R16G16_SNORM, SDL_GPU_TEXTUREFORMAT_R16G16B16A16_SNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_R8_UINT, SDL_GPU_TEXTUREFORMAT_R8G8_UINT, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT,
|
||||
SDL_GPU_TEXTUREFORMAT_R16_UINT, SDL_GPU_TEXTUREFORMAT_R16G16_UINT, SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UINT,
|
||||
SDL_GPU_TEXTUREFORMAT_R32_UINT, SDL_GPU_TEXTUREFORMAT_R32G32_UINT, SDL_GPU_TEXTUREFORMAT_R32G32B32A32_UINT,
|
||||
SDL_GPU_TEXTUREFORMAT_R8_INT, SDL_GPU_TEXTUREFORMAT_R8G8_INT, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_INT,
|
||||
SDL_GPU_TEXTUREFORMAT_R16_INT, SDL_GPU_TEXTUREFORMAT_R16G16_INT, SDL_GPU_TEXTUREFORMAT_R16G16B16A16_INT,
|
||||
SDL_GPU_TEXTUREFORMAT_R32_INT, SDL_GPU_TEXTUREFORMAT_R32G32_INT, SDL_GPU_TEXTUREFORMAT_R32G32B32A32_INT ->
|
||||
throw new UnsupportedOperationException("glLogicOp on snorm/integer color attachment format " + sdlTextureFormat + " is not emulated");
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
public static float channelMax(int cls, int channel) {
|
||||
final int bits = BITS[cls][channel];
|
||||
return bits == 0 ? 0.0f : (float) ((1 << bits) - 1);
|
||||
}
|
||||
|
||||
public static long withClass(long key, int location, int cls) {
|
||||
return key | ((long) cls << (4 + 4 * location));
|
||||
}
|
||||
|
||||
public static int classAt(long key, int location) {
|
||||
return (int) (key >>> (4 + 4 * location)) & 0xF;
|
||||
}
|
||||
|
||||
public static int opOf(long key) {
|
||||
return (int) (key & 0xF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.shader;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GlslTransformUtils;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess.Edit;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess.applyEdits;
|
||||
|
||||
public final class LogicOpLowering {
|
||||
|
||||
public static final int MAX_LOCATIONS = 8;
|
||||
public static final String DST_SAMPLER_PREFIX = "angelica_LogicOpDst";
|
||||
|
||||
private static final String MAIN_RENAME = "angelica_logicOpMain";
|
||||
private static final String SNIPPET = ShaderLoader.getShaderSource("angelica:sdlgpu/logic_op.glsl");
|
||||
|
||||
private record Output(String name, int location, int arrayLen, int vecSize) {}
|
||||
|
||||
private LogicOpLowering() {}
|
||||
|
||||
public static String lower(String preprocessedFragmentSource, long key, int program) {
|
||||
final GLSLParser.Translation_unitContext root = GlslTransformUtils.parseFullQuiet(preprocessedFragmentSource);
|
||||
final List<Edit> edits = new ArrayList<>();
|
||||
final List<Output> outputs = new ArrayList<>();
|
||||
boolean renamed = false;
|
||||
|
||||
for (GLSLParser.External_declarationContext ext : root.external_declaration()) {
|
||||
final GLSLParser.Function_definitionContext fn = ext.function_definition();
|
||||
if (fn != null) {
|
||||
renamed |= renameMain(fn.function_prototype(), edits);
|
||||
continue;
|
||||
}
|
||||
final GLSLParser.DeclarationContext decl = ext.declaration();
|
||||
if (decl == null) continue;
|
||||
if (decl.function_prototype() != null) {
|
||||
renameMain(decl.function_prototype(), edits);
|
||||
continue;
|
||||
}
|
||||
collectOutputs(decl, outputs, program);
|
||||
}
|
||||
if (!renamed) throw new IllegalStateException("glLogicOp lowering: program " + program + " fragment shader has no main()");
|
||||
|
||||
final String[] exprByLoc = new String[MAX_LOCATIONS];
|
||||
final int[] sizeByLoc = new int[MAX_LOCATIONS];
|
||||
resolveLocations(outputs, exprByLoc, sizeByLoc, program);
|
||||
|
||||
final int op = LogicOpFormats.opOf(key);
|
||||
final StringBuilder sb = new StringBuilder(applyEdits(preprocessedFragmentSource, edits));
|
||||
sb.append("\n#define ANGELICA_LOGIC_OP ").append(op).append("u\n");
|
||||
sb.append(SNIPPET).append('\n');
|
||||
for (int loc = 0; loc < MAX_LOCATIONS; loc++) {
|
||||
if (LogicOpFormats.classAt(key, loc) == 0) continue;
|
||||
if (exprByLoc[loc] == null) continue;
|
||||
if (sizeByLoc[loc] == 0) throw new IllegalStateException("glLogicOp lowering: program " + program + " output at location " + loc + " is not a float vector");
|
||||
sb.append("uniform sampler2D ").append(DST_SAMPLER_PREFIX).append(loc).append(";\n");
|
||||
}
|
||||
sb.append("void main() {\n ").append(MAIN_RENAME).append("();\n");
|
||||
for (int loc = 0; loc < MAX_LOCATIONS; loc++) {
|
||||
final int cls = LogicOpFormats.classAt(key, loc);
|
||||
if (cls == 0 || exprByLoc[loc] == null) continue;
|
||||
final String out = exprByLoc[loc];
|
||||
final int n = sizeByLoc[loc];
|
||||
sb.append(" ").append(out).append(" = ");
|
||||
sb.append("angelica_logicOp(").append(widen(out, n)).append(", ").append(DST_SAMPLER_PREFIX).append(loc).append(", vec4(");
|
||||
for (int c = 0; c < 4; c++) {
|
||||
if (c > 0) sb.append(", ");
|
||||
sb.append(LogicOpFormats.channelMax(cls, c));
|
||||
}
|
||||
sb.append("))").append(narrow(n)).append(";\n");
|
||||
}
|
||||
sb.append("}\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static boolean renameMain(GLSLParser.Function_prototypeContext proto, List<Edit> edits) {
|
||||
if (proto == null || proto.IDENTIFIER() == null || !"main".equals(proto.IDENTIFIER().getText())) return false;
|
||||
final Token t = proto.IDENTIFIER().getSymbol();
|
||||
edits.add(new Edit(t.getStartIndex(), t.getStopIndex(), MAIN_RENAME));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void collectOutputs(GLSLParser.DeclarationContext decl, List<Output> outputs, int program) {
|
||||
final GLSLParser.Init_declarator_listContext idl = decl.init_declarator_list();
|
||||
if (idl == null) return;
|
||||
final GLSLParser.Single_declarationContext single = idl.single_declaration();
|
||||
if (single == null || single.fully_specified_type() == null) return;
|
||||
final GLSLParser.Fully_specified_typeContext fst = single.fully_specified_type();
|
||||
if (fst.type_qualifier() == null) return;
|
||||
|
||||
boolean isOut = false;
|
||||
int location = -1;
|
||||
int index = 0;
|
||||
for (GLSLParser.Single_type_qualifierContext stq : fst.type_qualifier().single_type_qualifier()) {
|
||||
if (stq.storage_qualifier() != null && stq.storage_qualifier().OUT() != null) isOut = true;
|
||||
final GLSLParser.Layout_qualifierContext lq = stq.layout_qualifier();
|
||||
if (lq == null) continue;
|
||||
for (GLSLParser.Layout_qualifier_idContext id : lq.layout_qualifier_id_list().layout_qualifier_id()) {
|
||||
if (id.IDENTIFIER() == null || id.constant_expression() == null) continue;
|
||||
final String qname = id.IDENTIFIER().getText();
|
||||
if ("location".equals(qname)) location = parseInt(id.constant_expression().getText(), program);
|
||||
else if ("index".equals(qname)) index = parseInt(id.constant_expression().getText(), program);
|
||||
}
|
||||
}
|
||||
if (!isOut || index != 0) return;
|
||||
|
||||
final GLSLParser.Type_specifierContext ts = fst.type_specifier();
|
||||
final int vecSize = floatVecSize(ts.type_specifier_nonarray().getText());
|
||||
final int typeArrayLen = ts.array_specifier() != null ? arrayLen(ts.array_specifier(), program) : 0;
|
||||
|
||||
final List<GLSLParser.Typeless_declarationContext> names = new ArrayList<>();
|
||||
if (single.typeless_declaration() != null) names.add(single.typeless_declaration());
|
||||
names.addAll(idl.typeless_declaration());
|
||||
int nextLocation = location;
|
||||
for (GLSLParser.Typeless_declarationContext td : names) {
|
||||
final int len = td.array_specifier() != null ? arrayLen(td.array_specifier(), program) : typeArrayLen;
|
||||
outputs.add(new Output(td.IDENTIFIER().getText(), nextLocation, len, vecSize));
|
||||
if (nextLocation >= 0) nextLocation += Math.max(len, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void resolveLocations(List<Output> outputs, String[] exprByLoc, int[] sizeByLoc, int program) {
|
||||
int unlocated = 0;
|
||||
for (Output o : outputs) if (o.location() < 0) unlocated++;
|
||||
if (unlocated > 0 && outputs.size() > 1) {
|
||||
throw new IllegalStateException("glLogicOp lowering: program " + program + " declares " + outputs.size() + " fragment outputs without explicit locations");
|
||||
}
|
||||
for (Output o : outputs) {
|
||||
final int base = Math.max(o.location(), 0);
|
||||
final int count = Math.max(o.arrayLen(), 1);
|
||||
for (int k = 0; k < count; k++) {
|
||||
final int loc = base + k;
|
||||
if (loc >= MAX_LOCATIONS) throw new IllegalStateException("glLogicOp lowering: program " + program + " output " + o.name() + " exceeds location " + (MAX_LOCATIONS - 1));
|
||||
if (exprByLoc[loc] != null) throw new IllegalStateException("glLogicOp lowering: program " + program + " has two outputs at location " + loc);
|
||||
exprByLoc[loc] = o.arrayLen() > 0 ? o.name() + "[" + k + "]" : o.name();
|
||||
sizeByLoc[loc] = o.vecSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int arrayLen(GLSLParser.Array_specifierContext spec, int program) {
|
||||
final List<GLSLParser.DimensionContext> dims = spec.dimension();
|
||||
if (dims.size() != 1 || dims.get(0).constant_expression() == null) {
|
||||
throw new IllegalStateException("glLogicOp lowering: program " + program + " has an unsized or multidimensional fragment output array");
|
||||
}
|
||||
return parseInt(dims.get(0).constant_expression().getText(), program);
|
||||
}
|
||||
|
||||
private static int parseInt(String text, int program) {
|
||||
final String t = text.endsWith("u") || text.endsWith("U") ? text.substring(0, text.length() - 1) : text;
|
||||
try {
|
||||
return Integer.decode(t);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalStateException("glLogicOp lowering: program " + program + " uses non-literal constant '" + text + "' in a fragment output declaration");
|
||||
}
|
||||
}
|
||||
|
||||
private static int floatVecSize(String type) {
|
||||
return switch (type) {
|
||||
case "float" -> 1;
|
||||
case "vec2" -> 2;
|
||||
case "vec3" -> 3;
|
||||
case "vec4" -> 4;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
private static String widen(String expr, int n) {
|
||||
return switch (n) {
|
||||
case 1 -> "vec4(" + expr + ", 0.0, 0.0, 0.0)";
|
||||
case 2 -> "vec4(" + expr + ", 0.0, 0.0)";
|
||||
case 3 -> "vec4(" + expr + ", 0.0)";
|
||||
default -> expr;
|
||||
};
|
||||
}
|
||||
|
||||
private static String narrow(int n) {
|
||||
return switch (n) {
|
||||
case 1 -> ".x";
|
||||
case 2 -> ".xy";
|
||||
case 3 -> ".xyz";
|
||||
default -> "";
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.shader;
|
||||
|
||||
public final class LogicOpVariant {
|
||||
public long sdlShader;
|
||||
public int numSamplers;
|
||||
public int baseSamplers;
|
||||
public byte[] dstLocation;
|
||||
}
|
||||
@@ -431,11 +431,12 @@ public final class ShaderManager {
|
||||
prog.vertexSpirv = copyBuffer(vs.spirv);
|
||||
prog.fragmentSpirv = copyBuffer(fs.spirv);
|
||||
prog.vertexSource = vs.source;
|
||||
prog.fragmentSource = fs.source;
|
||||
prog.vertexReflection = vs.reflection;
|
||||
prog.maxFragOutputLocation = fs.reflection.maxOutputLocation();
|
||||
dumpLinkedSource(program, vs.source, fs.source);
|
||||
|
||||
applyVaryingMatch(prog, vs.reflection, fs.reflection);
|
||||
applyVaryingMatch(prog.fragmentSpirv, vs.reflection, fs.reflection);
|
||||
applyAttribLocationsAndInputMask(prog, vs.reflection);
|
||||
prog.vertexInputSetId = internVertexInputSet(prog.vertexInputMask, prog.vertexInputVecSize, prog.vertexInputBaseType);
|
||||
|
||||
@@ -615,11 +616,11 @@ public final class ShaderManager {
|
||||
return loc;
|
||||
}
|
||||
|
||||
private static void applyVaryingMatch(ProgramObject prog, StageReflection vs, StageReflection fs) {
|
||||
if (vs.vsOutputs().isEmpty() || fs.fsInputs().isEmpty() || prog.fragmentSpirv == null) return;
|
||||
private static void applyVaryingMatch(ByteBuffer fragmentSpirv, StageReflection vs, StageReflection fs) {
|
||||
if (vs.vsOutputs().isEmpty() || fs.fsInputs().isEmpty() || fragmentSpirv == null) return;
|
||||
final HashMap<String, Integer> vsOutLocs = new HashMap<>(vs.vsOutputs().size() * 2);
|
||||
for (VsOutput o : vs.vsOutputs()) vsOutLocs.put(o.name(), o.originalLocation());
|
||||
final IntBuffer fsBuf = prog.fragmentSpirv.asIntBuffer();
|
||||
final IntBuffer fsBuf = fragmentSpirv.asIntBuffer();
|
||||
for (FsInput fi : fs.fsInputs()) {
|
||||
final Integer vsLoc = vsOutLocs.get(fi.name());
|
||||
if (vsLoc == null || vsLoc == fi.originalLocation()) continue;
|
||||
@@ -785,6 +786,79 @@ public final class ShaderManager {
|
||||
}
|
||||
}
|
||||
|
||||
public LogicOpVariant getOrBuildLogicOpVariant(int program, long key) {
|
||||
final ProgramObject prog = programObjects.get(program);
|
||||
if (prog == null || !prog.linked || prog.fragmentSource.isEmpty()) {
|
||||
throw new IllegalStateException("glLogicOp variant: program " + program + " is not a linked graphics program");
|
||||
}
|
||||
final LogicOpVariant hit = prog.logicOpVariants.get(key);
|
||||
if (hit != null) return hit;
|
||||
final LogicOpVariant built = buildLogicOpVariant(program, prog, key);
|
||||
prog.logicOpVariants.put(key, built);
|
||||
return built;
|
||||
}
|
||||
|
||||
private LogicOpVariant buildLogicOpVariant(int program, ProgramObject prog, long key) {
|
||||
final String preprocessed = SpirvCompiler.preprocess(prog.fragmentSource, Shaderc.shaderc_fragment_shader, "logicop" + program, SpirvCompiler.Options.vulkanForced460Core());
|
||||
if (preprocessed == null) throw new IllegalStateException("glLogicOp variant: program " + program + " fragment preprocessing failed");
|
||||
final String src = LogicOpLowering.lower(preprocessed, key, program);
|
||||
if (SystemProperties.dumpShaders()) dumpLogicOpSource(program, key, src);
|
||||
final SpirvCompiler.Result r = SpirvCompiler.compile(src, Shaderc.shaderc_fragment_shader, "logicop" + program, SpirvCompiler.Options.vulkanForced460Core());
|
||||
if (r.spirv() == null) throw new IllegalStateException("glLogicOp variant: program " + program + " failed to compile: " + r.error());
|
||||
try {
|
||||
final GraphicsBindingMap bindingMap = remapSpirvForSDLGPU(r.spirv(), GL20.GL_FRAGMENT_SHADER);
|
||||
final StageReflection refl = reflectStage(r.spirv(), false);
|
||||
final ResourceCounts base = prog.fragmentResources;
|
||||
final ResourceCounts counts = refl.counts();
|
||||
if (counts.numUBOs() != base.numUBOs() || counts.numStorageBuffers() != base.numStorageBuffers() || counts.numStorageTextures() != base.numStorageTextures()
|
||||
|| !Arrays.equals(bindingMap.roStorageTextureGlSlots(), prog.fragmentGraphicsBindingMap.roStorageTextureGlSlots())
|
||||
|| !Arrays.equals(bindingMap.roSsboGlSlots(), prog.fragmentGraphicsBindingMap.roSsboGlSlots())) {
|
||||
throw new IllegalStateException("glLogicOp variant: program " + program + " variant resources " + counts + " differ from base " + base);
|
||||
}
|
||||
final List<String> names = refl.samplerNames();
|
||||
final List<String> baseNames = prog.fragmentSamplerNames;
|
||||
final int extra = names.size() - baseNames.size();
|
||||
if (extra < 0 || !names.subList(0, baseNames.size()).equals(baseNames)) {
|
||||
throw new IllegalStateException("glLogicOp variant: program " + program + " sampler order " + names + " does not extend base " + baseNames);
|
||||
}
|
||||
final byte[] dstLocation = new byte[extra];
|
||||
int prev = -1;
|
||||
for (int i = 0; i < extra; i++) {
|
||||
final String name = names.get(baseNames.size() + i);
|
||||
if (!name.startsWith(LogicOpLowering.DST_SAMPLER_PREFIX)) {
|
||||
throw new IllegalStateException("glLogicOp variant: program " + program + " unexpected appended sampler " + name);
|
||||
}
|
||||
final int loc = Integer.parseInt(name.substring(LogicOpLowering.DST_SAMPLER_PREFIX.length()));
|
||||
if (loc <= prev || LogicOpFormats.classAt(key, loc) == 0) {
|
||||
throw new IllegalStateException("glLogicOp variant: program " + program + " appended samplers " + names.subList(baseNames.size(), names.size()) + " are not in location order");
|
||||
}
|
||||
prev = loc;
|
||||
dstLocation[i] = (byte) loc;
|
||||
}
|
||||
applyVaryingMatch(r.spirv(), prog.vertexReflection, refl);
|
||||
final long shader = createSDLShader(r.spirv(), SDL_GPU_SHADERSTAGE_FRAGMENT, counts.numSamplers(), counts.numUBOs(), counts.numStorageBuffers(), counts.numStorageTextures());
|
||||
if (shader == 0) throw new IllegalStateException("glLogicOp variant: program " + program + " shader creation failed: " + SDLError.SDL_GetError());
|
||||
final LogicOpVariant v = new LogicOpVariant();
|
||||
v.sdlShader = shader;
|
||||
v.numSamplers = names.size();
|
||||
v.baseSamplers = baseNames.size();
|
||||
v.dstLocation = dstLocation;
|
||||
return v;
|
||||
} finally {
|
||||
memFree(r.spirv());
|
||||
}
|
||||
}
|
||||
|
||||
private static void dumpLogicOpSource(int program, long key, String src) {
|
||||
try {
|
||||
final Path dir = Path.of("patched_shaders");
|
||||
Files.createDirectories(dir);
|
||||
Files.writeString(dir.resolve("logicop_" + program + "_" + Long.toHexString(key) + ".fsh"), src);
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to dump glLogicOp variant for program {}: {}", program, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean linkDumpDirCleared;
|
||||
|
||||
private static void dumpLinkedSource(int program, String vertexSrc, String fragmentSrc) {
|
||||
@@ -839,6 +913,10 @@ public final class ShaderManager {
|
||||
}
|
||||
prog.vertexVariants.clear();
|
||||
prog.vertexVariantFailed.clear();
|
||||
for (LogicOpVariant v : prog.logicOpVariants.values()) {
|
||||
if (v.sdlShader != 0) SDL_ReleaseGPUShader(dev, v.sdlShader);
|
||||
}
|
||||
prog.logicOpVariants.clear();
|
||||
if (prog.sdlVertexShader != 0) { SDL_ReleaseGPUShader(dev, prog.sdlVertexShader); prog.sdlVertexShader = 0; }
|
||||
if (prog.sdlFragmentShader != 0) { SDL_ReleaseGPUShader(dev, prog.sdlFragmentShader); prog.sdlFragmentShader = 0; }
|
||||
if (prog.sdlComputePipeline != 0) { SDL_ReleaseGPUComputePipeline(dev, prog.sdlComputePipeline); prog.sdlComputePipeline = 0; }
|
||||
@@ -1745,6 +1823,8 @@ public final class ShaderManager {
|
||||
public StageReflection vertexReflection = StageReflection.EMPTY;
|
||||
public final Long2ObjectOpenHashMap<VertexVariant> vertexVariants = new Long2ObjectOpenHashMap<>();
|
||||
public final LongOpenHashSet vertexVariantFailed = new LongOpenHashSet();
|
||||
public String fragmentSource = "";
|
||||
public final Long2ObjectOpenHashMap<LogicOpVariant> logicOpVariants = new Long2ObjectOpenHashMap<>();
|
||||
public final Object2IntOpenHashMap<String> attribLocationBindings = newLocMap();
|
||||
public final Object2IntOpenHashMap<String> resolvedAttribLocations = newLocMap();
|
||||
public final Object2IntOpenHashMap<String> nameToLocation = newLocMap();
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
vec4 angelica_logicOp(vec4 src, sampler2D dstTex, vec4 maxv) {
|
||||
uvec4 s = uvec4(roundEven(clamp(src, 0.0, 1.0) * maxv));
|
||||
uvec4 d = uvec4(roundEven(clamp(texelFetch(dstTex, ivec2(gl_FragCoord.xy), 0), 0.0, 1.0) * maxv));
|
||||
uvec4 t0 = uvec4(0u - (ANGELICA_LOGIC_OP & 1u));
|
||||
uvec4 t1 = uvec4(0u - ((ANGELICA_LOGIC_OP >> 1u) & 1u));
|
||||
uvec4 t2 = uvec4(0u - ((ANGELICA_LOGIC_OP >> 2u) & 1u));
|
||||
uvec4 t3 = uvec4(0u - ((ANGELICA_LOGIC_OP >> 3u) & 1u));
|
||||
uvec4 r = ((~s & ~d & t0) | (~s & d & t1) | (s & ~d & t2) | (s & d & t3)) & uvec4(maxv);
|
||||
return mix(src, vec4(r) / max(maxv, vec4(1.0)), greaterThan(maxv, vec4(0.0)));
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#version 460 core
|
||||
uniform sampler2D depthSampler;
|
||||
layout(location = 0) out vec4 depthOut;
|
||||
void main() { depthOut = vec4(texelFetch(depthSampler, ivec2(gl_FragCoord.xy), 0).r, 0.0, 0.0, 1.0); }
|
||||
@@ -0,0 +1,4 @@
|
||||
#version 460 core
|
||||
uniform vec4 bitValue;
|
||||
layout(location = 0) out vec4 stencilOut;
|
||||
void main() { stencilOut = bitValue; }
|
||||
+30
@@ -5,6 +5,7 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
@@ -29,6 +30,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
class GlsmSdlAttachmentClearTest {
|
||||
|
||||
private static final int SIZE = GlsmSdlHeadlessRig.SIZE;
|
||||
private static final int RED = 0xFFFF0000;
|
||||
private static final int BLACK = 0xFF000000;
|
||||
|
||||
@BeforeAll
|
||||
static void boot() {
|
||||
@@ -570,4 +573,31 @@ class GlsmSdlAttachmentClearTest {
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
GlsmSdlHeadlessRig.bindTarget();
|
||||
}
|
||||
|
||||
@Test
|
||||
void stencilWrittenLastFramePersists() {
|
||||
GlsmSdlHeadlessRig.bindTarget();
|
||||
GlsmSdlHeadlessRig.writeStencilOneInBottomHalf();
|
||||
|
||||
GlsmSdlHeadlessRig.endFrame();
|
||||
GlsmSdlHeadlessRig.beginFrameAndReset();
|
||||
GlsmSdlHeadlessRig.drawRedWhereStencilIsOne();
|
||||
|
||||
final int[] pixels = GlsmSdlHeadlessRig.readTarget();
|
||||
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, SIZE / 2, 4, RED, "stencil written last frame");
|
||||
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, SIZE / 2, SIZE - 5, BLACK, "outside last frame's stencil");
|
||||
}
|
||||
|
||||
@Test
|
||||
void aSingleAspectClearLeavesTheTextureUndefined() {
|
||||
final int fbo = GlsmSdlHeadlessRig.createDepthStencilFbo();
|
||||
GLStateManager.glDepthMask(true);
|
||||
GLStateManager.glClear(GL11.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
final ResourceManager resourceManager = Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager");
|
||||
final long depthTexture = resourceManager.getFbo(fbo).depthTexture;
|
||||
GlsmSdlHeadlessRig.endFrame();
|
||||
|
||||
assertFalse(resourceManager.isTextureContentDefined(depthTexture), "the stencil aspect was never written, so the first pass must still clear it");
|
||||
}
|
||||
}
|
||||
+22
-20
@@ -155,32 +155,13 @@ class GlsmSdlCopyBlitTest {
|
||||
assertRow(pixels, SIZE - 1, SENTINEL, "last out-of-range row");
|
||||
}
|
||||
|
||||
private static int createSecondaryFbo() {
|
||||
final int color = GLStateManager.glGenTextures();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, color);
|
||||
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, GL11.GL_RGBA8, SIZE, SIZE, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
|
||||
final int depth = GLStateManager.glGenRenderbuffers();
|
||||
GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depth);
|
||||
GLStateManager.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_DEPTH24_STENCIL8, SIZE, SIZE);
|
||||
|
||||
final int fbo = GLStateManager.glGenFramebuffers();
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, fbo);
|
||||
GLStateManager.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, color, 0);
|
||||
GLStateManager.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, depth);
|
||||
return fbo;
|
||||
}
|
||||
|
||||
@Test
|
||||
void blitFramebufferWithColorAndDepthBitsMovesColor() {
|
||||
GlsmSdlHeadlessRig.bindTarget();
|
||||
GlsmSdlHeadlessRig.clearTo(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
GlsmSdlHeadlessRig.solidQuad(0.0f, 1.0f, 0.0f);
|
||||
|
||||
final int destination = createSecondaryFbo();
|
||||
final int destination = GlsmSdlHeadlessRig.createDepthStencilFbo();
|
||||
GLStateManager.glViewport(0, 0, SIZE, SIZE);
|
||||
GLStateManager.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
|
||||
@@ -243,4 +224,25 @@ class GlsmSdlCopyBlitTest {
|
||||
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, SIZE / 2, SIZE / 2 - 4, GREEN, "blitted fbo0 below the split");
|
||||
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, SIZE / 2, SIZE / 2 + 4, RED, "blitted fbo0 above the split");
|
||||
}
|
||||
|
||||
@Test
|
||||
void blitFramebufferStencilSurvivesTheDestinationsFirstDraw() {
|
||||
GlsmSdlHeadlessRig.bindTarget();
|
||||
GlsmSdlHeadlessRig.writeStencilOneInBottomHalf();
|
||||
|
||||
final int destination = GlsmSdlHeadlessRig.createDepthStencilFbo();
|
||||
GLStateManager.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
GlsmSdlHeadlessRig.bindTarget();
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, destination);
|
||||
GLStateManager.glBlitFramebuffer(0, 0, SIZE, SIZE, 0, 0, SIZE, SIZE, GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT, GL11.GL_NEAREST);
|
||||
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, destination);
|
||||
GlsmSdlHeadlessRig.drawRedWhereStencilIsOne();
|
||||
|
||||
final int[] pixels = GlsmSdlHeadlessRig.readTarget();
|
||||
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, SIZE / 2, 4, RED, "stencil carried by the blit");
|
||||
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, SIZE / 2, SIZE - 5, BLUE, "outside the blitted stencil");
|
||||
}
|
||||
}
|
||||
@@ -228,7 +228,7 @@ class GlsmSdlFfpTest {
|
||||
|
||||
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 13, SIZE / 2, 0xFFFF0000, "entity -1 must render red");
|
||||
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 32, SIZE / 2, 0xFF00FF00, "entity 7 must render green");
|
||||
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 51, SIZE / 2, 0xFF0000FF, "entity 42 must render blue");
|
||||
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 51, SIZE / 2, 0xFF0000FF, "entity 50020 must render blue");
|
||||
}
|
||||
|
||||
private static void enableGuiStandardItemLighting() {
|
||||
|
||||
@@ -116,6 +116,44 @@ public final class GlsmSdlHeadlessRig {
|
||||
GLStateManager.glViewport(0, 0, SIZE, SIZE);
|
||||
}
|
||||
|
||||
public static int createDepthStencilFbo() {
|
||||
final int color = GLStateManager.glGenTextures();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, color);
|
||||
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, GL11.GL_RGBA8, SIZE, SIZE, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
|
||||
final int depth = GLStateManager.glGenRenderbuffers();
|
||||
GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depth);
|
||||
GLStateManager.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_DEPTH24_STENCIL8, SIZE, SIZE);
|
||||
|
||||
final int fbo = GLStateManager.glGenFramebuffers();
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, fbo);
|
||||
GLStateManager.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, color, 0);
|
||||
GLStateManager.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, depth);
|
||||
return fbo;
|
||||
}
|
||||
|
||||
public static void writeStencilOneInBottomHalf() {
|
||||
GLStateManager.glClearStencil(0);
|
||||
GLStateManager.glStencilMask(0xFF);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_STENCIL_TEST);
|
||||
GLStateManager.glStencilFunc(GL11.GL_ALWAYS, 1, 0xFF);
|
||||
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE);
|
||||
halfQuad(-1.0f, 0.0f, 0.0f, 1.0f, 0.0f);
|
||||
GLStateManager.glDisable(GL11.GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
public static void drawRedWhereStencilIsOne() {
|
||||
GLStateManager.glEnable(GL11.GL_STENCIL_TEST);
|
||||
GLStateManager.glStencilFunc(GL11.GL_EQUAL, 1, 0xFF);
|
||||
GLStateManager.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
|
||||
solidQuad(1.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glDisable(GL11.GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
public static void clearTo(float r, float g, float b, float a) {
|
||||
GLStateManager.glClearColor(r, g, b, a);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.LogicOpFixture;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.SIZE;
|
||||
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.describe;
|
||||
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.pixelAt;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@Tag("glsm-sdl")
|
||||
@ExtendWith(GlsmSdlFrameExtension.class)
|
||||
class GlsmSdlLogicOpTest {
|
||||
|
||||
private static final String VERT = """
|
||||
#version 330 core
|
||||
layout(location = 0) in vec3 a_Position;
|
||||
void main() {
|
||||
gl_Position = vec4(a_Position, 1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String FRAG_SINGLE = """
|
||||
#version 330 core
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
if (gl_FragCoord.x < 0.0) return;
|
||||
fragColor = vec4(51.0, 153.0, 230.0, 128.0) / 255.0;
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String FRAG_MRT = """
|
||||
#version 330 core
|
||||
layout(location = 0) out vec4 color0;
|
||||
layout(location = 1) out vec4 color1;
|
||||
void main() {
|
||||
color0 = vec4(51.0, 153.0, 230.0, 128.0) / 255.0;
|
||||
color1 = vec4(51.0, 102.0, 204.0, 255.0) / 255.0;
|
||||
}
|
||||
""";
|
||||
|
||||
private static final int[] PROGRAM_SRC = LogicOpFixture.SOURCES[1];
|
||||
|
||||
private static int singleProgram;
|
||||
private static int mrtProgram;
|
||||
|
||||
@BeforeAll
|
||||
static void boot() {
|
||||
GlsmSdlHeadlessRig.boot();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
GLStateManager.glDisable(GL11.GL_COLOR_LOGIC_OP);
|
||||
GLStateManager.glLogicOp(GL11.GL_COPY);
|
||||
GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
GLStateManager.glUseProgram(0);
|
||||
LogicOpFixture.deleteResources();
|
||||
}
|
||||
|
||||
@Test
|
||||
void everyOpMatchesTheBitwiseReference() {
|
||||
for (int op : LogicOpFixture.OPS) {
|
||||
for (int[] src : LogicOpFixture.SOURCES) {
|
||||
GlsmSdlHeadlessRig.beginFrame();
|
||||
GlsmSdlHeadlessRig.bindTarget();
|
||||
LogicOpFixture.clearToDst();
|
||||
LogicOpFixture.drawQuad(op, src);
|
||||
assertCenter(op, src, LogicOpFixture.label(op, src));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
assertCenter(GL11.GL_XOR, src, "blend enabled under XOR");
|
||||
}
|
||||
|
||||
@Test
|
||||
void disablingLogicOpRestoresBaseShaderAndBlending() {
|
||||
final int[] src = LogicOpFixture.SOURCES[1];
|
||||
LogicOpFixture.clearToDst();
|
||||
LogicOpFixture.drawQuad(GL11.GL_XOR, src);
|
||||
GLStateManager.glEnable(GL11.GL_BLEND);
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
|
||||
LogicOpFixture.quad(src);
|
||||
|
||||
final int got = centerPixel();
|
||||
final int r = Math.min(255, LogicOpFixture.expected(GL11.GL_XOR, src[0], LogicOpFixture.DST_R) + src[0]);
|
||||
final int g = Math.min(255, LogicOpFixture.expected(GL11.GL_XOR, src[1], LogicOpFixture.DST_G) + src[1]);
|
||||
final int b = Math.min(255, LogicOpFixture.expected(GL11.GL_XOR, src[2], LogicOpFixture.DST_B) + src[2]);
|
||||
final int a = Math.min(255, LogicOpFixture.expected(GL11.GL_XOR, src[3], LogicOpFixture.DST_A) + src[3]);
|
||||
assertEquals((a << 24) | (r << 16) | (g << 8) | b, got, () -> "additive after XOR: " + describe(got));
|
||||
}
|
||||
|
||||
@Test
|
||||
void userProgramIsLowered() {
|
||||
singleProgram = buildProgram(singleProgram, FRAG_SINGLE);
|
||||
for (int op : new int[] { GL11.GL_OR_REVERSE, GL11.GL_COPY_INVERTED, GL11.GL_OR_INVERTED, GL11.GL_XOR }) {
|
||||
GlsmSdlHeadlessRig.beginFrame();
|
||||
GlsmSdlHeadlessRig.bindTarget();
|
||||
LogicOpFixture.clearToDst();
|
||||
GLStateManager.glUseProgram(singleProgram);
|
||||
LogicOpFixture.drawQuad(op, PROGRAM_SRC);
|
||||
GLStateManager.glUseProgram(0);
|
||||
assertCenter(op, PROGRAM_SRC, "user program " + LogicOpFixture.label(op, PROGRAM_SRC));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void floatAttachmentPassesThroughWhileUnormAttachmentIsLowered() {
|
||||
mrtProgram = buildProgram(mrtProgram, FRAG_MRT);
|
||||
final int unorm = colorTexture(GL11.GL_RGBA8, GL11.GL_UNSIGNED_BYTE);
|
||||
final int half = colorTexture(GL30.GL_RGBA16F, GL11.GL_FLOAT);
|
||||
final int fbo = GLStateManager.glGenFramebuffers();
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, fbo);
|
||||
GLStateManager.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, unorm, 0);
|
||||
GLStateManager.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT1, GL11.GL_TEXTURE_2D, half, 0);
|
||||
final IntBuffer bufs = BufferUtils.createIntBuffer(2);
|
||||
bufs.put(GL30.GL_COLOR_ATTACHMENT0).put(GL30.GL_COLOR_ATTACHMENT1).flip();
|
||||
GLStateManager.glDrawBuffers(bufs);
|
||||
GLStateManager.glViewport(0, 0, SIZE, SIZE);
|
||||
|
||||
GLStateManager.glEnable(GL11.GL_BLEND);
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
|
||||
LogicOpFixture.clearToDst();
|
||||
GLStateManager.glUseProgram(mrtProgram);
|
||||
LogicOpFixture.drawQuad(GL11.GL_OR_REVERSE, PROGRAM_SRC);
|
||||
GLStateManager.glUseProgram(0);
|
||||
GLStateManager.glDisable(GL11.GL_BLEND);
|
||||
GlsmSdlHeadlessRig.endFrame();
|
||||
|
||||
GLStateManager.glReadBuffer(GL30.GL_COLOR_ATTACHMENT0);
|
||||
final ByteBuffer px = BufferUtils.createByteBuffer(4);
|
||||
GLStateManager.glReadPixels(SIZE / 2, SIZE / 2, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, px);
|
||||
assertEquals(LogicOpFixture.expected(GL11.GL_OR_REVERSE, PROGRAM_SRC[0], LogicOpFixture.DST_R), px.get(0) & 0xFF, "unorm red");
|
||||
assertEquals(LogicOpFixture.expected(GL11.GL_OR_REVERSE, PROGRAM_SRC[1], LogicOpFixture.DST_G), px.get(1) & 0xFF, "unorm green");
|
||||
assertEquals(LogicOpFixture.expected(GL11.GL_OR_REVERSE, PROGRAM_SRC[2], LogicOpFixture.DST_B), px.get(2) & 0xFF, "unorm blue");
|
||||
assertEquals(LogicOpFixture.expected(GL11.GL_OR_REVERSE, PROGRAM_SRC[3], LogicOpFixture.DST_A), px.get(3) & 0xFF, "unorm alpha");
|
||||
|
||||
GLStateManager.glReadBuffer(GL30.GL_COLOR_ATTACHMENT1);
|
||||
final ByteBuffer fpx = BufferUtils.createByteBuffer(16).order(ByteOrder.nativeOrder());
|
||||
GLStateManager.glReadPixels(SIZE / 2, SIZE / 2, 1, 1, GL11.GL_RGBA, GL11.GL_FLOAT, fpx);
|
||||
assertEquals(0.2f, fpx.getFloat(0), 1e-3f, "float red passes through unblended");
|
||||
assertEquals(0.4f, fpx.getFloat(4), 1e-3f, "float green passes through unblended");
|
||||
assertEquals(0.8f, fpx.getFloat(8), 1e-3f, "float blue passes through unblended");
|
||||
assertEquals(1.0f, fpx.getFloat(12), 1e-3f, "float alpha passes through unblended");
|
||||
GLStateManager.glReadBuffer(GL30.GL_COLOR_ATTACHMENT0);
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
|
||||
GLStateManager.glDeleteFramebuffers(fbo);
|
||||
GLStateManager.glDeleteTextures(unorm);
|
||||
GLStateManager.glDeleteTextures(half);
|
||||
}
|
||||
|
||||
private static int colorTexture(int internalFormat, int type) {
|
||||
final int id = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, id);
|
||||
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, GL11.GL_RGBA, type, (ByteBuffer) null);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
return id;
|
||||
}
|
||||
|
||||
private static int buildProgram(int cached, String frag) {
|
||||
if (cached != 0) return cached;
|
||||
final int vs = compile(GL20.GL_VERTEX_SHADER, VERT);
|
||||
final int fs = compile(GL20.GL_FRAGMENT_SHADER, frag);
|
||||
final int p = GLStateManager.glCreateProgram();
|
||||
GLStateManager.glAttachShader(p, vs);
|
||||
GLStateManager.glAttachShader(p, fs);
|
||||
GLStateManager.glLinkProgram(p);
|
||||
assertTrue(GLStateManager.glGetProgrami(p, GL20.GL_LINK_STATUS) != 0, "program did not link");
|
||||
return p;
|
||||
}
|
||||
|
||||
private static int compile(int type, String src) {
|
||||
final int s = GLStateManager.glCreateShader(type);
|
||||
GLStateManager.glShaderSource(s, src);
|
||||
GLStateManager.glCompileShader(s);
|
||||
assertTrue(GLStateManager.glGetShaderi(s, GL20.GL_COMPILE_STATUS) != 0, () -> "shader: " + GLStateManager.glGetShaderInfoLog(s));
|
||||
return s;
|
||||
}
|
||||
|
||||
private static int centerPixel() {
|
||||
final int[] pixels = GlsmSdlHeadlessRig.readTarget();
|
||||
return pixelAt(pixels, SIZE, SIZE / 2, SIZE / 2);
|
||||
}
|
||||
|
||||
private static void assertCenter(int op, int[] src, String label) {
|
||||
final int got = centerPixel();
|
||||
final int r = LogicOpFixture.expected(op, src[0], LogicOpFixture.DST_R);
|
||||
final int g = LogicOpFixture.expected(op, src[1], LogicOpFixture.DST_G);
|
||||
final int b = LogicOpFixture.expected(op, src[2], LogicOpFixture.DST_B);
|
||||
final int a = LogicOpFixture.expected(op, src[3], LogicOpFixture.DST_A);
|
||||
assertEquals((a << 24) | (r << 16) | (g << 8) | b, got, () -> label + ": " + describe(got));
|
||||
}
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ReadbackFixture;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SDLGPUGate;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@Tag("glsm-sdl")
|
||||
@ExtendWith(GlsmSdlFrameExtension.class)
|
||||
class GlsmSdlReadbackMatrixTest {
|
||||
|
||||
private static final int FBO0_W = 8;
|
||||
private static final int FBO0_H = 6;
|
||||
|
||||
private static FrameManager frameManager;
|
||||
|
||||
@BeforeAll
|
||||
static void boot() {
|
||||
GlsmSdlHeadlessRig.boot();
|
||||
frameManager = Reflect.get(BackendManager.RENDER_BACKEND, "frameManager");
|
||||
final ResourceManager resourceManager = Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager");
|
||||
frameManager.destroyFinalTarget();
|
||||
frameManager.finalTarget().create(SDLGPUGate.device(), resourceManager, FBO0_W, FBO0_H, resourceManager.mapTextureFormat(GL11.GL_RGBA8));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void dropFinalTarget() {
|
||||
GlsmSdlHeadlessRig.endFrame();
|
||||
if (frameManager != null) frameManager.destroyFinalTarget();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void resetPack() {
|
||||
ReadbackFixture.resetPack();
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@Test
|
||||
void fbo0FloatReadIsBottomUp() {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
GLStateManager.glViewport(0, 0, FBO0_W, FBO0_H);
|
||||
GLStateManager.glClearColor(0f, 0f, 1f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_SCISSOR_TEST);
|
||||
GLStateManager.glScissor(0, 0, FBO0_W, 1);
|
||||
GLStateManager.glClearColor(1f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
|
||||
final ByteBuffer out = BufferUtils.createByteBuffer(FBO0_W * FBO0_H * 16).order(ByteOrder.nativeOrder());
|
||||
GLStateManager.glReadPixels(0, 0, FBO0_W, FBO0_H, GL11.GL_RGBA, GL11.GL_FLOAT, out);
|
||||
assertEquals(1f, out.getFloat(0), "GL row 0 is the scissored bottom row (red)");
|
||||
assertEquals(0f, out.getFloat(8), "GL row 0 blue");
|
||||
final int top = (FBO0_H - 1) * FBO0_W * 16;
|
||||
assertEquals(0f, out.getFloat(top), "top row red");
|
||||
assertEquals(1f, out.getFloat(top + 8), "top row blue");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fbo0ReadAfterBareClearSeesTheClear() {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
GLStateManager.glViewport(0, 0, FBO0_W, FBO0_H);
|
||||
GLStateManager.glClearColor(0.2f, 0.4f, 0.6f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
final ByteBuffer out = BufferUtils.createByteBuffer(FBO0_W * FBO0_H * 4);
|
||||
GLStateManager.glReadPixels(0, 0, FBO0_W, FBO0_H, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, out);
|
||||
assertEquals(51, out.get(0) & 0xFF, "red after bare clear");
|
||||
assertEquals(102, out.get(1) & 0xFF, "green after bare clear");
|
||||
assertEquals(153, out.get(2) & 0xFF, "blue after bare clear");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fbo0DepthAndStencilAreBottomUp() {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
GLStateManager.glViewport(0, 0, FBO0_W, FBO0_H);
|
||||
GLStateManager.glDepthMask(true);
|
||||
GLStateManager.glStencilMask(0xFF);
|
||||
GLStateManager.glClearDepth(0.75);
|
||||
GLStateManager.glClearStencil(0x11);
|
||||
GLStateManager.glClear(GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_SCISSOR_TEST);
|
||||
GLStateManager.glScissor(0, 0, FBO0_W, 1);
|
||||
GLStateManager.glClearDepth(0.25);
|
||||
GLStateManager.glClearStencil(0x22);
|
||||
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 = BufferUtils.createByteBuffer(FBO0_W * FBO0_H * 4).order(ByteOrder.nativeOrder());
|
||||
GLStateManager.glReadPixels(0, 0, FBO0_W, FBO0_H, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, depth);
|
||||
assertEquals(0.25f, depth.getFloat(0), 1e-6f, "fbo0 depth bottom row");
|
||||
assertEquals(0.75f, depth.getFloat((FBO0_H - 1) * FBO0_W * 4), 1e-6f, "fbo0 depth top row");
|
||||
|
||||
final ByteBuffer stencil = BufferUtils.createByteBuffer(FBO0_W * FBO0_H);
|
||||
GLStateManager.glReadPixels(0, 0, FBO0_W, FBO0_H, GL11.GL_STENCIL_INDEX, GL11.GL_UNSIGNED_BYTE, stencil);
|
||||
assertEquals(0x22, stencil.get(0) & 0xFF, "fbo0 stencil bottom row");
|
||||
assertEquals(0x11, stencil.get((FBO0_H - 1) * FBO0_W) & 0xFF, "fbo0 stencil top row");
|
||||
}
|
||||
}
|
||||
+1
@@ -30,6 +30,7 @@ class GlsmSdlSplashTargetTest {
|
||||
final OffscreenTarget target = createSplashTarget(backend);
|
||||
Reflect.set(backend, "splashTarget", target);
|
||||
try {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
TestThreads.run("SplashTarget-Seed-Thread", () -> {
|
||||
Reflect.invoke(backend, "enterContext", new Class<?>[]{boolean.class}, true);
|
||||
GlsmSdlHeadlessRig.solidQuad(0f, 1f, 0f);
|
||||
|
||||
+6
-6
@@ -74,16 +74,16 @@ class ClearConsumptionTest {
|
||||
|
||||
@Test
|
||||
void depthAndStencilAreConsumedIndependently() {
|
||||
final ResourceManager rm = rm();
|
||||
final ContextState st = new ContextState();
|
||||
FBOClearTracker.recordPendingDepthClear(st, DEPTH, 1f);
|
||||
FBOClearTracker.recordPendingStencilClear(st, DEPTH, 0);
|
||||
|
||||
consumeClears(rm(), st, fbo(SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT), DUMMY, 0, true, true, true);
|
||||
consumeClears(rm, st, fbo(SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT), DUMMY, 0, true, true, true);
|
||||
|
||||
assertFalse(st.pendingDepthTextures.contains(DEPTH));
|
||||
assertFalse(st.pendingStencilTextures.contains(DEPTH));
|
||||
assertTrue(st.clearedTexturesThisFrame.contains(DEPTH));
|
||||
assertTrue(st.clearedStencilTexturesThisFrame.contains(DEPTH));
|
||||
assertTrue(rm.isTextureContentDefined(DEPTH), "the clear pass defines the depth-stencil contents");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -96,17 +96,17 @@ class ClearConsumptionTest {
|
||||
|
||||
assertFalse(st.pendingDepthTextures.contains(DEPTH));
|
||||
assertTrue(st.pendingStencilTextures.contains(DEPTH), "the format has no stencil aspect to clear");
|
||||
assertFalse(st.clearedStencilTexturesThisFrame.contains(DEPTH));
|
||||
}
|
||||
|
||||
@Test
|
||||
void anUnclearedDepthTargetIsStillConsumed() {
|
||||
final ResourceManager rm = rm();
|
||||
final ContextState st = new ContextState();
|
||||
FBOClearTracker.recordPendingDepthClear(st, DEPTH, 1f);
|
||||
|
||||
consumeClears(rm(), st, fbo(SDL_GPU_TEXTUREFORMAT_D24_UNORM), DUMMY, 0, false, false, false);
|
||||
consumeClears(rm, st, fbo(SDL_GPU_TEXTUREFORMAT_D24_UNORM), DUMMY, 0, false, false, false);
|
||||
|
||||
assertFalse(st.pendingDepthTextures.contains(DEPTH));
|
||||
assertFalse(st.clearedTexturesThisFrame.contains(DEPTH), "no clear was baked, so nothing was cleared");
|
||||
assertFalse(rm.isTextureContentDefined(DEPTH), "no clear was baked, so nothing was defined");
|
||||
}
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.pipeline;
|
||||
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
|
||||
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.resource.TextureOps;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.util.MemoryAccess;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.sdl.SDL_GPUDepthStencilTargetInfo;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.lwjgl.sdl.SDLGPU.SDL_GPU_LOADOP_LOAD;
|
||||
import static org.lwjgl.sdl.SDLGPU.SDL_GPU_STOREOP_STORE;
|
||||
import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
|
||||
import static org.lwjgl.system.MemoryStack.stackPush;
|
||||
|
||||
@Tag("glsm-sdl")
|
||||
@Timeout(120)
|
||||
class DepthStencilReadbackTest {
|
||||
|
||||
private static final int W = 24;
|
||||
private static final int H = 16;
|
||||
private static final int RX = 4;
|
||||
private static final int RY = 2;
|
||||
private static final int RW = 8;
|
||||
private static final int RH = 5;
|
||||
|
||||
private static SdlTestRig rig;
|
||||
private static FrameManager frameManager;
|
||||
private static ShaderManager sm;
|
||||
private static PipelineStore store;
|
||||
private static FBOClearTracker clearTracker;
|
||||
private static TextureOps textureOps;
|
||||
private static AttachmentClear attachmentClear;
|
||||
private static DepthStencilReadback readback;
|
||||
private static ContextState st;
|
||||
private static long depthTexture;
|
||||
private static int depthFormat;
|
||||
|
||||
@BeforeAll
|
||||
static void setUp() throws Exception {
|
||||
rig = SdlTestRig.acquireRealDevice();
|
||||
frameManager = rig.frameManager;
|
||||
rig.resourceManager.cachePreferredDepthFormats();
|
||||
sm = new ShaderManager(rig.device);
|
||||
store = new PipelineStore(rig.device);
|
||||
clearTracker = new FBOClearTracker(frameManager, rig.resourceManager, sm);
|
||||
textureOps = new TextureOps(rig.device, frameManager, rig.resourceManager, clearTracker);
|
||||
attachmentClear = new AttachmentClear(frameManager, store, sm);
|
||||
readback = new DepthStencilReadback(frameManager, rig.resourceManager, store, sm, clearTracker);
|
||||
st = new ContextState();
|
||||
|
||||
rig.resourceManager.createTexture(9801, GL11.GL_TEXTURE_2D, GL30.GL_DEPTH24_STENCIL8, W, H, 1, 1);
|
||||
depthTexture = rig.resourceManager.ensureTextureUsage(9801, SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET);
|
||||
assertNotEquals(0L, depthTexture, "depth+stencil texture creation failed");
|
||||
depthFormat = rig.resourceManager.getTextureMeta(9801).sdlFormat();
|
||||
assertTrue(PixelOps.isDepthStencilFormat(depthFormat), "test needs a packed depth+stencil format");
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDown() {
|
||||
if (frameManager != null && frameManager.isFrameActive()) frameManager.endFrame();
|
||||
if (readback != null) {
|
||||
readback.release(st);
|
||||
readback.shutdown();
|
||||
}
|
||||
if (store != null) store.shutdown();
|
||||
SdlTestRig.releaseRealDevice();
|
||||
}
|
||||
|
||||
private static void clearRegion(float depth, int stencil) {
|
||||
final FboState fbo = new FboState();
|
||||
fbo.drawBuffers = new int[0];
|
||||
fbo.cachedColorFormats = new int[0];
|
||||
fbo.depthTexture = depthTexture;
|
||||
fbo.depthFormat = depthFormat;
|
||||
fbo.width = W;
|
||||
fbo.height = H;
|
||||
st.scissorEnabled = true;
|
||||
st.scissorX = RX;
|
||||
st.scissorY = RY;
|
||||
st.scissorW = RW;
|
||||
st.scissorH = RH;
|
||||
st.depthClearValue = depth;
|
||||
st.stencilClearValue = stencil;
|
||||
st.pipeline.stencilWriteMask = 0xFF;
|
||||
try (MemoryStack stack = stackPush()) {
|
||||
final SDL_GPUDepthStencilTargetInfo ds = SDL_GPUDepthStencilTargetInfo.calloc(stack);
|
||||
final long addr = ds.address();
|
||||
MemoryAccess.putAddress(addr + SDL_GPUDepthStencilTargetInfo.TEXTURE, depthTexture);
|
||||
MemoryAccess.putInt(addr + SDL_GPUDepthStencilTargetInfo.LOAD_OP, SDL_GPU_LOADOP_LOAD);
|
||||
MemoryAccess.putInt(addr + SDL_GPUDepthStencilTargetInfo.STORE_OP, SDL_GPU_STOREOP_STORE);
|
||||
MemoryAccess.putInt(addr + SDL_GPUDepthStencilTargetInfo.STENCIL_LOAD_OP, SDL_GPU_LOADOP_LOAD);
|
||||
MemoryAccess.putInt(addr + SDL_GPUDepthStencilTargetInfo.STENCIL_STORE_OP, SDL_GPU_STOREOP_STORE);
|
||||
frameManager.beginRenderPass(null, ds);
|
||||
assertTrue(attachmentClear.clearInPass(st, frameManager.frame(), attachmentClear.forFbo(fbo), false, true, true), "scissored clear failed");
|
||||
frameManager.endRenderPassIfActive();
|
||||
}
|
||||
st.scissorEnabled = false;
|
||||
}
|
||||
|
||||
private static ByteBuffer download(long scratch, int bytesPerTexel) {
|
||||
frameManager.submitMidFrame();
|
||||
final ByteBuffer out = MemoryUtil.memAlloc(W * H * bytesPerTexel);
|
||||
textureOps.readbackTexture(scratch, 0, 0, W, H, 0, out);
|
||||
out.rewind();
|
||||
return out;
|
||||
}
|
||||
|
||||
private static boolean inRegion(int x, int y) {
|
||||
return x >= RX && x < RX + RW && y >= RY && y < RY + RH;
|
||||
}
|
||||
|
||||
@Test
|
||||
void readsPendingClearThenScissoredRegion() {
|
||||
frameManager.beginFrame();
|
||||
FBOClearTracker.recordPendingDepthClear(st, depthTexture, 0.25f);
|
||||
FBOClearTracker.recordPendingStencilClear(st, depthTexture, 0xA5);
|
||||
|
||||
final long depthScratch = readback.renderDepth(st, depthTexture, depthFormat, W, H);
|
||||
assertNotEquals(0L, depthScratch, "depth readback pass failed");
|
||||
final ByteBuffer d0 = download(depthScratch, 4);
|
||||
try {
|
||||
for (int i = 0; i < W * H; i++) assertEquals(0.25f, d0.getFloat(i * 4), 1e-6f, "pending depth clear at texel " + i);
|
||||
} finally {
|
||||
MemoryUtil.memFree(d0);
|
||||
}
|
||||
|
||||
clearRegion(0.75f, 0x3C);
|
||||
|
||||
final long stencilScratch = readback.renderStencil(st, depthTexture, depthFormat, W, H);
|
||||
assertNotEquals(0L, stencilScratch, "stencil readback pass failed");
|
||||
final ByteBuffer s = download(stencilScratch, 1);
|
||||
try {
|
||||
for (int y = 0; y < H; y++) {
|
||||
for (int x = 0; x < W; x++) {
|
||||
assertEquals(inRegion(x, y) ? 0x3C : 0xA5, s.get(y * W + x) & 0xFF, "stencil at x=" + x + " y=" + y);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
MemoryUtil.memFree(s);
|
||||
}
|
||||
|
||||
assertEquals(depthScratch, readback.renderDepth(st, depthTexture, depthFormat, W, H), "same-size readback must reuse the scratch");
|
||||
final ByteBuffer d1 = download(depthScratch, 4);
|
||||
try {
|
||||
for (int y = 0; y < H; y++) {
|
||||
for (int x = 0; x < W; x++) {
|
||||
assertEquals(inRegion(x, y) ? 0.75f : 0.25f, d1.getFloat((y * W + x) * 4), 1e-6f, "depth at x=" + x + " y=" + y);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
MemoryUtil.memFree(d1);
|
||||
}
|
||||
|
||||
final long stencilAgain = readback.renderStencil(st, depthTexture, depthFormat, W, H);
|
||||
final ByteBuffer s2 = download(stencilAgain, 1);
|
||||
try {
|
||||
assertEquals(0x3C, s2.get(RY * W + RX) & 0xFF, "stencil must survive the readback passes");
|
||||
assertEquals(0xA5, s2.get(0) & 0xFF, "stencil must survive the readback passes");
|
||||
} finally {
|
||||
MemoryUtil.memFree(s2);
|
||||
}
|
||||
frameManager.endFrame();
|
||||
}
|
||||
}
|
||||
+7
-51
@@ -55,68 +55,24 @@ class BgraStorageChannelTest {
|
||||
assertArrayEquals(new byte[]{3, 2, 1, 4}, toArray(p, 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
void readback_rgbaStorage_rgbaRequest_passesThrough() {
|
||||
final ByteBuffer p = pixel(0x11, 0x22, 0x33, 0x44);
|
||||
PixelOps.postProcessReadback(p, 1, 1, GL11.GL_RGBA, RGBA, false);
|
||||
assertArrayEquals(new byte[]{0x11, 0x22, 0x33, 0x44}, toArray(p, 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
void readback_rgbaStorage_bgraRequest_swaps() {
|
||||
final ByteBuffer p = pixel(0x11, 0x22, 0x33, 0x44);
|
||||
PixelOps.postProcessReadback(p, 1, 1, GL12.GL_BGRA, RGBA, false);
|
||||
assertArrayEquals(new byte[]{0x33, 0x22, 0x11, 0x44}, toArray(p, 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
void readback_bgraStorage_bgraRequest_passesThrough() {
|
||||
final ByteBuffer p = pixel(0x11, 0x22, 0x33, 0x44);
|
||||
PixelOps.postProcessReadback(p, 1, 1, GL12.GL_BGRA, BGRA, false);
|
||||
assertArrayEquals(new byte[]{0x11, 0x22, 0x33, 0x44}, toArray(p, 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
void readback_bgraStorage_rgbaRequest_swaps() {
|
||||
final ByteBuffer p = pixel(0x11, 0x22, 0x33, 0x44);
|
||||
PixelOps.postProcessReadback(p, 1, 1, GL11.GL_RGBA, BGRA, false);
|
||||
assertArrayEquals(new byte[]{0x33, 0x22, 0x11, 0x44}, toArray(p, 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
void readback_jmAtlasWaterRoundTrip() {
|
||||
final ByteBuffer p = pixel(223, 164, 64, 255); // BGRA storage of (R=64,G=164,B=223,A=255)
|
||||
PixelOps.postProcessReadback(p, 1, 1, GL12.GL_BGRA, BGRA, false);
|
||||
assertArrayEquals(new byte[]{(byte) 223, (byte) 164, (byte) 64, (byte) 255}, toArray(p, 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
void readback_flipRowsAppliesBeforeChannelSwap() {
|
||||
final ByteBuffer p = ByteBuffer.allocateDirect(8).order(ByteOrder.nativeOrder());
|
||||
p.put(new byte[]{(byte) 0xAA, 0x01, 0x02, 0x03, (byte) 0xBB, 0x10, 0x20, 0x30}).rewind();
|
||||
PixelOps.postProcessReadback(p, 1, 2, GL12.GL_BGRA, RGBA, true);
|
||||
|
||||
assertArrayEquals(new byte[]{0x20, 0x10, (byte) 0xBB, 0x30, 0x02, 0x01, (byte) 0xAA, 0x03}, toArray(p, 8));
|
||||
}
|
||||
|
||||
@Test
|
||||
void upload_rgbaSrc_rgbaStorage_passesThrough() {
|
||||
final ByteBuffer src = pixel(0x11, 0x22, 0x33, 0x44);
|
||||
final ByteBuffer out = PixelOps.prepareUploadBuffer(GL11.GL_RGBA, RGBA, src, 1, 1);
|
||||
final ByteBuffer out = PixelOps.prepareUploadBuffer(GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, RGBA, src, 1, 1);
|
||||
assertSame(src, out, "no transform expected");
|
||||
}
|
||||
|
||||
@Test
|
||||
void upload_bgraSrc_bgraStorage_passesThrough() {
|
||||
final ByteBuffer src = pixel(0x11, 0x22, 0x33, 0x44);
|
||||
final ByteBuffer out = PixelOps.prepareUploadBuffer(GL12.GL_BGRA, BGRA, src, 1, 1);
|
||||
final ByteBuffer out = PixelOps.prepareUploadBuffer(GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, BGRA, src, 1, 1);
|
||||
assertSame(src, out);
|
||||
}
|
||||
|
||||
@Test
|
||||
void upload_rgbaSrc_bgraStorage_swaps() {
|
||||
final ByteBuffer src = pixel(0x11, 0x22, 0x33, 0x44);
|
||||
final ByteBuffer out = PixelOps.prepareUploadBuffer(GL11.GL_RGBA, BGRA, src, 1, 1);
|
||||
final ByteBuffer out = PixelOps.prepareUploadBuffer(GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, BGRA, src, 1, 1);
|
||||
try {
|
||||
assertNotSame(src, out);
|
||||
assertArrayEquals(new byte[]{0x33, 0x22, 0x11, 0x44}, toArray(out, 4));
|
||||
@@ -128,7 +84,7 @@ class BgraStorageChannelTest {
|
||||
@Test
|
||||
void upload_bgraSrc_rgbaStorage_swaps() {
|
||||
final ByteBuffer src = pixel(0x11, 0x22, 0x33, 0x44);
|
||||
final ByteBuffer out = PixelOps.prepareUploadBuffer(GL12.GL_BGRA, RGBA, src, 1, 1);
|
||||
final ByteBuffer out = PixelOps.prepareUploadBuffer(GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, RGBA, src, 1, 1);
|
||||
try {
|
||||
assertNotSame(src, out);
|
||||
assertArrayEquals(new byte[]{0x33, 0x22, 0x11, 0x44}, toArray(out, 4));
|
||||
@@ -141,7 +97,7 @@ class BgraStorageChannelTest {
|
||||
void upload_luminanceSrc_rgbaStorage_expandsOnly() {
|
||||
final ByteBuffer src = ByteBuffer.allocateDirect(1).order(ByteOrder.nativeOrder());
|
||||
src.put((byte) 0x77).rewind();
|
||||
final ByteBuffer out = PixelOps.prepareUploadBuffer(GL11.GL_LUMINANCE, RGBA, src, 1, 1);
|
||||
final ByteBuffer out = PixelOps.prepareUploadBuffer(GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE, RGBA, src, 1, 1);
|
||||
try {
|
||||
assertNotSame(src, out);
|
||||
assertArrayEquals(new byte[]{0x77, 0x77, 0x77, (byte) 0xFF}, toArray(out, 4));
|
||||
@@ -154,7 +110,7 @@ class BgraStorageChannelTest {
|
||||
void upload_luminanceSrc_bgraStorage_expandsAndSwaps() {
|
||||
final ByteBuffer src = ByteBuffer.allocateDirect(1).order(ByteOrder.nativeOrder());
|
||||
src.put((byte) 0x77).rewind();
|
||||
final ByteBuffer out = PixelOps.prepareUploadBuffer(GL11.GL_LUMINANCE, BGRA, src, 1, 1);
|
||||
final ByteBuffer out = PixelOps.prepareUploadBuffer(GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE, BGRA, src, 1, 1);
|
||||
try {
|
||||
assertNotSame(src, out);
|
||||
assertArrayEquals(new byte[]{0x77, 0x77, 0x77, (byte) 0xFF}, toArray(out, 4));
|
||||
@@ -167,7 +123,7 @@ class BgraStorageChannelTest {
|
||||
void upload_luminanceAlphaSrc_bgraStorage_expandsAndSwaps() {
|
||||
final ByteBuffer src = ByteBuffer.allocateDirect(2).order(ByteOrder.nativeOrder());
|
||||
src.put((byte) 0x42).put((byte) 0x88).rewind();
|
||||
final ByteBuffer out = PixelOps.prepareUploadBuffer(GL11.GL_LUMINANCE_ALPHA, BGRA, src, 1, 1);
|
||||
final ByteBuffer out = PixelOps.prepareUploadBuffer(GL11.GL_LUMINANCE_ALPHA, GL11.GL_UNSIGNED_BYTE, BGRA, src, 1, 1);
|
||||
try {
|
||||
assertNotSame(src, out);
|
||||
assertArrayEquals(new byte[]{0x42, 0x42, 0x42, (byte) 0x88}, toArray(out, 4));
|
||||
|
||||
+18
-17
@@ -29,32 +29,34 @@ class FullCoverageClearDiscardTest {
|
||||
final ContextState st = new ContextState();
|
||||
FBOClearTracker.recordPendingColorClear(st, HANDLE, 1f, 0f, 0f, 1f);
|
||||
|
||||
assertTrue(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta()));
|
||||
assertTrue(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta(), true));
|
||||
assertFalse(st.pendingColorTextures.contains(HANDLE), "pending clear must be dropped, not left queued");
|
||||
assertFalse(st.pendingColorValues.containsKey(HANDLE));
|
||||
assertTrue(st.clearedTexturesThisFrame.contains(HANDLE), "the overwrite counts as the clear");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fullTargetColorBlitDefinesTheContents() {
|
||||
void copyWithNoPendingClearDefinesTheDestination() {
|
||||
final ContextState st = new ContextState();
|
||||
FBOClearTracker.recordPendingColorClear(st, HANDLE, 1f, 0f, 0f, 1f);
|
||||
|
||||
final SdlTestRig rig = SdlTestRig.create();
|
||||
final FBOClearTracker tracker = new FBOClearTracker(rig.frameManager, rig.resourceManager, null);
|
||||
assertTrue(tracker.discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta()));
|
||||
tracker.resolveDestinationForWrite(st, HANDLE, meta(), 0, 0, 0, 0, W, H, true);
|
||||
|
||||
assertTrue(rig.resourceManager.isTextureContentDefined(HANDLE), "the overwrite defines the contents, so a later frame must not clear it back");
|
||||
assertTrue(rig.resourceManager.isTextureContentDefined(HANDLE), "a copy is a write, so the next pass must load it, not clear it");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fullTargetDepthBlitDropsThePendingClear() {
|
||||
void depthStencilClearsDropOnlyForAnAllAspectOverwrite() {
|
||||
final ContextState st = new ContextState();
|
||||
FBOClearTracker.recordPendingDepthClear(st, HANDLE, 1f);
|
||||
FBOClearTracker.recordPendingStencilClear(st, HANDLE, 0);
|
||||
final FBOClearTracker tracker = tracker();
|
||||
|
||||
assertTrue(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta()));
|
||||
assertFalse(st.pendingDepthTextures.contains(HANDLE));
|
||||
assertTrue(st.clearedTexturesThisFrame.contains(HANDLE));
|
||||
assertFalse(tracker.discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta(), false));
|
||||
assertTrue(st.pendingDepthTextures.contains(HANDLE) && st.pendingStencilTextures.contains(HANDLE), "a single-aspect overwrite must materialize, not drop, the other aspect's clear");
|
||||
|
||||
assertTrue(tracker.discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta(), true));
|
||||
assertFalse(st.pendingDepthTextures.contains(HANDLE) || st.pendingStencilTextures.contains(HANDLE));
|
||||
assertFalse(st.pendingDepthValues.containsKey(HANDLE) || st.pendingStencilValues.containsKey(HANDLE));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -69,7 +71,7 @@ class FullCoverageClearDiscardTest {
|
||||
for (int[] c : partial) {
|
||||
final ContextState st = new ContextState();
|
||||
FBOClearTracker.recordPendingColorClear(st, HANDLE, 1f, 0f, 0f, 1f);
|
||||
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, c[0], c[1], c[2], c[3], c[4], meta()),
|
||||
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, c[0], c[1], c[2], c[3], c[4], meta(), true),
|
||||
"x=" + c[0] + " y=" + c[1] + " level=" + c[2] + " w=" + c[3] + " h=" + c[4]);
|
||||
assertTrue(st.pendingColorTextures.contains(HANDLE), "the clear must survive a partial overwrite");
|
||||
}
|
||||
@@ -80,7 +82,7 @@ class FullCoverageClearDiscardTest {
|
||||
final ContextState st = new ContextState();
|
||||
FBOClearTracker.recordPendingColorClear(st, HANDLE, 1f, 0f, 0f, 1f);
|
||||
final ResourceManager.TextureMeta volume = new ResourceManager.TextureMeta(0, 0, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, W, H, 4, 1, 0);
|
||||
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, volume),
|
||||
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, volume, true),
|
||||
"one slice of a volume is not full coverage");
|
||||
assertTrue(st.pendingColorTextures.contains(HANDLE));
|
||||
}
|
||||
@@ -88,15 +90,14 @@ class FullCoverageClearDiscardTest {
|
||||
@Test
|
||||
void noPendingClearIsNotAnOverwriteClaim() {
|
||||
final ContextState st = new ContextState();
|
||||
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta()));
|
||||
assertFalse(st.clearedTexturesThisFrame.contains(HANDLE));
|
||||
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta(), true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingMetaFallsBackToMaterializing() {
|
||||
final ContextState st = new ContextState();
|
||||
FBOClearTracker.recordPendingColorClear(st, HANDLE, 1f, 0f, 0f, 1f);
|
||||
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, null));
|
||||
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, null, true));
|
||||
assertTrue(st.pendingColorTextures.contains(HANDLE));
|
||||
}
|
||||
|
||||
@@ -107,7 +108,7 @@ class FullCoverageClearDiscardTest {
|
||||
FBOClearTracker.snapshotFlushGenerations(st);
|
||||
final int before = st.pendingMutationGen;
|
||||
|
||||
assertTrue(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta()));
|
||||
assertTrue(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta(), true));
|
||||
assertEquals(before + 1, st.pendingMutationGen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,587 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.resource;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
import static org.lwjgl.sdl.SDLGPU.*;
|
||||
|
||||
class PixelPackTest {
|
||||
|
||||
private static final int SENTINEL = 0xEE;
|
||||
|
||||
@BeforeEach
|
||||
void littleEndianHost() {
|
||||
assumeTrue(ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
private static byte[] b(int... v) {
|
||||
final byte[] out = new byte[v.length];
|
||||
for (int i = 0; i < v.length; i++) out[i] = (byte) v[i];
|
||||
return out;
|
||||
}
|
||||
|
||||
private static byte[] le16(int... v) {
|
||||
final byte[] out = new byte[v.length * 2];
|
||||
for (int i = 0; i < v.length; i++) {
|
||||
out[i * 2] = (byte) v[i];
|
||||
out[i * 2 + 1] = (byte) (v[i] >>> 8);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static byte[] le32(int... v) {
|
||||
final byte[] out = new byte[v.length * 4];
|
||||
for (int i = 0; i < v.length; i++) {
|
||||
out[i * 4] = (byte) v[i];
|
||||
out[i * 4 + 1] = (byte) (v[i] >>> 8);
|
||||
out[i * 4 + 2] = (byte) (v[i] >>> 16);
|
||||
out[i * 4 + 3] = (byte) (v[i] >>> 24);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static byte[] f32(float... v) {
|
||||
final int[] bits = new int[v.length];
|
||||
for (int i = 0; i < v.length; i++) bits[i] = Float.floatToRawIntBits(v[i]);
|
||||
return le32(bits);
|
||||
}
|
||||
|
||||
private static ByteBuffer direct(byte[] content, int size) {
|
||||
final ByteBuffer buf = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
|
||||
for (int i = 0; i < size; i++) buf.put(i, (byte) SENTINEL);
|
||||
for (int i = 0; i < content.length; i++) buf.put(i, content[i]);
|
||||
return buf;
|
||||
}
|
||||
|
||||
private static byte[] read(ByteBuffer buf, int len) {
|
||||
final byte[] out = new byte[len];
|
||||
for (int i = 0; i < len; i++) out[i] = buf.get(i);
|
||||
return out;
|
||||
}
|
||||
|
||||
private static PackState ps(int alignment) {
|
||||
final PackState ps = new PackState();
|
||||
ps.alignment = alignment;
|
||||
return ps;
|
||||
}
|
||||
|
||||
private static byte[] pack1(int sdlFormat, byte[] texel, boolean readPixels, int format, int type) {
|
||||
final ByteBuffer src = direct(texel, texel.length);
|
||||
final int outSize = PixelOps.glPixelSize(format, type);
|
||||
final ByteBuffer dst = direct(new byte[0], outSize + 8);
|
||||
PixelPack.packColor(MemoryUtil.memAddress(src), sdlFormat, storageBase(sdlFormat), texel.length, 1, 1, false, readPixels, format, type, ps(1), MemoryUtil.memAddress(dst));
|
||||
for (int i = outSize; i < outSize + 8; i++) assertEquals((byte) SENTINEL, dst.get(i), "wrote past pixel");
|
||||
return read(dst, outSize);
|
||||
}
|
||||
|
||||
private static int storageBase(int sdlFormat) {
|
||||
return switch (sdlFormat) {
|
||||
case SDL_GPU_TEXTUREFORMAT_R8_UNORM, SDL_GPU_TEXTUREFORMAT_R16_UNORM, SDL_GPU_TEXTUREFORMAT_R16_FLOAT,
|
||||
SDL_GPU_TEXTUREFORMAT_R32_FLOAT, SDL_GPU_TEXTUREFORMAT_R8_SNORM, SDL_GPU_TEXTUREFORMAT_R16_SNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_R8_UINT, SDL_GPU_TEXTUREFORMAT_R8_INT, SDL_GPU_TEXTUREFORMAT_R16_UINT,
|
||||
SDL_GPU_TEXTUREFORMAT_R16_INT, SDL_GPU_TEXTUREFORMAT_R32_UINT, SDL_GPU_TEXTUREFORMAT_R32_INT -> GL11.GL_RED;
|
||||
case SDL_GPU_TEXTUREFORMAT_R8G8_UNORM, SDL_GPU_TEXTUREFORMAT_R16G16_UNORM, SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT,
|
||||
SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT, SDL_GPU_TEXTUREFORMAT_R8G8_SNORM, SDL_GPU_TEXTUREFORMAT_R16G16_SNORM,
|
||||
SDL_GPU_TEXTUREFORMAT_R8G8_UINT, SDL_GPU_TEXTUREFORMAT_R8G8_INT, SDL_GPU_TEXTUREFORMAT_R16G16_UINT,
|
||||
SDL_GPU_TEXTUREFORMAT_R16G16_INT, SDL_GPU_TEXTUREFORMAT_R32G32_UINT, SDL_GPU_TEXTUREFORMAT_R32G32_INT -> GL30.GL_RG;
|
||||
case SDL_GPU_TEXTUREFORMAT_A8_UNORM -> GL11.GL_ALPHA;
|
||||
case SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT, SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM -> GL11.GL_RGB;
|
||||
default -> GL11.GL_RGBA;
|
||||
};
|
||||
}
|
||||
|
||||
private static byte[] packBase(int sdlFormat, int base, byte[] texel, boolean readPixels, int format, int type) {
|
||||
final ByteBuffer src = direct(texel, texel.length);
|
||||
final int outSize = PixelOps.glPixelSize(format, type);
|
||||
final ByteBuffer dst = direct(new byte[0], outSize);
|
||||
PixelPack.packColor(MemoryUtil.memAddress(src), sdlFormat, base, texel.length, 1, 1, false, readPixels, format, type, ps(1), MemoryUtil.memAddress(dst));
|
||||
return read(dst, outSize);
|
||||
}
|
||||
|
||||
@Test
|
||||
void rgbBaseOverRgbaStorageReadsAlphaOne() {
|
||||
final int rgba8 = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
||||
final byte[] px = b(0x11, 0x22, 0x33, 0x80);
|
||||
assertArrayEquals(b(0x11, 0x22, 0x33, 0xFF), packBase(rgba8, GL11.GL_RGB, px, true, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x11, 0x22, 0x33, 0xFF), packBase(rgba8, GL11.GL_RGB, px, false, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x33, 0x22, 0x11, 0xFF), packBase(rgba8, GL11.GL_RGB, px, true, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV));
|
||||
assertArrayEquals(b(0x11, 0x22, 0x33), packBase(rgba8, GL11.GL_RGB, px, true, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0xFF), packBase(rgba8, GL11.GL_RGB, px, true, GL11.GL_ALPHA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(f32(1.0f), packBase(SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT, GL11.GL_RGB, le16(0, 0, 0, 0x3800), true, GL11.GL_ALPHA, GL11.GL_FLOAT));
|
||||
assertArrayEquals(b(1, 2, 3, 1), packBase(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT, GL11.GL_RGB, b(1, 2, 3, 9), true, GL30.GL_RGBA_INTEGER, GL11.GL_UNSIGNED_BYTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void luminanceIntensityAlphaBasesRebase() {
|
||||
final int rgba8 = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
||||
final byte[] px = b(0x40, 0x50, 0x60, 0x70);
|
||||
assertArrayEquals(b(0x40, 0x00, 0x00, 0xFF), packBase(rgba8, GL11.GL_LUMINANCE, px, true, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x40, 0x00, 0x00, 0xFF), packBase(rgba8, GL11.GL_INTENSITY, px, false, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x40, 0x00, 0x00, 0x70), packBase(rgba8, GL11.GL_LUMINANCE_ALPHA, px, true, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x00, 0x00, 0x00, 0x70), packBase(rgba8, GL11.GL_ALPHA, px, true, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x40), packBase(rgba8, GL11.GL_LUMINANCE, px, true, GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x40, 0x70), packBase(rgba8, GL11.GL_LUMINANCE_ALPHA, px, true, GL11.GL_LUMINANCE_ALPHA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x00), packBase(rgba8, GL11.GL_ALPHA, px, true, GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void luminanceSumUsesRebasedChannels() {
|
||||
assertArrayEquals(b(0x80), packBase(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, GL30.GL_RG, b(0x40, 0x40, 0x40, 0x70), true, GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0xC0), packBase(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, GL11.GL_RGB, b(0x40, 0x40, 0x40, 0x70), true, GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void glBaseFormatFollowsMesa() {
|
||||
assertEquals(GL11.GL_RGB, PixelPack.glBaseFormat(GL11.GL_RGB8));
|
||||
assertEquals(GL11.GL_RGB, PixelPack.glBaseFormat(GL30.GL_R11F_G11F_B10F));
|
||||
assertEquals(GL11.GL_RGB, PixelPack.glBaseFormat(GL30.GL_RGB16UI));
|
||||
assertEquals(GL11.GL_RGBA, PixelPack.glBaseFormat(GL11.GL_RGBA8));
|
||||
assertEquals(GL11.GL_RGBA, PixelPack.glBaseFormat(GL11.GL_RGB10_A2));
|
||||
assertEquals(GL11.GL_RGBA, PixelPack.glBaseFormat(GL21.GL_SRGB8_ALPHA8));
|
||||
assertEquals(GL11.GL_RGBA, PixelPack.glBaseFormat(GL30.GL_RGBA16F));
|
||||
assertEquals(GL11.GL_RED, PixelPack.glBaseFormat(GL30.GL_R16F));
|
||||
assertEquals(GL11.GL_RED, PixelPack.glBaseFormat(GL30.GL_R8I));
|
||||
assertEquals(GL30.GL_RG, PixelPack.glBaseFormat(GL30.GL_RG32UI));
|
||||
assertEquals(GL11.GL_ALPHA, PixelPack.glBaseFormat(GL11.GL_ALPHA8));
|
||||
assertEquals(GL11.GL_LUMINANCE, PixelPack.glBaseFormat(1));
|
||||
assertEquals(GL11.GL_LUMINANCE_ALPHA, PixelPack.glBaseFormat(GL11.GL_LUMINANCE8_ALPHA8));
|
||||
assertEquals(GL11.GL_INTENSITY, PixelPack.glBaseFormat(GL11.GL_INTENSITY16));
|
||||
assertEquals(GL11.GL_DEPTH_COMPONENT, PixelPack.glBaseFormat(GL30.GL_DEPTH_COMPONENT32F));
|
||||
assertEquals(GL30.GL_DEPTH_STENCIL, PixelPack.glBaseFormat(GL30.GL_DEPTH24_STENCIL8));
|
||||
assertEquals(GL11.GL_STENCIL_INDEX, PixelPack.glBaseFormat(GL30.GL_STENCIL_INDEX8));
|
||||
assertEquals(-1, PixelPack.glBaseFormat(0x1234));
|
||||
assertThrows(IllegalArgumentException.class, () -> packBase(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, -1, b(0, 0, 0, 0), true, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertThrows(IllegalArgumentException.class, () -> packBase(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, GL11.GL_DEPTH_COMPONENT, b(0, 0, 0, 0), true, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
}
|
||||
|
||||
private static byte[] readPix(int sdlFormat, byte[] texel, int format, int type) {
|
||||
return pack1(sdlFormat, texel, true, format, type);
|
||||
}
|
||||
|
||||
private static byte[] getTex(int sdlFormat, byte[] texel, int format, int type) {
|
||||
return pack1(sdlFormat, texel, false, format, type);
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeRgba8() {
|
||||
assertArrayEquals(b(0x11, 0x22, 0x33, 0x44), readPix(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, b(0x11, 0x22, 0x33, 0x44), GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(le16(0x1111, 0x2222, 0x3333, 0x4444), readPix(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, b(0x11, 0x22, 0x33, 0x44), GL11.GL_RGBA, GL11.GL_UNSIGNED_SHORT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeBgra8() {
|
||||
assertArrayEquals(b(0x11, 0x22, 0x33, 0x44), readPix(SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM, b(0x33, 0x22, 0x11, 0x44), GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x33, 0x22, 0x11, 0x44), readPix(SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM, b(0x33, 0x22, 0x11, 0x44), GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x33, 0x22, 0x11, 0x44), readPix(SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM, b(0x33, 0x22, 0x11, 0x44), GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV));
|
||||
assertArrayEquals(le16(0x1111, 0x2222, 0x3333), readPix(SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM, b(0x33, 0x22, 0x11, 0x44), GL11.GL_RGB, GL11.GL_UNSIGNED_SHORT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeSrgbReturnsStoredValues() {
|
||||
assertArrayEquals(b(0x80, 0x40, 0x20, 0x10), readPix(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB, b(0x80, 0x40, 0x20, 0x10), GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x80, 0x40, 0x20, 0x10), readPix(SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB, b(0x20, 0x40, 0x80, 0x10), GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeR8AndRg8RebaseAlphaToOne() {
|
||||
assertArrayEquals(b(0xFF, 0x00, 0x00, 0xFF), readPix(SDL_GPU_TEXTUREFORMAT_R8_UNORM, b(0xFF), GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x40, 0x80, 0x00, 0xFF), readPix(SDL_GPU_TEXTUREFORMAT_R8G8_UNORM, b(0x40, 0x80), GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x40, 0x80), readPix(SDL_GPU_TEXTUREFORMAT_R8G8_UNORM, b(0x40, 0x80), GL30.GL_RG, GL11.GL_UNSIGNED_BYTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeUnorm16() {
|
||||
assertArrayEquals(b(0x12), readPix(SDL_GPU_TEXTUREFORMAT_R16_UNORM, le16(0x1234), GL11.GL_RED, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(f32(0.0f, 1.0f), readPix(SDL_GPU_TEXTUREFORMAT_R16G16_UNORM, le16(0x0000, 0xFFFF), GL30.GL_RG, GL11.GL_FLOAT));
|
||||
assertArrayEquals(b(18, 128, 255, 0), readPix(SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM, le16(0x1234, 0x8000, 0xFFFF, 0x0000), GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeA8RebasesColorToZero() {
|
||||
assertArrayEquals(b(0, 0, 0, 0x7F), readPix(SDL_GPU_TEXTUREFORMAT_A8_UNORM, b(0x7F), GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x7F), readPix(SDL_GPU_TEXTUREFORMAT_A8_UNORM, b(0x7F), GL11.GL_ALPHA, GL11.GL_UNSIGNED_BYTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeHalfFloat() {
|
||||
assertArrayEquals(f32(1.0f, 0.0f, 0.0f, 1.0f), readPix(SDL_GPU_TEXTUREFORMAT_R16_FLOAT, le16(0x3C00), GL11.GL_RGBA, GL11.GL_FLOAT));
|
||||
assertArrayEquals(f32(0.5f, -2.0f), readPix(SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT, le16(0x3800, 0xC000), GL30.GL_RG, GL11.GL_FLOAT));
|
||||
assertArrayEquals(f32(0.25f, 0.5f, 1.0f, 2.0f), readPix(SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT, le16(0x3400, 0x3800, 0x3C00, 0x4000), GL11.GL_RGBA, GL11.GL_FLOAT));
|
||||
assertArrayEquals(le16(0x3400, 0x3800, 0x3C00, 0x4000), readPix(SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT, le16(0x3400, 0x3800, 0x3C00, 0x4000), GL11.GL_RGBA, GL30.GL_HALF_FLOAT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeFloat32() {
|
||||
assertArrayEquals(b(191), readPix(SDL_GPU_TEXTUREFORMAT_R32_FLOAT, f32(0.75f), GL11.GL_RED, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(f32(0.75f), readPix(SDL_GPU_TEXTUREFORMAT_R32_FLOAT, f32(0.75f), GL11.GL_RED, GL11.GL_FLOAT));
|
||||
assertArrayEquals(f32(1.5f, -0.5f), readPix(SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT, f32(1.5f, -0.5f), GL30.GL_RG, GL11.GL_FLOAT));
|
||||
assertArrayEquals(le16(0x3E00, 0xB800), readPix(SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT, f32(1.5f, -0.5f), GL30.GL_RG, GL30.GL_HALF_FLOAT));
|
||||
assertArrayEquals(f32(-1.0f, 0.0f, 0.5f, 3.0f), readPix(SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT, f32(-1.0f, 0.0f, 0.5f, 3.0f), GL11.GL_RGBA, GL11.GL_FLOAT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeR11G11B10() {
|
||||
assertArrayEquals(f32(1.0f, 0.5f, 2.0f, 1.0f), readPix(SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT, le32(0x801C03C0), GL11.GL_RGBA, GL11.GL_FLOAT));
|
||||
assertArrayEquals(le32(0x801C03C0), readPix(SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT, le32(0x801C03C0), GL11.GL_RGB, GL30.GL_UNSIGNED_INT_10F_11F_11F_REV));
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeR10G10B10A2() {
|
||||
assertArrayEquals(b(255, 128, 0, 255), readPix(SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM, le32(0xC00803FF), GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(le32(0xC00803FF), readPix(SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM, le32(0xC00803FF), GL11.GL_RGBA, GL12.GL_UNSIGNED_INT_2_10_10_10_REV));
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodePacked16() {
|
||||
assertArrayEquals(b(255, 130, 8, 255), readPix(SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM, le16(0xFC01), GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(le16(0xFC01), readPix(SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM, le16(0xFC01), GL11.GL_RGB, GL12.GL_UNSIGNED_SHORT_5_6_5));
|
||||
assertArrayEquals(b(132, 0, 255, 255), readPix(SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM, le16(0xC01F), GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(255, 136, 0, 68), readPix(SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM, le16(0x4F80), GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeSnorm() {
|
||||
assertArrayEquals(f32(-1.0f), getTex(SDL_GPU_TEXTUREFORMAT_R8_SNORM, b(0x81), GL11.GL_RED, GL11.GL_FLOAT));
|
||||
assertArrayEquals(f32(0.0f), readPix(SDL_GPU_TEXTUREFORMAT_R8_SNORM, b(0x81), GL11.GL_RED, GL11.GL_FLOAT));
|
||||
assertArrayEquals(b(255), getTex(SDL_GPU_TEXTUREFORMAT_R8_SNORM, b(0x7F), GL11.GL_RED, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(f32(1.0f, -1.0f), getTex(SDL_GPU_TEXTUREFORMAT_R8G8_SNORM, b(0x7F, 0x80), GL30.GL_RG, GL11.GL_FLOAT));
|
||||
assertArrayEquals(b(0x7F, 0x00, 0x81, 0x7F), getTex(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM, b(0x7F, 0x00, 0x81, 0x7F), GL11.GL_RGBA, GL11.GL_BYTE));
|
||||
assertArrayEquals(f32(1.0f), getTex(SDL_GPU_TEXTUREFORMAT_R16_SNORM, le16(0x7FFF), GL11.GL_RED, GL11.GL_FLOAT));
|
||||
assertArrayEquals(f32(-1.0f, 0.0f), getTex(SDL_GPU_TEXTUREFORMAT_R16G16_SNORM, le16(0x8000, 0x0000), GL30.GL_RG, GL11.GL_FLOAT));
|
||||
assertArrayEquals(b(127, 0x81, 64, 0), getTex(SDL_GPU_TEXTUREFORMAT_R16G16B16A16_SNORM, le16(0x7FFF, 0x8001, 0x4000, 0x0000), GL11.GL_RGBA, GL11.GL_BYTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeInteger8() {
|
||||
assertArrayEquals(b(200, 0, 0, 1), readPix(SDL_GPU_TEXTUREFORMAT_R8_UINT, b(200), GL30.GL_RGBA_INTEGER, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0), readPix(SDL_GPU_TEXTUREFORMAT_R8_INT, b(0xFB), GL30.GL_RED_INTEGER, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(le32(-5), readPix(SDL_GPU_TEXTUREFORMAT_R8_INT, b(0xFB), GL30.GL_RED_INTEGER, GL11.GL_INT));
|
||||
assertArrayEquals(le16(9, 250), readPix(SDL_GPU_TEXTUREFORMAT_R8G8_UINT, b(9, 250), GL30.GL_RG_INTEGER, GL11.GL_UNSIGNED_SHORT));
|
||||
assertArrayEquals(b(0x80, 0x7F), readPix(SDL_GPU_TEXTUREFORMAT_R8G8_INT, b(0x80, 0x7F), GL30.GL_RG_INTEGER, GL11.GL_BYTE));
|
||||
assertArrayEquals(b(3, 2, 1, 4), readPix(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT, b(1, 2, 3, 4), GL30.GL_BGRA_INTEGER, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(le16(0xFFFF, 2, 3, 4), readPix(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_INT, b(0xFF, 2, 3, 4), GL30.GL_RGBA_INTEGER, GL11.GL_SHORT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeInteger16() {
|
||||
assertArrayEquals(le32(65535), readPix(SDL_GPU_TEXTUREFORMAT_R16_UINT, le16(0xFFFF), GL30.GL_RED_INTEGER, GL11.GL_UNSIGNED_INT));
|
||||
assertArrayEquals(le16(0x8000), readPix(SDL_GPU_TEXTUREFORMAT_R16_INT, le16(0x8000), GL30.GL_RED_INTEGER, GL11.GL_SHORT));
|
||||
assertArrayEquals(b(255, 1), readPix(SDL_GPU_TEXTUREFORMAT_R16G16_UINT, le16(0xFFFF, 1), GL30.GL_RG_INTEGER, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(le16(0x7FFF, 1), readPix(SDL_GPU_TEXTUREFORMAT_R16G16_UINT, le16(0xFFFF, 1), GL30.GL_RG_INTEGER, GL11.GL_SHORT));
|
||||
assertArrayEquals(b(0x80, 0x7F), readPix(SDL_GPU_TEXTUREFORMAT_R16G16_INT, le16(0x8000, 0x7FFF), GL30.GL_RG_INTEGER, GL11.GL_BYTE));
|
||||
assertArrayEquals(le32(0xC03FFFFF), readPix(SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UINT, le16(1023, 2000, 3, 5), GL30.GL_RGBA_INTEGER, GL12.GL_UNSIGNED_INT_2_10_10_10_REV));
|
||||
assertArrayEquals(le32(0, 2, 3, 4), readPix(SDL_GPU_TEXTUREFORMAT_R16G16B16A16_INT, le16(0xFFFF, 2, 3, 4), GL30.GL_RGBA_INTEGER, GL11.GL_UNSIGNED_INT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeInteger32() {
|
||||
assertArrayEquals(le16(0xFFFF), readPix(SDL_GPU_TEXTUREFORMAT_R32_UINT, le32(70000), GL30.GL_RED_INTEGER, GL11.GL_UNSIGNED_SHORT));
|
||||
assertArrayEquals(le32(-70000), readPix(SDL_GPU_TEXTUREFORMAT_R32_INT, le32(-70000), GL30.GL_RED_INTEGER, GL11.GL_INT));
|
||||
assertArrayEquals(le32(0x7FFFFFFF, 1), readPix(SDL_GPU_TEXTUREFORMAT_R32G32_UINT, le32(0xFFFFFFFF, 1), GL30.GL_RG_INTEGER, GL11.GL_INT));
|
||||
assertArrayEquals(le32(0, 7), readPix(SDL_GPU_TEXTUREFORMAT_R32G32_INT, le32(-7, 7), GL30.GL_RG_INTEGER, GL11.GL_UNSIGNED_INT));
|
||||
assertArrayEquals(le32(0x7FFFFFFF, 5, 0, 7), readPix(SDL_GPU_TEXTUREFORMAT_R32G32B32A32_UINT, le32(0xFFFFFFFF, 5, 0, 7), GL30.GL_RGBA_INTEGER, GL11.GL_INT));
|
||||
assertArrayEquals(le32(0, 2, 3, 4), readPix(SDL_GPU_TEXTUREFORMAT_R32G32B32A32_INT, le32(-1, 2, 3, 4), GL30.GL_RGBA_INTEGER, GL11.GL_UNSIGNED_INT));
|
||||
assertArrayEquals(le32(-1, 2, 3), readPix(SDL_GPU_TEXTUREFORMAT_R32G32B32A32_INT, le32(-1, 2, 3, 4), GL30.GL_RGB_INTEGER, GL11.GL_INT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeUnormToIntegerTypes() {
|
||||
final byte[] px = b(0xFF, 0x80, 0x00, 0x40);
|
||||
final int rgba8 = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
||||
assertArrayEquals(b(127, 64, 0, 32), readPix(rgba8, px, GL11.GL_RGBA, GL11.GL_BYTE));
|
||||
assertArrayEquals(le16(0xFFFF, 0x8080, 0x0000, 0x4040), readPix(rgba8, px, GL11.GL_RGBA, GL11.GL_UNSIGNED_SHORT));
|
||||
assertArrayEquals(le16(0x7FFF, 0x4040, 0x0000, 0x2020), readPix(rgba8, px, GL11.GL_RGBA, GL11.GL_SHORT));
|
||||
assertArrayEquals(le32(0xFFFFFFFF, 0x80808080, 0x00000000, 0x40404040), readPix(rgba8, px, GL11.GL_RGBA, GL11.GL_UNSIGNED_INT));
|
||||
assertArrayEquals(le32(0x7FFFFFFF, 0x40404040, 0x00000000, 0x20202020), readPix(rgba8, px, GL11.GL_RGBA, GL11.GL_INT));
|
||||
assertArrayEquals(f32(1.0f, 0.0f, 1.0f, 0.0f), readPix(rgba8, b(0xFF, 0x00, 0xFF, 0x00), GL11.GL_RGBA, GL11.GL_FLOAT));
|
||||
assertArrayEquals(le16(0x3C00, 0x0000, 0x3C00, 0x0000), readPix(rgba8, b(0xFF, 0x00, 0xFF, 0x00), GL11.GL_RGBA, GL30.GL_HALF_FLOAT));
|
||||
assertArrayEquals(b(0x00, 0x80, 0xFF), readPix(rgba8, px, GL12.GL_BGR, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x80), readPix(rgba8, px, GL11.GL_GREEN, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(le16(0x0000), readPix(rgba8, px, GL11.GL_BLUE, GL11.GL_UNSIGNED_SHORT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodePackedTypes() {
|
||||
final byte[] px = b(0xFF, 0x80, 0x00, 0x40);
|
||||
final int rgba8 = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
||||
assertArrayEquals(b(0xF0), readPix(rgba8, px, GL11.GL_RGB, GL12.GL_UNSIGNED_BYTE_3_3_2));
|
||||
assertArrayEquals(b(0x27), readPix(rgba8, px, GL11.GL_RGB, GL12.GL_UNSIGNED_BYTE_2_3_3_REV));
|
||||
assertArrayEquals(le16(0xFC00), readPix(rgba8, px, GL11.GL_RGB, GL12.GL_UNSIGNED_SHORT_5_6_5));
|
||||
assertArrayEquals(le16(0x041F), readPix(rgba8, px, GL11.GL_RGB, GL12.GL_UNSIGNED_SHORT_5_6_5_REV));
|
||||
assertArrayEquals(le16(0xF804), readPix(rgba8, px, GL11.GL_RGBA, GL12.GL_UNSIGNED_SHORT_4_4_4_4));
|
||||
assertArrayEquals(le16(0x408F), readPix(rgba8, px, GL11.GL_RGBA, GL12.GL_UNSIGNED_SHORT_4_4_4_4_REV));
|
||||
assertArrayEquals(le16(0xFC00), readPix(rgba8, px, GL11.GL_RGBA, GL12.GL_UNSIGNED_SHORT_5_5_5_1));
|
||||
assertArrayEquals(le16(0x021F), readPix(rgba8, px, GL11.GL_RGBA, GL12.GL_UNSIGNED_SHORT_1_5_5_5_REV));
|
||||
assertArrayEquals(le32(0xFF800040), readPix(rgba8, px, GL11.GL_RGBA, GL12.GL_UNSIGNED_INT_8_8_8_8));
|
||||
assertArrayEquals(le32(0x40FF8000), readPix(rgba8, px, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV));
|
||||
assertArrayEquals(le32(0xFFE02001), readPix(rgba8, px, GL11.GL_RGBA, GL12.GL_UNSIGNED_INT_10_10_10_2));
|
||||
assertArrayEquals(le32(0x40080BFF), readPix(rgba8, px, GL11.GL_RGBA, GL12.GL_UNSIGNED_INT_2_10_10_10_REV));
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeUnormUpConversionRoundsLikeSpec() {
|
||||
final int rgba8 = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
||||
assertArrayEquals(le32(0x666664CD), readPix(rgba8, b(51, 102, 153, 85), GL11.GL_RGBA, GL12.GL_UNSIGNED_INT_2_10_10_10_REV));
|
||||
assertArrayEquals(le32(0x59964736), readPix(rgba8, b(205, 100, 102, 102), GL11.GL_RGBA, GL12.GL_UNSIGNED_INT_2_10_10_10_REV));
|
||||
assertArrayEquals(le16(0x0102, 0xFEFE), getTex(SDL_GPU_TEXTUREFORMAT_R8G8_SNORM, b(1, 0xFF), GL30.GL_RG, GL11.GL_SHORT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodePackedFloatTypes() {
|
||||
final byte[] half = le16(0x3C00, 0x3800, 0x4000, 0x3C00);
|
||||
assertArrayEquals(le32(0x801C03C0), readPix(SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT, half, GL11.GL_RGB, GL30.GL_UNSIGNED_INT_10F_11F_11F_REV));
|
||||
assertArrayEquals(le32(0x81010100), getTex(SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT, le16(0x3C00, 0x3800, 0x3400, 0x3C00), GL11.GL_RGB, GL30.GL_UNSIGNED_INT_5_9_9_9_REV));
|
||||
}
|
||||
|
||||
@Test
|
||||
void encodeHalfRoundsToNearestEven() {
|
||||
final byte[] src = f32(1.00048828125f, 1.00146484375f, Math.scalb(1.0f, -24), -2.0f);
|
||||
assertArrayEquals(le16(0x3C00, 0x3C02, 0x0001, 0xC000), readPix(SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT, src, GL11.GL_RGBA, GL30.GL_HALF_FLOAT));
|
||||
assertArrayEquals(le16(0x7BFF, 0x7C00), readPix(SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT, f32(65504.0f, 65520.0f), GL30.GL_RG, GL30.GL_HALF_FLOAT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rebaseAlphaForRgbSources() {
|
||||
assertArrayEquals(f32(1.0f, 0.5f, 2.0f, 1.0f), readPix(SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT, le32(0x801C03C0), GL11.GL_RGBA, GL11.GL_FLOAT));
|
||||
assertArrayEquals(le16(0xFFFF), readPix(SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM, le16(0xFC01), GL11.GL_ALPHA, GL11.GL_UNSIGNED_SHORT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void luminanceSumsForReadPixelsButNotGetTexImage() {
|
||||
final int rgba8 = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
||||
assertArrayEquals(b(192), readPix(rgba8, b(0x40, 0x40, 0x40, 0x80), GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(192, 128), readPix(rgba8, b(0x40, 0x40, 0x40, 0x80), GL11.GL_LUMINANCE_ALPHA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x40), getTex(rgba8, b(0x40, 0x40, 0x40, 0x80), GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x40, 0x80), getTex(rgba8, b(0x40, 0x40, 0x40, 0x80), GL11.GL_LUMINANCE_ALPHA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0x40), readPix(SDL_GPU_TEXTUREFORMAT_R8_UNORM, b(0x40), GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void luminanceSumClampsForFixedPointOnly() {
|
||||
assertArrayEquals(b(255), readPix(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, b(0xFF, 0xFF, 0, 0), GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(f32(1.0f), readPix(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, b(0xFF, 0xFF, 0, 0), GL11.GL_LUMINANCE, GL11.GL_FLOAT));
|
||||
assertArrayEquals(f32(1.5f), readPix(SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT, f32(0.5f, 0.75f, 0.25f, 1.0f), GL11.GL_LUMINANCE, GL11.GL_FLOAT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void clampVersusNoClamp() {
|
||||
final byte[] half = le16(0x3400, 0x3800, 0x3C00, 0x4000);
|
||||
assertArrayEquals(b(64, 128, 255, 255), readPix(SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT, half, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertArrayEquals(b(0), readPix(SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT, le16(0xC000, 0x3C00), GL11.GL_RED, GL11.GL_UNSIGNED_BYTE));
|
||||
final byte[] f = f32(-1.0f, 0.0f, 0.5f, 3.0f);
|
||||
assertArrayEquals(le16(0, 0, 16384, 32767), readPix(SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT, f, GL11.GL_RGBA, GL11.GL_SHORT));
|
||||
assertArrayEquals(le16(0x8001, 0, 16384, 32767), getTex(SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT, f, GL11.GL_RGBA, GL11.GL_SHORT));
|
||||
assertArrayEquals(f, getTex(SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT, f, GL11.GL_RGBA, GL11.GL_FLOAT));
|
||||
}
|
||||
|
||||
private static byte[] packR8(byte[] src, int w, int h, int format, int type, PackState ps, boolean flip, int dstSize) {
|
||||
final ByteBuffer s = direct(src, src.length);
|
||||
final ByteBuffer d = direct(new byte[0], dstSize);
|
||||
PixelPack.packColor(MemoryUtil.memAddress(s), SDL_GPU_TEXTUREFORMAT_R8_UNORM, GL11.GL_RED, w, w, h, flip, true, format, type, ps, MemoryUtil.memAddress(d));
|
||||
return read(d, dstSize);
|
||||
}
|
||||
|
||||
@Test
|
||||
void packAlignment() {
|
||||
final byte[] src = b(1, 2, 3, 4, 5, 6);
|
||||
assertArrayEquals(b(1, 2, 3, 4, 5, 6, SENTINEL), packR8(src, 3, 2, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, ps(1), false, 7));
|
||||
assertEquals(6L, PixelPack.imageSize(GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, 3, 2, ps(1)));
|
||||
assertArrayEquals(b(1, 2, 3, SENTINEL, 4, 5, 6, SENTINEL), packR8(src, 3, 2, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, ps(4), false, 8));
|
||||
assertEquals(7L, PixelPack.imageSize(GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, 3, 2, ps(4)));
|
||||
assertArrayEquals(b(1, 2, 3, SENTINEL, SENTINEL, SENTINEL, SENTINEL, SENTINEL, 4, 5, 6, SENTINEL), packR8(src, 3, 2, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, ps(8), false, 12));
|
||||
assertEquals(11L, PixelPack.imageSize(GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, 3, 2, ps(8)));
|
||||
assertEquals(12, PixelPack.rowStride(GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, 3, ps(4)));
|
||||
assertEquals(24, PixelPack.rowStride(GL30.GL_RG, GL11.GL_FLOAT, 3, ps(8)));
|
||||
assertEquals(24, PixelPack.rowStride(GL30.GL_DEPTH_STENCIL, GL30.GL_FLOAT_32_UNSIGNED_INT_24_8_REV, 3, ps(4)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void packRowLengthAndSkips() {
|
||||
final PackState rl = ps(1);
|
||||
rl.rowLength = 5;
|
||||
assertArrayEquals(b(1, 2, SENTINEL, SENTINEL, SENTINEL, 3, 4, SENTINEL), packR8(b(1, 2, 3, 4), 2, 2, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, rl, false, 8));
|
||||
assertEquals(7L, PixelPack.imageSize(GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, 2, 2, rl));
|
||||
|
||||
final PackState skip = ps(1);
|
||||
skip.rowLength = 4;
|
||||
skip.skipRows = 2;
|
||||
skip.skipPixels = 1;
|
||||
final byte[] out = packR8(b(7, 8), 2, 1, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, skip, false, 12);
|
||||
final byte[] expected = b(SENTINEL, SENTINEL, SENTINEL, SENTINEL, SENTINEL, SENTINEL, SENTINEL, SENTINEL, SENTINEL, 7, 8, SENTINEL);
|
||||
assertArrayEquals(expected, out);
|
||||
assertEquals(11L, PixelPack.imageSize(GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, 2, 1, skip));
|
||||
|
||||
final PackState rgbSkip = ps(4);
|
||||
rgbSkip.skipPixels = 1;
|
||||
assertArrayEquals(b(SENTINEL, SENTINEL, SENTINEL, 9, 0, 0, SENTINEL), packR8(b(9), 1, 1, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, rgbSkip, false, 7));
|
||||
}
|
||||
|
||||
@Test
|
||||
void packFlipRows() {
|
||||
assertArrayEquals(b(3, 4, 1, 2), packR8(b(1, 2, 3, 4), 2, 2, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, ps(1), true, 4));
|
||||
assertArrayEquals(b(3, 0, 4, 0, 1, 0, 2, 0), packR8(b(1, 2, 3, 4), 2, 2, GL30.GL_RG, GL11.GL_UNSIGNED_BYTE, ps(1), true, 8));
|
||||
}
|
||||
|
||||
@Test
|
||||
void packFlipRowsWithChannelSwap() {
|
||||
final ByteBuffer s = direct(b(0xAA, 0x01, 0x02, 0x03, 0xBB, 0x10, 0x20, 0x30), 8);
|
||||
final ByteBuffer d = direct(new byte[0], 8);
|
||||
PixelPack.packColor(MemoryUtil.memAddress(s), SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, GL11.GL_RGBA, 4, 1, 2, true, true, GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, ps(4), MemoryUtil.memAddress(d));
|
||||
assertArrayEquals(b(0x20, 0x10, 0xBB, 0x30, 0x02, 0x01, 0xAA, 0x03), read(d, 8));
|
||||
assertArrayEquals(b(223, 164, 64, 255), readPix(SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM, b(223, 164, 64, 255), GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void packSwapBytes() {
|
||||
final PackState swap = ps(1);
|
||||
swap.swapBytes = true;
|
||||
final ByteBuffer s16 = direct(le16(0x1234, 0xABCD), 4);
|
||||
final ByteBuffer d16 = direct(new byte[0], 4);
|
||||
PixelPack.packColor(MemoryUtil.memAddress(s16), SDL_GPU_TEXTUREFORMAT_R16G16_UNORM, GL30.GL_RG, 4, 1, 1, false, true, GL30.GL_RG, GL11.GL_UNSIGNED_SHORT, swap, MemoryUtil.memAddress(d16));
|
||||
assertArrayEquals(b(0x12, 0x34, 0xAB, 0xCD), read(d16, 4));
|
||||
|
||||
final ByteBuffer s32 = direct(f32(1.0f), 4);
|
||||
final ByteBuffer d32 = direct(new byte[0], 4);
|
||||
PixelPack.packColor(MemoryUtil.memAddress(s32), SDL_GPU_TEXTUREFORMAT_R32_FLOAT, GL11.GL_RED, 4, 1, 1, false, true, GL11.GL_RED, GL11.GL_FLOAT, swap, MemoryUtil.memAddress(d32));
|
||||
assertArrayEquals(b(0x3F, 0x80, 0x00, 0x00), read(d32, 4));
|
||||
|
||||
final ByteBuffer s8888 = direct(b(0xFF, 0x80, 0x00, 0x40), 4);
|
||||
final ByteBuffer d8888 = direct(new byte[0], 4);
|
||||
PixelPack.packColor(MemoryUtil.memAddress(s8888), SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, GL11.GL_RGBA, 4, 1, 1, false, true, GL11.GL_RGBA, GL12.GL_UNSIGNED_INT_8_8_8_8, swap, MemoryUtil.memAddress(d8888));
|
||||
assertArrayEquals(b(0xFF, 0x80, 0x00, 0x40), read(d8888, 4));
|
||||
|
||||
final ByteBuffer s565 = direct(b(0xFF, 0x80, 0x00, 0x40), 4);
|
||||
final ByteBuffer d565 = direct(new byte[0], 2);
|
||||
PixelPack.packColor(MemoryUtil.memAddress(s565), SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, GL11.GL_RGBA, 4, 1, 1, false, true, GL11.GL_RGB, GL12.GL_UNSIGNED_SHORT_5_6_5, swap, MemoryUtil.memAddress(d565));
|
||||
assertArrayEquals(b(0xFC, 0x00), read(d565, 2));
|
||||
|
||||
final ByteBuffer sub = direct(b(0x11, 0x22, 0x33, 0x44), 4);
|
||||
final ByteBuffer dub = direct(new byte[0], 4);
|
||||
PixelPack.packColor(MemoryUtil.memAddress(sub), SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, GL11.GL_RGBA, 4, 1, 1, false, true, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, swap, MemoryUtil.memAddress(dub));
|
||||
assertArrayEquals(b(0x11, 0x22, 0x33, 0x44), read(dub, 4));
|
||||
}
|
||||
|
||||
private static byte[] depth(int type, int outBytes, PackState ps, boolean flip, float... z) {
|
||||
final ByteBuffer s = direct(f32(z), z.length * 4);
|
||||
final ByteBuffer d = direct(new byte[0], outBytes);
|
||||
PixelPack.packDepth(MemoryUtil.memAddress(s), z.length * 4, z.length, 1, flip, type, ps, MemoryUtil.memAddress(d));
|
||||
return read(d, outBytes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void packDepthTypes() {
|
||||
final float[] z = {0.0f, 0.5f, 1.0f, 0.25f};
|
||||
assertArrayEquals(f32(z), depth(GL11.GL_FLOAT, 16, ps(4), false, z));
|
||||
assertArrayEquals(le32(0, 0x7FFFFFFF, 0xFFFFFFFF, 0x3FFFFFFF), depth(GL11.GL_UNSIGNED_INT, 16, ps(4), false, z));
|
||||
assertArrayEquals(le16(0, 0x8000, 0xFFFF, 0x4000), depth(GL11.GL_UNSIGNED_SHORT, 8, ps(4), false, z));
|
||||
assertArrayEquals(b(0, 127, 255, 63), depth(GL11.GL_UNSIGNED_BYTE, 4, ps(4), false, z));
|
||||
assertArrayEquals(b(0, 63, 127, 31), depth(GL11.GL_BYTE, 4, ps(4), false, z));
|
||||
assertArrayEquals(le16(0, 16383, 32767, 8191), depth(GL11.GL_SHORT, 8, ps(4), false, z));
|
||||
assertArrayEquals(le32(0, 1073741823, 2147483647, 536870911), depth(GL11.GL_INT, 16, ps(4), false, z));
|
||||
assertArrayEquals(le16(0x0000, 0x3800, 0x3C00, 0x3400), depth(GL30.GL_HALF_FLOAT, 8, ps(4), false, z));
|
||||
}
|
||||
|
||||
@Test
|
||||
void packDepthFlipAndSwap() {
|
||||
final ByteBuffer s = direct(f32(0.0f, 1.0f), 8);
|
||||
final ByteBuffer d = direct(new byte[0], 4);
|
||||
final PackState swap = ps(1);
|
||||
swap.swapBytes = true;
|
||||
PixelPack.packDepth(MemoryUtil.memAddress(s), 4, 1, 2, true, GL11.GL_UNSIGNED_SHORT, swap, MemoryUtil.memAddress(d));
|
||||
assertArrayEquals(b(0xFF, 0xFF, 0x00, 0x00), read(d, 4));
|
||||
}
|
||||
|
||||
private static byte[] stencil(int type, int outBytes) {
|
||||
final ByteBuffer s = direct(b(0, 1, 0x80, 0xFF), 4);
|
||||
final ByteBuffer d = direct(new byte[0], outBytes);
|
||||
PixelPack.packStencil(MemoryUtil.memAddress(s), 4, 4, 1, false, type, ps(4), MemoryUtil.memAddress(d));
|
||||
return read(d, outBytes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void packStencilTypes() {
|
||||
assertArrayEquals(b(0, 1, 0x80, 0xFF), stencil(GL11.GL_UNSIGNED_BYTE, 4));
|
||||
assertArrayEquals(b(0, 1, 0x00, 0x7F), stencil(GL11.GL_BYTE, 4));
|
||||
assertArrayEquals(le16(0, 1, 128, 255), stencil(GL11.GL_UNSIGNED_SHORT, 8));
|
||||
assertArrayEquals(le16(0, 1, 128, 255), stencil(GL11.GL_SHORT, 8));
|
||||
assertArrayEquals(le32(0, 1, 128, 255), stencil(GL11.GL_UNSIGNED_INT, 16));
|
||||
assertArrayEquals(le32(0, 1, 128, 255), stencil(GL11.GL_INT, 16));
|
||||
assertArrayEquals(f32(0.0f, 1.0f, 128.0f, 255.0f), stencil(GL11.GL_FLOAT, 16));
|
||||
assertArrayEquals(le16(0x0000, 0x3C00, 0x5800, 0x5BF8), stencil(GL30.GL_HALF_FLOAT, 8));
|
||||
}
|
||||
|
||||
@Test
|
||||
void packStencilFlipRows() {
|
||||
final ByteBuffer s = direct(b(1, 2, 3, 4), 4);
|
||||
final ByteBuffer d = direct(new byte[0], 8);
|
||||
PixelPack.packStencil(MemoryUtil.memAddress(s), 2, 2, 2, true, GL11.GL_UNSIGNED_BYTE, ps(4), MemoryUtil.memAddress(d));
|
||||
assertArrayEquals(b(3, 4, SENTINEL, SENTINEL, 1, 2, SENTINEL, SENTINEL), read(d, 8));
|
||||
}
|
||||
|
||||
private static byte[] depthStencil(int type, int outBytes, PackState ps) {
|
||||
final ByteBuffer z = direct(f32(1.0f, 0.5f), 8);
|
||||
final ByteBuffer st = direct(b(0xFF, 0x12), 2);
|
||||
final ByteBuffer d = direct(new byte[0], outBytes);
|
||||
PixelPack.packDepthStencil(MemoryUtil.memAddress(z), MemoryUtil.memAddress(st), 8, 2, 2, 1, false, type, ps, MemoryUtil.memAddress(d));
|
||||
return read(d, outBytes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void packDepthStencilTypes() {
|
||||
assertArrayEquals(le32(0xFFFFFFFF, 0x7FFFFF12), depthStencil(GL30.GL_UNSIGNED_INT_24_8, 8, ps(4)));
|
||||
final byte[] f = new byte[16];
|
||||
System.arraycopy(f32(1.0f), 0, f, 0, 4);
|
||||
System.arraycopy(le32(0xFF), 0, f, 4, 4);
|
||||
System.arraycopy(f32(0.5f), 0, f, 8, 4);
|
||||
System.arraycopy(le32(0x12), 0, f, 12, 4);
|
||||
assertArrayEquals(f, depthStencil(GL30.GL_FLOAT_32_UNSIGNED_INT_24_8_REV, 16, ps(4)));
|
||||
final PackState swap = ps(4);
|
||||
swap.swapBytes = true;
|
||||
assertArrayEquals(b(0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0x12), depthStencil(GL30.GL_UNSIGNED_INT_24_8, 8, swap));
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateRejectsBadCombinations() {
|
||||
assertThrows(IllegalArgumentException.class, () -> PixelPack.validate(GL11.GL_RGBA, GL12.GL_UNSIGNED_SHORT_5_6_5));
|
||||
assertThrows(IllegalArgumentException.class, () -> PixelPack.validate(GL12.GL_BGR, GL12.GL_UNSIGNED_SHORT_5_6_5));
|
||||
assertThrows(IllegalArgumentException.class, () -> PixelPack.validate(GL11.GL_RGBA, GL30.GL_UNSIGNED_INT_10F_11F_11F_REV));
|
||||
assertThrows(IllegalArgumentException.class, () -> PixelPack.validate(GL30.GL_RGB_INTEGER, GL11.GL_FLOAT));
|
||||
assertThrows(IllegalArgumentException.class, () -> PixelPack.validate(GL30.GL_DEPTH_STENCIL, GL11.GL_UNSIGNED_INT));
|
||||
assertThrows(IllegalArgumentException.class, () -> PixelPack.validate(GL11.GL_DEPTH_COMPONENT, GL30.GL_UNSIGNED_INT_24_8));
|
||||
assertThrows(IllegalArgumentException.class, () -> PixelPack.validate(GL11.GL_RGBA, GL11.GL_DOUBLE));
|
||||
assertThrows(IllegalArgumentException.class, () -> PixelPack.validate(GL11.GL_RGBA, GL30.GL_UNSIGNED_INT_5_9_9_9_REV));
|
||||
assertDoesNotThrow(() -> PixelPack.validate(GL30.GL_RGBA_INTEGER, GL12.GL_UNSIGNED_INT_2_10_10_10_REV));
|
||||
assertDoesNotThrow(() -> PixelPack.validate(GL11.GL_STENCIL_INDEX, GL11.GL_FLOAT));
|
||||
assertDoesNotThrow(() -> PixelPack.validate(GL30.GL_DEPTH_STENCIL, GL30.GL_UNSIGNED_INT_24_8));
|
||||
assertDoesNotThrow(() -> PixelPack.validate(GL11.GL_RGB, GL30.GL_UNSIGNED_INT_10F_11F_11F_REV));
|
||||
}
|
||||
|
||||
@Test
|
||||
void packColorRejectsIntegerMismatchAndUnknownSources() {
|
||||
assertThrows(IllegalArgumentException.class, () -> readPix(SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT, f32(0, 0, 0, 0), GL30.GL_RGBA_INTEGER, GL11.GL_UNSIGNED_BYTE));
|
||||
assertThrows(IllegalArgumentException.class, () -> readPix(SDL_GPU_TEXTUREFORMAT_R8_UINT, b(1), GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertThrows(IllegalArgumentException.class, () -> readPix(SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM, b(0, 0, 0, 0), GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE));
|
||||
assertThrows(IllegalArgumentException.class, () -> readPix(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, b(0, 0, 0, 0), GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void formatClassifiers() {
|
||||
assertEquals(true, PixelPack.isDepthFormat(GL11.GL_DEPTH_COMPONENT));
|
||||
assertEquals(true, PixelPack.isStencilFormat(GL11.GL_STENCIL_INDEX));
|
||||
assertEquals(true, PixelPack.isDepthStencilFormat(GL30.GL_DEPTH_STENCIL));
|
||||
assertEquals(false, PixelPack.isDepthFormat(GL11.GL_RGBA));
|
||||
}
|
||||
}
|
||||
+1
@@ -84,6 +84,7 @@ class ResourceManagerFormatMappingTest {
|
||||
@Test
|
||||
void testPackedFormats() {
|
||||
assertEquals(SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT, map(GL30.GL_R11F_G11F_B10F));
|
||||
assertEquals(SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM, map(GL11.GL_RGB10_A2));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
-11
@@ -56,15 +56,6 @@ class StencilClearTrackingTest {
|
||||
assertTrue(FBOClearTracker.fboHasPendingClear(st, fbo), "the pass must be rebuilt to apply the stencil clear");
|
||||
}
|
||||
|
||||
@Test
|
||||
void recordingAStencilClearRetractsTheClearedThisFrameMark() {
|
||||
final ContextState st = new ContextState();
|
||||
st.clearedStencilTexturesThisFrame.add(DEPTH);
|
||||
|
||||
FBOClearTracker.recordPendingStencilClear(st, DEPTH, 0);
|
||||
assertFalse(st.clearedStencilTexturesThisFrame.contains(DEPTH), "a second clear in one frame must not be swallowed");
|
||||
}
|
||||
|
||||
@Test
|
||||
void recordingBumpsMutationGen() {
|
||||
final ContextState st = new ContextState();
|
||||
@@ -79,7 +70,6 @@ class StencilClearTrackingTest {
|
||||
void scrubDropsStencilStateWithTheTexture() {
|
||||
final ContextState st = new ContextState();
|
||||
FBOClearTracker.recordPendingStencilClear(st, DEPTH, 7);
|
||||
st.clearedStencilTexturesThisFrame.add(DEPTH);
|
||||
|
||||
final SdlTestRig rig = SdlTestRig.create();
|
||||
final int glId = rig.resourceManager.genTexture();
|
||||
@@ -88,7 +78,6 @@ class StencilClearTrackingTest {
|
||||
|
||||
assertFalse(st.pendingStencilTextures.contains(DEPTH));
|
||||
assertFalse(st.pendingStencilValues.containsKey(DEPTH));
|
||||
assertFalse(st.clearedStencilTexturesThisFrame.contains(DEPTH));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.shader;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.util.shaderc.Shaderc;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.function.IntBinaryOperator;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.lwjgl.sdl.SDLGPU.*;
|
||||
import static org.lwjgl.system.MemoryUtil.memFree;
|
||||
|
||||
class LogicOpLoweringTest {
|
||||
|
||||
private static final int OR_REVERSE = 13;
|
||||
private static final int XOR = 6;
|
||||
|
||||
private static long key(int op, int... locClassPairs) {
|
||||
long k = op;
|
||||
for (int i = 0; i < locClassPairs.length; i += 2) k = LogicOpFormats.withClass(k, locClassPairs[i], locClassPairs[i + 1]);
|
||||
return k;
|
||||
}
|
||||
|
||||
private static String lower(String source, long key) {
|
||||
final String pre = SpirvCompiler.preprocess(ShaderTransformChain.run(source, GL20.GL_FRAGMENT_SHADER), Shaderc.shaderc_fragment_shader, "logicop.frag", SpirvCompiler.Options.vulkanForced460Core());
|
||||
assertNotNull(pre);
|
||||
return LogicOpLowering.lower(pre, key, 7);
|
||||
}
|
||||
|
||||
private static List<String> compileAndReflectSamplers(String src) {
|
||||
final SpirvCompiler.Result r = SpirvCompiler.compile(src, Shaderc.shaderc_fragment_shader, "logicop.frag", SpirvCompiler.Options.vulkanForced460Core());
|
||||
assertNotNull(r.spirv(), () -> "compile failed: " + r.error() + "\n" + src);
|
||||
final ByteBuffer spirv = r.spirv();
|
||||
try {
|
||||
ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_FRAGMENT_SHADER);
|
||||
return ShaderManager.reflectStage(spirv, false).samplerNames();
|
||||
} finally {
|
||||
memFree(spirv);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleOutWithEarlyReturn() {
|
||||
final int rgba8 = LogicOpFormats.classOf(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM);
|
||||
final String out = lower("""
|
||||
#version 330 core
|
||||
uniform sampler2D tex;
|
||||
in vec2 uv;
|
||||
out vec4 color;
|
||||
void main() {
|
||||
color = texture(tex, uv);
|
||||
if (color.a < 0.1) return;
|
||||
color.rgb *= 0.5;
|
||||
}
|
||||
""", key(OR_REVERSE, 0, rgba8));
|
||||
assertTrue(out.contains("void angelica_logicOpMain()"), out);
|
||||
assertTrue(out.contains("#define ANGELICA_LOGIC_OP 13u"), out);
|
||||
assertTrue(out.contains("uniform sampler2D angelica_LogicOpDst0;"), out);
|
||||
assertTrue(out.contains("color = angelica_logicOp(color, angelica_LogicOpDst0, vec4(255.0, 255.0, 255.0, 255.0));"), out);
|
||||
assertEquals(List.of("tex", "angelica_LogicOpDst0"), compileAndReflectSamplers(out));
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitLocationMrtSkipsPassthroughLocation() {
|
||||
final int rgba8 = LogicOpFormats.classOf(SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM);
|
||||
final int r16 = LogicOpFormats.classOf(SDL_GPU_TEXTUREFORMAT_R16_UNORM);
|
||||
final String out = lower("""
|
||||
#version 330 core
|
||||
layout(location = 2) out vec4 c;
|
||||
layout(location = 0) out vec4 a;
|
||||
layout(location = 1) out vec4 b;
|
||||
void main() { a = vec4(1.0); b = vec4(0.5); c = vec4(0.25); }
|
||||
""", key(XOR, 0, rgba8, 2, r16));
|
||||
assertTrue(out.contains("a = angelica_logicOp(a, angelica_LogicOpDst0, vec4(255.0, 255.0, 255.0, 255.0));"), out);
|
||||
assertTrue(out.contains("c = angelica_logicOp(c, angelica_LogicOpDst2, vec4(65535.0, 0.0, 0.0, 0.0));"), out);
|
||||
assertFalse(out.contains("angelica_LogicOpDst1"), out);
|
||||
assertFalse(out.contains("b = angelica_logicOp"), out);
|
||||
assertEquals(List.of("angelica_LogicOpDst0", "angelica_LogicOpDst2"), compileAndReflectSamplers(out));
|
||||
}
|
||||
|
||||
@Test
|
||||
void arrayOutExpandsToConsecutiveLocations() {
|
||||
final int rgba8 = LogicOpFormats.classOf(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM);
|
||||
final String out = lower("""
|
||||
#version 330 core
|
||||
#define N 3
|
||||
layout(location = 1) out vec4 fragData[N];
|
||||
void main() { fragData[0] = vec4(1.0); fragData[1] = vec4(0.0); fragData[2] = vec4(0.5); }
|
||||
""", key(OR_REVERSE, 1, rgba8, 3, rgba8));
|
||||
assertTrue(out.contains("fragData[0] = angelica_logicOp(fragData[0], angelica_LogicOpDst1,"), out);
|
||||
assertTrue(out.contains("fragData[2] = angelica_logicOp(fragData[2], angelica_LogicOpDst3,"), out);
|
||||
assertFalse(out.contains("fragData[1] = angelica_logicOp"), out);
|
||||
assertEquals(List.of("angelica_LogicOpDst1", "angelica_LogicOpDst3"), compileAndReflectSamplers(out));
|
||||
}
|
||||
|
||||
@Test
|
||||
void narrowOutputIsWidenedAndNarrowed() {
|
||||
final int r8 = LogicOpFormats.classOf(SDL_GPU_TEXTUREFORMAT_R8_UNORM);
|
||||
final String out = lower("""
|
||||
#version 330 core
|
||||
out float v;
|
||||
void main() { v = 0.5; }
|
||||
""", key(XOR, 0, r8));
|
||||
assertTrue(out.contains("v = angelica_logicOp(vec4(v, 0.0, 0.0, 0.0), angelica_LogicOpDst0, vec4(255.0, 0.0, 0.0, 0.0)).x;"), out);
|
||||
assertEquals(List.of("angelica_LogicOpDst0"), compileAndReflectSamplers(out));
|
||||
}
|
||||
|
||||
@Test
|
||||
void compatTransformedOutputWithDefaultUniforms() {
|
||||
final int rgba8 = LogicOpFormats.classOf(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM);
|
||||
final String out = lower("""
|
||||
#version 330 core
|
||||
layout (location = 0) out vec4 angelica_FragData0;
|
||||
uniform float angelica_currentAlphaTest;
|
||||
uniform sampler2D tex;
|
||||
uniform vec4 tint;
|
||||
in vec2 uv;
|
||||
void main() {
|
||||
angelica_FragData0 = texture(tex, uv) * tint;
|
||||
if (angelica_FragData0.a <= angelica_currentAlphaTest) discard;
|
||||
}
|
||||
""", key(OR_REVERSE, 0, rgba8));
|
||||
assertTrue(out.contains("angelica_FragData0 = angelica_logicOp(angelica_FragData0, angelica_LogicOpDst0,"), out);
|
||||
assertEquals(List.of("tex", "angelica_LogicOpDst0"), compileAndReflectSamplers(out));
|
||||
}
|
||||
|
||||
@Test
|
||||
void ambiguousOutsThrow() {
|
||||
final int rgba8 = LogicOpFormats.classOf(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM);
|
||||
final IllegalStateException e = assertThrows(IllegalStateException.class, () -> lower("""
|
||||
#version 330 core
|
||||
out vec4 a;
|
||||
out vec4 b;
|
||||
void main() { a = vec4(1.0); b = vec4(0.0); }
|
||||
""", key(XOR, 0, rgba8)));
|
||||
assertTrue(e.getMessage().contains("program 7"), e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void mesaOpMatchesTruthTable() {
|
||||
final IntBinaryOperator[] gl = {
|
||||
(s, d) -> 0,
|
||||
(s, d) -> s & d,
|
||||
(s, d) -> s & ~d,
|
||||
(s, d) -> s,
|
||||
(s, d) -> ~s & d,
|
||||
(s, d) -> d,
|
||||
(s, d) -> s ^ d,
|
||||
(s, d) -> s | d,
|
||||
(s, d) -> ~(s | d),
|
||||
(s, d) -> ~(s ^ d),
|
||||
(s, d) -> ~d,
|
||||
(s, d) -> s | ~d,
|
||||
(s, d) -> ~s,
|
||||
(s, d) -> ~s | d,
|
||||
(s, d) -> ~(s & d),
|
||||
(s, d) -> ~0,
|
||||
};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int mode = 0;
|
||||
for (int s = 0; s < 2; s++) {
|
||||
for (int d = 0; d < 2; d++) {
|
||||
mode |= (gl[i].applyAsInt(s, d) & 1) << (2 * s + d);
|
||||
}
|
||||
}
|
||||
assertEquals(mode, LogicOpFormats.mesaOp(GL11.GL_CLEAR + i), "GL opcode 0x" + Integer.toHexString(GL11.GL_CLEAR + i));
|
||||
}
|
||||
assertEquals(LogicOpFormats.OP_COPY, LogicOpFormats.mesaOp(GL11.GL_COPY));
|
||||
assertEquals(1, LogicOpFormats.mesaOp(GL11.GL_NOR));
|
||||
assertEquals(2, LogicOpFormats.mesaOp(GL11.GL_AND_INVERTED));
|
||||
assertEquals(4, LogicOpFormats.mesaOp(GL11.GL_AND_REVERSE));
|
||||
assertEquals(8, LogicOpFormats.mesaOp(GL11.GL_AND));
|
||||
}
|
||||
|
||||
@Test
|
||||
void formatClasses() {
|
||||
final int rgba = LogicOpFormats.classOf(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM);
|
||||
final int bgra = LogicOpFormats.classOf(SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM);
|
||||
assertTrue(rgba > 0);
|
||||
assertEquals(rgba, bgra);
|
||||
for (int c = 0; c < 4; c++) assertEquals(255.0f, LogicOpFormats.channelMax(rgba, c));
|
||||
final int b5g6r5 = LogicOpFormats.classOf(SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM);
|
||||
assertEquals(31.0f, LogicOpFormats.channelMax(b5g6r5, 0));
|
||||
assertEquals(63.0f, LogicOpFormats.channelMax(b5g6r5, 1));
|
||||
assertEquals(0.0f, LogicOpFormats.channelMax(b5g6r5, 3));
|
||||
assertEquals(0, LogicOpFormats.classOf(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB));
|
||||
assertEquals(0, LogicOpFormats.classOf(SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT));
|
||||
assertEquals(0, LogicOpFormats.classOf(SDL_GPU_TEXTUREFORMAT_INVALID));
|
||||
assertEquals(0.0f, LogicOpFormats.channelMax(0, 0));
|
||||
assertThrows(UnsupportedOperationException.class, () -> LogicOpFormats.classOf(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM));
|
||||
assertThrows(UnsupportedOperationException.class, () -> LogicOpFormats.classOf(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void keyPacking() {
|
||||
final long k = key(OR_REVERSE, 0, 1, 7, 11);
|
||||
assertEquals(OR_REVERSE, LogicOpFormats.opOf(k));
|
||||
assertEquals(1, LogicOpFormats.classAt(k, 0));
|
||||
assertEquals(0, LogicOpFormats.classAt(k, 3));
|
||||
assertEquals(11, LogicOpFormats.classAt(k, 7));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.gtnewhorizons.angelica;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.proxy.CommonProxy;
|
||||
import cpw.mods.fml.common.Mod;
|
||||
import cpw.mods.fml.common.SidedProxy;
|
||||
@@ -32,7 +31,7 @@ public class AngelicaMod {
|
||||
public static final String MOD_ID = "angelica";
|
||||
public static final Logger LOGGER = LogManager.getLogger("Angelica");
|
||||
public static final boolean lwjglDebug = SystemProperties.LWJGL_DEBUG;
|
||||
public static final boolean tracyEnabled = Tracy.ENABLED;
|
||||
public static final String KEY_CATEGORY = "key.categories.angelica";
|
||||
|
||||
@SidedProxy(
|
||||
clientSide = "com.gtnewhorizons.angelica.proxy.ClientProxy",
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.gtnewhorizons.angelica.glsm.streaming.PersistentStreamingBuffer;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
|
||||
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
|
||||
import com.gtnewhorizons.angelica.mixins.interfaces.FontRendererAccessor;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.BatchDrawDefaults;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.TesrBatchRenderer;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
@@ -153,6 +154,7 @@ public class BatchingFontRenderer {
|
||||
|
||||
/** {@code OpenGlHelper.lightmapTexUnit} */
|
||||
private static final int LIGHTMAP_TEX_UNIT = 1;
|
||||
private static final int FULL_BRIGHT_LIGHT = GLSMConfig.packBrightness(240.0f, 240.0f);
|
||||
private static int rawCapacity = INITIAL_BATCH_SIZE * VERTEX_SIZE;
|
||||
private static ByteBuffer vertexData = memAlloc(rawCapacity);
|
||||
private static long vertexDataAddress = memAddress0(vertexData);
|
||||
@@ -188,6 +190,7 @@ public class BatchingFontRenderer {
|
||||
private float lightmapV = 0.0f;
|
||||
private boolean lightmapActive = false;
|
||||
private int lightmapTextureId = 0;
|
||||
private int lightmapPackedLight = -1;
|
||||
|
||||
private final Vector3f lightingFactor = new Vector3f(1.0f, 1.0f, 1.0f);
|
||||
private boolean lightingFactorActive = false;
|
||||
@@ -214,7 +217,8 @@ public class BatchingFontRenderer {
|
||||
private void captureLightmapState() {
|
||||
final int unitBinding = GLStateManager.getTextures().getTextureUnitBindings(LIGHTMAP_TEX_UNIT).getBinding();
|
||||
|
||||
final boolean active = isWorldSpaceText() && unitBinding != 0;
|
||||
final boolean active = isWorldSpaceText() && unitBinding != 0
|
||||
&& GLStateManager.getTextures().getTextureUnitStates(LIGHTMAP_TEX_UNIT).isEnabled();
|
||||
|
||||
float u = 0.0f;
|
||||
float v = 0.0f;
|
||||
@@ -226,8 +230,9 @@ public class BatchingFontRenderer {
|
||||
v = scratchLightmapUv.y;
|
||||
texture = unitBinding;
|
||||
}
|
||||
final int packedLight = active ? GLSMConfig.packedLastBrightness() : FULL_BRIGHT_LIGHT;
|
||||
|
||||
if (active == lightmapActive && u == lightmapU && v == lightmapV && texture == lightmapTextureId) {
|
||||
if (active == lightmapActive && u == lightmapU && v == lightmapV && texture == lightmapTextureId && packedLight == lightmapPackedLight) {
|
||||
return;
|
||||
}
|
||||
sealBatchSegment();
|
||||
@@ -235,6 +240,7 @@ public class BatchingFontRenderer {
|
||||
lightmapU = u;
|
||||
lightmapV = v;
|
||||
lightmapTextureId = texture;
|
||||
lightmapPackedLight = packedLight;
|
||||
}
|
||||
|
||||
private static final class TextSegment {
|
||||
@@ -261,6 +267,7 @@ public class BatchingFontRenderer {
|
||||
|
||||
private int deferredCmdWatermark;
|
||||
private static final ObjectArrayList<TextSegment> deferredSegments = ObjectArrayList.wrap(new TextSegment[16], 0);
|
||||
private static final BatchDrawDefaults DEFERRED_DRAW_DEFAULTS = new BatchDrawDefaults();
|
||||
private static final ObjectArrayList<TextSegment> deferredSegmentPool = ObjectArrayList.wrap(new TextSegment[16], 0);
|
||||
|
||||
private final Matrix4f batchProj = new Matrix4f();
|
||||
@@ -532,7 +539,7 @@ public class BatchingFontRenderer {
|
||||
segment.lightmapU = lightmapU;
|
||||
segment.lightmapV = lightmapV;
|
||||
segment.lightmapTexture = lightmapTextureId;
|
||||
segment.packedLight = GLSMConfig.packedLastBrightness();
|
||||
segment.packedLight = lightmapPackedLight;
|
||||
final Vector3f normal = ShaderManager.getCurrentNormal();
|
||||
segment.normalX = normal.x;
|
||||
segment.normalY = normal.y;
|
||||
@@ -617,21 +624,26 @@ public class BatchingFontRenderer {
|
||||
public static void flushDeferredText() {
|
||||
if (deferredSegments.isEmpty()) return;
|
||||
|
||||
if (deferredSegments.getFirst().owner.shouldDrawThroughPipeline()) {
|
||||
flushDeferredThroughPipeline();
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
DEFERRED_DRAW_DEFAULTS.apply();
|
||||
try {
|
||||
GLStateManager.beginForeignDraw();
|
||||
if (deferredSegments.getFirst().owner.shouldDrawThroughPipeline()) {
|
||||
flushDeferredThroughPipeline();
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
flushDeferredTextInner();
|
||||
GLStateManager.beginForeignDraw();
|
||||
try {
|
||||
flushDeferredTextInner();
|
||||
} finally {
|
||||
GLStateManager.endForeignDraw();
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.endForeignDraw();
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
}
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
DEFERRED_DRAW_DEFAULTS.restore();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -641,6 +653,7 @@ public class BatchingFontRenderer {
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.FONT_PIPELINE);
|
||||
try {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
GLStateManager.enableAlphaTest();
|
||||
GLStateManager.enableBlend();
|
||||
@@ -688,6 +701,7 @@ public class BatchingFontRenderer {
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.FONT);
|
||||
try {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
deferredSegments.get(0).owner.setupFontDrawState();
|
||||
GLStateManager.glDepthMask(false);
|
||||
flushLastTexture = DUMMY_RESOURCE_LOCATION;
|
||||
@@ -997,7 +1011,7 @@ public class BatchingFontRenderer {
|
||||
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(prevBlockEntityId);
|
||||
final Vector3f normal = ShaderManager.getCurrentNormal();
|
||||
emitRangeThroughPipeline(this, cmdsData, batchSealedEnd, cmdCount,
|
||||
GLSMConfig.packedLastBrightness(), normal.x, normal.y, normal.z);
|
||||
lightmapPackedLight, normal.x, normal.y, normal.z);
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.glPopMatrix();
|
||||
@@ -1091,6 +1105,7 @@ public class BatchingFontRenderer {
|
||||
lightmapU = 0.0f;
|
||||
lightmapV = 0.0f;
|
||||
lightmapTextureId = 0;
|
||||
lightmapPackedLight = -1;
|
||||
}
|
||||
|
||||
// === Actual text mesh generation
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.gtnewhorizons.angelica.client.gui;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.debug.profiling.TracyCaptureNotifier;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.TracyBackend;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.TracyOptions;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.ActionOption;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionFlag;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionGroup;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionImpl;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.OptionPage;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.ControlValueFormatter;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.SliderControl;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.control.TickBoxControl;
|
||||
import me.jellysquid.mods.sodium.client.gui.options.storage.AngelicaOptionsStorage;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class TracyOptionPages {
|
||||
|
||||
private static final AngelicaOptionsStorage angelicaOpts = new AngelicaOptionsStorage();
|
||||
|
||||
public static OptionPage tracy() {
|
||||
final String stopLabel = I18n.format("options.angelica.tracy.captureAction.stop");
|
||||
final String savingLabel = I18n.format("options.angelica.tracy.captureAction.saving");
|
||||
final String startLabel = I18n.format("options.angelica.tracy.captureAction.start");
|
||||
final Supplier<String> captureActionLabel = () -> switch (Tracy.captureState()) {
|
||||
case TracyBackend.CAPTURE_CONNECTING, TracyBackend.CAPTURE_RECORDING -> stopLabel;
|
||||
case TracyBackend.CAPTURE_SAVING -> savingLabel;
|
||||
default -> startLabel;
|
||||
};
|
||||
|
||||
final String restartLabel = I18n.format("options.angelica.tracy.status.restart");
|
||||
final String runningLabel = I18n.format("options.angelica.tracy.status.running");
|
||||
final String runningForcedLabel = I18n.format("options.angelica.tracy.status.runningForced");
|
||||
final String failedLabel = I18n.format("options.angelica.tracy.status.failed");
|
||||
final String offLabel = I18n.format("options.angelica.tracy.status.off");
|
||||
final Supplier<String> statusLabel = () -> {
|
||||
if (AngelicaConfig.enableTracy != TracyOptions.configEnabled()
|
||||
|| AngelicaConfig.tracyAllowRemote != TracyOptions.configRemote()
|
||||
|| AngelicaConfig.tracyFineZones != TracyOptions.configFineZones()
|
||||
|| AngelicaConfig.tracyMaxSrcLocs != TracyOptions.configMaxSrcLocs()) {
|
||||
return restartLabel;
|
||||
}
|
||||
if (Tracy.ENABLED) {
|
||||
return SystemProperties.TRACY_OVERRIDE != null ? runningForcedLabel : runningLabel;
|
||||
}
|
||||
return TracyOptions.enabled() ? failedLabel : offLabel;
|
||||
};
|
||||
|
||||
return new OptionPage(I18n.format("options.angelica.tracy.page"), ImmutableList.of(
|
||||
OptionGroup.createBuilder()
|
||||
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
|
||||
.setName(I18n.format("options.angelica.tracy.enable"))
|
||||
.setTooltip(overrideTooltip("options.angelica.tracy.enable.tooltip", "angelica.tracy", SystemProperties.TRACY_OVERRIDE != null))
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> AngelicaConfig.enableTracy = value, opts -> AngelicaConfig.enableTracy)
|
||||
.setEnabled(SystemProperties.TRACY_OVERRIDE == null)
|
||||
.setFlags(OptionFlag.REQUIRES_GAME_RESTART)
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
|
||||
.setName(I18n.format("options.angelica.tracy.allowRemote"))
|
||||
.setTooltip(I18n.format("options.angelica.tracy.allowRemote.tooltip"))
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> AngelicaConfig.tracyAllowRemote = value, opts -> AngelicaConfig.tracyAllowRemote)
|
||||
.setFlags(OptionFlag.REQUIRES_GAME_RESTART)
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
|
||||
.setName(I18n.format("options.angelica.tracy.fineZones"))
|
||||
.setTooltip(overrideTooltip("options.angelica.tracy.fineZones.tooltip", "angelica.tracy.fineZones", SystemProperties.TRACY_FINE_ZONES_OVERRIDE != null))
|
||||
.setControl(TickBoxControl::new)
|
||||
.setBinding((opts, value) -> AngelicaConfig.tracyFineZones = value, opts -> AngelicaConfig.tracyFineZones)
|
||||
.setEnabled(SystemProperties.TRACY_FINE_ZONES_OVERRIDE == null)
|
||||
.setFlags(OptionFlag.REQUIRES_GAME_RESTART)
|
||||
.build())
|
||||
.add(OptionImpl.createBuilder(int.class, angelicaOpts)
|
||||
.setName(I18n.format("options.angelica.tracy.maxSrcLocs"))
|
||||
.setTooltip(overrideTooltip("options.angelica.tracy.maxSrcLocs.tooltip", "angelica.tracy.maxSrcLocs", SystemProperties.TRACY_MAX_SRC_LOCS_OVERRIDE != null))
|
||||
.setControl(o -> new SliderControl(o, 1024, 16384, 1024, ControlValueFormatter.number()))
|
||||
.setBinding((opts, value) -> AngelicaConfig.tracyMaxSrcLocs = value, opts -> AngelicaConfig.tracyMaxSrcLocs)
|
||||
.setEnabled(SystemProperties.TRACY_MAX_SRC_LOCS_OVERRIDE == null)
|
||||
.setFlags(OptionFlag.REQUIRES_GAME_RESTART)
|
||||
.build())
|
||||
.build(),
|
||||
OptionGroup.createBuilder()
|
||||
.add(new ActionOption(I18n.format("options.angelica.tracy.status"), I18n.format("options.angelica.tracy.status.tooltip"),
|
||||
statusLabel, () -> {}, () -> false))
|
||||
.build(),
|
||||
OptionGroup.createBuilder()
|
||||
.add(OptionImpl.createBuilder(int.class, angelicaOpts)
|
||||
.setName(I18n.format("options.angelica.tracy.captureLength"))
|
||||
.setTooltip(I18n.format("options.angelica.tracy.captureLength.tooltip"))
|
||||
.setControl(o -> new SliderControl(o, 10, 300, 10, ControlValueFormatter.seconds()))
|
||||
.setBinding((opts, value) -> AngelicaConfig.tracyCaptureSeconds = value, opts -> AngelicaConfig.tracyCaptureSeconds)
|
||||
.setEnabled(Tracy.ENABLED)
|
||||
.build())
|
||||
.add(new ActionOption(I18n.format("options.angelica.tracy.captureAction"), I18n.format("options.angelica.tracy.captureAction.tooltip"),
|
||||
captureActionLabel, TracyCaptureNotifier.INSTANCE::toggle, () -> Tracy.ENABLED))
|
||||
.build()));
|
||||
}
|
||||
|
||||
private static String overrideTooltip(String baseKey, String flag, boolean overridden) {
|
||||
final String base = I18n.format(baseKey);
|
||||
return overridden ? base + " " + I18n.format("options.angelica.tracy.overriddenBy", flag) : base;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import org.embeddedt.embeddium.impl.render.chunk.fog.FogService;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.shader.ChunkFogMode;
|
||||
|
||||
public class AngelicaFogService implements FogService {
|
||||
public static final AngelicaFogService INSTANCE = new AngelicaFogService();
|
||||
|
||||
private final float[] fogColorArray = new float[4];
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,11 +2,15 @@ package com.gtnewhorizons.angelica.commands;
|
||||
|
||||
// Debug commands adapted from Beddium by Ven and FalsePattern
|
||||
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.debug.ChunkDebugMinimap;
|
||||
import com.gtnewhorizons.angelica.debug.flyby.FlybyRoute;
|
||||
import com.gtnewhorizons.angelica.debug.flyby.FlybyRunner;
|
||||
import com.gtnewhorizons.angelica.debug.profiling.AsprofRecorder;
|
||||
import com.gtnewhorizons.angelica.debug.profiling.TracyCaptureNotifier;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.TracyBackend;
|
||||
import com.gtnewhorizons.angelica.rendering.RenderRecovery;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasDebugScreenHandler;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer;
|
||||
@@ -19,23 +23,42 @@ import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class AngelicaCommand extends CommandBase {
|
||||
|
||||
private static final List<String> SUBCOMMANDS = buildSubcommands();
|
||||
private static final Map<String, String> HELP = buildHelp();
|
||||
private static final String[] SUBCOMMANDS = buildSubcommands();
|
||||
private static final String USAGE = "/angelica <" + String.join("|", SUBCOMMANDS) + ">";
|
||||
|
||||
private static List<String> buildSubcommands() {
|
||||
final List<String> subcommands = new ArrayList<>(Arrays.asList("wireframe", "fog", "minimap", "flyby", "profile", "help"));
|
||||
if (isDeobfuscatedEnvironment()) {
|
||||
subcommands.add("crashtest");
|
||||
private static Map<String, String> buildHelp() {
|
||||
final Map<String, String> help = new LinkedHashMap<>();
|
||||
if (SystemProperties.debugTooling()) {
|
||||
help.put("wireframe", helpLine("wireframe", "Toggle wireframe rendering"));
|
||||
help.put("fog", helpLine("fog", "Toggle fog debug on F3"));
|
||||
help.put("minimap", helpLine("minimap", "Toggle chunk debug overlay"));
|
||||
help.put("flyby", helpLine("flyby <" + FlybyRoute.ids() + ">", "Run a deterministic benchmark route"));
|
||||
help.put("profile", helpLine("profile <start|stop|status>", "Control async-profiler (JFR) recording"));
|
||||
if (SystemProperties.isDeobf()) {
|
||||
help.put("crashtest", helpLine("crashtest", "Arm a crash on the next tile entity render"));
|
||||
}
|
||||
}
|
||||
if (Tracy.ENABLED) {
|
||||
help.put("tracy", helpLine("tracy <start|stop|status>", "Control Tracy capture recording"));
|
||||
}
|
||||
return help;
|
||||
}
|
||||
|
||||
private static String[] buildSubcommands() {
|
||||
final String[] subcommands = HELP.keySet().toArray(new String[HELP.size() + 1]);
|
||||
subcommands[HELP.size()] = "help";
|
||||
return subcommands;
|
||||
}
|
||||
|
||||
private static boolean isDeobfuscatedEnvironment() {
|
||||
return SystemProperties.isDeobf();
|
||||
private static String helpLine(String syntax, String description) {
|
||||
return EnumChatFormatting.GRAY + " /angelica " + syntax + EnumChatFormatting.WHITE + " - " + description;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,7 +68,7 @@ public class AngelicaCommand extends CommandBase {
|
||||
|
||||
@Override
|
||||
public String getCommandUsage(ICommandSender sender) {
|
||||
return "/angelica <wireframe|fog|minimap|flyby|profile" + (isDeobfuscatedEnvironment() ? "|crashtest" : "") + "|help>";
|
||||
return USAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,7 +96,7 @@ public class AngelicaCommand extends CommandBase {
|
||||
@Override
|
||||
public List<String> addTabCompletionOptions(ICommandSender sender, String[] args) {
|
||||
if (args.length == 1) {
|
||||
return getListOfStringsMatchingLastWord(args, SUBCOMMANDS.toArray(new String[0]));
|
||||
return getListOfStringsMatchingLastWord(args, SUBCOMMANDS);
|
||||
}
|
||||
if (args.length == 2 && "flyby".equalsIgnoreCase(args[0])) {
|
||||
final List<String> options = new ArrayList<>();
|
||||
@@ -84,7 +107,10 @@ public class AngelicaCommand extends CommandBase {
|
||||
return getListOfStringsMatchingLastWord(args, options.toArray(new String[0]));
|
||||
}
|
||||
if (args.length == 2 && "profile".equalsIgnoreCase(args[0])) {
|
||||
return getListOfStringsMatchingLastWord(args, new String[] { "start", "stop", "status" });
|
||||
return getListOfStringsMatchingLastWord(args, "start", "stop", "status");
|
||||
}
|
||||
if (args.length == 2 && "tracy".equalsIgnoreCase(args[0])) {
|
||||
return getListOfStringsMatchingLastWord(args, "start", "stop", "status");
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
@@ -97,6 +123,10 @@ public class AngelicaCommand extends CommandBase {
|
||||
}
|
||||
|
||||
final String subcommand = args[0].toLowerCase();
|
||||
if (!HELP.containsKey(subcommand)) {
|
||||
sendHelp(sender);
|
||||
return;
|
||||
}
|
||||
if (requiresCheats(subcommand) && !cheatsAllowed()) {
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "[Angelica] " + subcommand + " needs cheats enabled on a world you host"));
|
||||
return;
|
||||
@@ -108,14 +138,8 @@ public class AngelicaCommand extends CommandBase {
|
||||
case "minimap" -> handleMinimap(sender);
|
||||
case "flyby" -> handleFlyby(sender, args);
|
||||
case "profile" -> handleProfile(sender, args);
|
||||
case "crashtest" -> {
|
||||
if (isDeobfuscatedEnvironment()) {
|
||||
handleCrashTest(sender);
|
||||
} else {
|
||||
sendHelp(sender);
|
||||
}
|
||||
}
|
||||
default -> sendHelp(sender);
|
||||
case "tracy" -> handleTracy(sender, args);
|
||||
case "crashtest" -> handleCrashTest(sender);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,15 +263,75 @@ public class AngelicaCommand extends CommandBase {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleTracy(ICommandSender sender, String[] args) {
|
||||
if (args.length < 2) {
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.AQUA + "[Angelica] " + EnumChatFormatting.WHITE + "Usage: /angelica tracy <start|stop|status> [seconds]"));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args[1].toLowerCase()) {
|
||||
case "start" -> {
|
||||
int seconds = AngelicaConfig.tracyCaptureSeconds;
|
||||
if (args.length > 2) {
|
||||
try {
|
||||
seconds = Integer.parseInt(args[2]);
|
||||
} catch (NumberFormatException e) {
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.AQUA + "[Angelica] " + EnumChatFormatting.WHITE + "Usage: /angelica tracy start [seconds]"));
|
||||
return;
|
||||
}
|
||||
if (seconds < 0) {
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.AQUA + "[Angelica] " + EnumChatFormatting.WHITE + "Usage: /angelica tracy start [seconds]"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
final String error = TracyCaptureNotifier.INSTANCE.startCapture(seconds);
|
||||
if (error != null) {
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "[Angelica] " + error));
|
||||
} else {
|
||||
final String length = seconds > 0 ? seconds + "s" : "until stopped";
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.AQUA + "[Angelica] " + EnumChatFormatting.WHITE + "Tracy capture started (" + length + "): " + TracyCaptureNotifier.INSTANCE.path()));
|
||||
}
|
||||
}
|
||||
case "stop" -> {
|
||||
final int state = Tracy.captureState();
|
||||
if (state == TracyBackend.CAPTURE_CONNECTING || state == TracyBackend.CAPTURE_RECORDING) {
|
||||
Tracy.captureStop();
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.AQUA + "[Angelica] " + EnumChatFormatting.WHITE + "Stopping Tracy capture, saving..."));
|
||||
} else if (state == TracyBackend.CAPTURE_SAVING) {
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "[Angelica] Tracy capture is already saving"));
|
||||
} else {
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "[Angelica] No Tracy capture is running"));
|
||||
}
|
||||
}
|
||||
case "status" -> {
|
||||
final int state = Tracy.captureState();
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.AQUA + "[Angelica] " + EnumChatFormatting.WHITE + "Tracy capture: " + tracyStateName(state) + " (" + Tracy.captureElapsedMs() / 1000L + "s)"));
|
||||
if (state == TracyBackend.CAPTURE_FAILED) {
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.GRAY + " " + Tracy.captureError()));
|
||||
} else if (TracyCaptureNotifier.INSTANCE.path() != null) {
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.GRAY + " " + TracyCaptureNotifier.INSTANCE.path()));
|
||||
}
|
||||
}
|
||||
default -> sender.addChatMessage(new ChatComponentText(EnumChatFormatting.AQUA + "[Angelica] " + EnumChatFormatting.WHITE + "Usage: /angelica tracy <start|stop|status> [seconds]"));
|
||||
}
|
||||
}
|
||||
|
||||
private static String tracyStateName(int state) {
|
||||
return switch (state) {
|
||||
case TracyBackend.CAPTURE_IDLE -> "idle";
|
||||
case TracyBackend.CAPTURE_CONNECTING -> "connecting";
|
||||
case TracyBackend.CAPTURE_RECORDING -> "recording";
|
||||
case TracyBackend.CAPTURE_SAVING -> "saving";
|
||||
case TracyBackend.CAPTURE_DONE -> "done";
|
||||
case TracyBackend.CAPTURE_FAILED -> "failed";
|
||||
default -> "unknown";
|
||||
};
|
||||
}
|
||||
|
||||
private void sendHelp(ICommandSender sender) {
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.AQUA + "[Angelica] Debug Commands:"));
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.GRAY + " /angelica wireframe" + EnumChatFormatting.WHITE + " - Toggle wireframe rendering"));
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.GRAY + " /angelica fog" + EnumChatFormatting.WHITE + " - Toggle fog debug on F3"));
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.GRAY + " /angelica minimap" + EnumChatFormatting.WHITE + " - Toggle chunk debug overlay"));
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.GRAY + " /angelica flyby <" + FlybyRoute.ids() + ">" + EnumChatFormatting.WHITE + " - Run a deterministic benchmark route"));
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.GRAY + " /angelica profile <start|stop|status>" + EnumChatFormatting.WHITE + " - Control async-profiler (JFR) recording"));
|
||||
if (isDeobfuscatedEnvironment()) {
|
||||
sender.addChatMessage(new ChatComponentText(EnumChatFormatting.GRAY + " /angelica crashtest" + EnumChatFormatting.WHITE + " - Arm a crash on the next tile entity render"));
|
||||
for (String line : HELP.values()) {
|
||||
sender.addChatMessage(new ChatComponentText(line));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.gtnewhorizons.angelica.compat.draconicevolution;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.BatchEligibility;
|
||||
import net.minecraft.client.renderer.entity.RenderItem;
|
||||
import net.minecraft.item.ItemBlock;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.client.IItemRenderer.ItemRenderType;
|
||||
import net.minecraftforge.client.MinecraftForgeClient;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public final class PlacedItemRenderCompat {
|
||||
private PlacedItemRenderCompat() {}
|
||||
|
||||
public static boolean requiresLiveRender(ItemStack stack) {
|
||||
if (stack == null) return false;
|
||||
if (MinecraftForgeClient.getItemRenderer(stack, ItemRenderType.ENTITY) != null) return true;
|
||||
//TODO: Properly optimize this later, right now it's breaking everything.
|
||||
return stack.getItem() instanceof ItemBlock;
|
||||
}
|
||||
|
||||
public static void render(double x, double y, double z, Runnable draw) {
|
||||
final boolean inFrame = RenderItem.renderInFrame;
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
try {
|
||||
GLStateManager.glTranslated(x, y, z);
|
||||
draw.run();
|
||||
} finally {
|
||||
RenderItem.renderInFrame = inFrame;
|
||||
GLStateManager.glPopAttrib();
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
public static byte renderWithBatchState(double x, double y, double z, byte state, Runnable draw) {
|
||||
BatchEligibility.beginIsolated(state, GLStateManager.drawCalls);
|
||||
try {
|
||||
render(x, y, z, draw);
|
||||
} finally {
|
||||
state = BatchEligibility.endIsolated(state, GLStateManager.drawCalls);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizons.angelica.api.tesr.TesrMaterial;
|
||||
import com.gtnewhorizons.angelica.api.tesr.TesrShader;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.TextureUtilHooks;
|
||||
import com.gtnewhorizons.angelica.rendering.tesr.DrawState;
|
||||
import com.gtnewhorizons.angelica.shadercompat.ShaderGlint;
|
||||
import it.unimi.dsi.fastutil.Hash;
|
||||
@@ -18,7 +19,6 @@ import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.layer.PassOverride;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@@ -86,18 +86,22 @@ public final class RenderLayer implements BlendingStateHolder {
|
||||
this.hash = h;
|
||||
}
|
||||
|
||||
public static RenderLayer tesr(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits, int glintSlot, int cull, boolean lit) {
|
||||
return build(texture, material, pass, offsetFactor, offsetUnits, glintSlot, cull, lit, false);
|
||||
public static RenderLayer tesr(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits, int glintSlot, int cull, boolean lit, boolean lightmap) {
|
||||
return tesr(texture, material, pass, offsetFactor, offsetUnits, glintSlot, cull, lit, lightmap, false);
|
||||
}
|
||||
|
||||
public static RenderLayer tesrNoPass(ResourceLocation texture, TesrMaterial material, int cull, boolean lit) {
|
||||
return build(texture, material, PassOverride.NONE, 0.0f, 0.0f, ShaderGlint.NO_TINT, cull, lit, true);
|
||||
public static RenderLayer tesr(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits, int glintSlot, int cull, boolean lit, boolean lightmap, boolean afterGlint) {
|
||||
return build(texture, material, pass, offsetFactor, offsetUnits, glintSlot, cull, lit, lightmap, false, afterGlint);
|
||||
}
|
||||
|
||||
private static RenderLayer build(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits, int glintSlot, int cull, boolean lit, boolean noPass) {
|
||||
final DrawState state = DrawState.forMaterial(material, cull, lit, offsetFactor, offsetUnits);
|
||||
public static RenderLayer tesrNoPass(ResourceLocation texture, TesrMaterial material, int cull, boolean lit, boolean lightmap) {
|
||||
return build(texture, material, PassOverride.NONE, 0.0f, 0.0f, ShaderGlint.NO_TINT, cull, lit, lightmap, true, false);
|
||||
}
|
||||
|
||||
private static RenderLayer build(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits, int glintSlot, int cull, boolean lit, boolean lightmap, boolean noPass, boolean afterGlint) {
|
||||
final DrawState state = DrawState.forMaterial(material, cull, lit, lightmap, offsetFactor, offsetUnits);
|
||||
final Hook hook = hookFor(material, noPass ? PassOverride.NONE : pass, glintSlot);
|
||||
final TransparencyType transparency = transparencyFor(state.getBlend());
|
||||
final TransparencyType transparency = afterGlint ? TransparencyType.AFTER_GLINT : transparencyFor(state.getBlend());
|
||||
final RenderLayer candidate = new RenderLayer(null, BatchVertexFormats.POSITION_COLOR_TEXTURE_LIGHTF_NORMAL,
|
||||
GL11.GL_QUADS, TESR_BUFFER_SIZE, state, texture, material.isUnfilteredAtlas(), transparency, hook,
|
||||
noPass);
|
||||
@@ -106,8 +110,8 @@ public final class RenderLayer implements BlendingStateHolder {
|
||||
retained.name = "angelica_tesr_" + (noPass ? "nopass_" : "")
|
||||
+ material.transparency().name().toLowerCase(Locale.ROOT) + (noPass ? "" : pass.nameSuffix())
|
||||
+ (offsetFactor == 0.0f && offsetUnits == 0.0f ? "" : "_offset" + offsetFactor + "_" + offsetUnits)
|
||||
+ "_cull" + state.getCull() + (state.isLit() ? "_lit" : "")
|
||||
+ (glintSlot == ShaderGlint.NO_TINT ? "" : "_tint" + glintSlot);
|
||||
+ "_cull" + state.getCull() + (state.isLit() ? "_lit" : "") + (state.isLightmap() ? "" : "_nolightmap")
|
||||
+ (glintSlot == ShaderGlint.NO_TINT ? "" : "_tint" + glintSlot) + (afterGlint ? "_after_glint" : "");
|
||||
}
|
||||
return retained;
|
||||
}
|
||||
@@ -200,7 +204,7 @@ public final class RenderLayer implements BlendingStateHolder {
|
||||
Minecraft.getMinecraft().getTextureManager().bindTexture(textureId);
|
||||
if (unfilteredAtlas) {
|
||||
filtering = true;
|
||||
TextureUtil.func_152777_a(false, false, 1.0F);
|
||||
TextureUtilHooks.forceNearestFilter();
|
||||
}
|
||||
} else {
|
||||
GLStateManager.disableTexture();
|
||||
@@ -219,7 +223,7 @@ public final class RenderLayer implements BlendingStateHolder {
|
||||
}
|
||||
if (filtering) {
|
||||
try {
|
||||
TextureUtil.func_147945_b();
|
||||
TextureUtilHooks.restoreFilterAfterNearest();
|
||||
} catch (Throwable cleanup) {
|
||||
RenderFailures.suppress(failure, cleanup);
|
||||
}
|
||||
@@ -237,7 +241,7 @@ public final class RenderLayer implements BlendingStateHolder {
|
||||
} finally {
|
||||
if (textureId != null && unfilteredAtlas) {
|
||||
try {
|
||||
TextureUtil.func_147945_b();
|
||||
TextureUtilHooks.restoreFilterAfterNearest();
|
||||
} catch (Throwable cleanup) {
|
||||
failure = RenderFailures.suppress(failure, cleanup);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.gtnewhorizons.angelica.compat.thaumcraft;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPhase;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
|
||||
import net.coderbot.iris.uniforms.ItemIdManager;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** The held scanner's pane, icons and labels must follow translucent world geometry. */
|
||||
public final class ThaumometerScreen {
|
||||
private static final List<Capture> pending = new ArrayList<>();
|
||||
|
||||
private ThaumometerScreen() {}
|
||||
|
||||
public static Capture begin() {
|
||||
if (DisplayListManager.isRecording()) return null;
|
||||
final Capture capture = new Capture();
|
||||
GLStateManager.glNewList(capture.list, GL11.GL_COMPILE);
|
||||
return capture;
|
||||
}
|
||||
|
||||
public static void finish(Capture capture) {
|
||||
GLStateManager.glEndList();
|
||||
pending.add(capture);
|
||||
}
|
||||
|
||||
public static void abort(Capture capture) {
|
||||
if (DisplayListManager.isRecording()) GLStateManager.glEndList();
|
||||
GLStateManager.glDeleteLists(capture.list, 1);
|
||||
}
|
||||
|
||||
public static void discard() {
|
||||
for (Capture capture : pending) GLStateManager.glDeleteLists(capture.list, 1);
|
||||
pending.clear();
|
||||
}
|
||||
|
||||
public static void render(WorldRenderingPipeline pipeline) {
|
||||
if (pending.isEmpty()) return;
|
||||
final WorldRenderingPhase phase = pipeline.getPhase();
|
||||
final Matrix4f texture = new Matrix4f(GLStateManager.getTextures().getTextureUnitMatrix(0));
|
||||
final Matrix4f lightmap = new Matrix4f(GLStateManager.getTextures().getTextureUnitMatrix(1));
|
||||
final float brightnessX = GLStateManager.ctx().lastBrightnessX;
|
||||
final float brightnessY = GLStateManager.ctx().lastBrightnessY;
|
||||
final Boolean translucency = GbufferPrograms.beginTranslucencyDeclaration(true);
|
||||
GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPushMatrix();
|
||||
ItemIdManager.pushItemId();
|
||||
try {
|
||||
pipeline.setPhase(WorldRenderingPhase.HAND_TRANSLUCENT);
|
||||
GbufferPrograms.setBlockEntityDefaults();
|
||||
GLStateManager.enableDepthTest();
|
||||
GLStateManager.glDepthMask(false);
|
||||
for (Capture capture : pending) capture.replay();
|
||||
} finally {
|
||||
ItemIdManager.popItemId();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.setTextureMatrix(0, texture);
|
||||
GLStateManager.setTextureMatrix(1, lightmap);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glPopAttrib();
|
||||
GLStateManager.setLightmapTextureCoords(GL13.GL_TEXTURE1, brightnessX, brightnessY);
|
||||
GbufferPrograms.endTranslucencyDeclaration(translucency);
|
||||
pipeline.setPhase(phase);
|
||||
discard();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Capture {
|
||||
private final int list = GLStateManager.glGenLists(1);
|
||||
private final Matrix4f modelView = new Matrix4f(GLStateManager.getModelViewMatrix());
|
||||
private final Matrix4f projection = new Matrix4f(GLStateManager.getProjectionMatrix());
|
||||
private final Matrix4f texture = new Matrix4f(GLStateManager.getTextures().getTextureUnitMatrix(0));
|
||||
private final Matrix4f lightmapMatrix = new Matrix4f(GLStateManager.getTextures().getTextureUnitMatrix(1));
|
||||
private final float brightnessX = GLStateManager.ctx().lastBrightnessX;
|
||||
private final float brightnessY = GLStateManager.ctx().lastBrightnessY;
|
||||
private final Vector3f normal = new Vector3f(ShaderManager.getCurrentNormal());
|
||||
private final AlphaState alphaState = GLStateManager.getAlphaState().readEffective(new AlphaState());
|
||||
private final int depthFunction = GLStateManager.getDepthState().getFunc();
|
||||
private final int itemId = ItemIdManager.getItemId();
|
||||
private final boolean cull = GLStateManager.getCullState().isEnabled();
|
||||
private final boolean alpha = GLStateManager.getAlphaTest().isEffectivelyEnabled();
|
||||
private final boolean lightmap = GLStateManager.getTextures().getTextureUnitStates(1).isEnabled();
|
||||
private final boolean lighting = GLStateManager.getLightingState().isEnabled();
|
||||
|
||||
private void replay() {
|
||||
GLStateManager.setProjectionMatrix(projection);
|
||||
GLStateManager.setModelViewMatrix(modelView);
|
||||
GLStateManager.setTextureMatrix(0, texture);
|
||||
GLStateManager.setTextureMatrix(1, lightmapMatrix);
|
||||
GLStateManager.setLightmapTextureCoords(GL13.GL_TEXTURE1, brightnessX, brightnessY);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glNormal3f(normal.x, normal.y, normal.z);
|
||||
GLStateManager.glDepthFunc(depthFunction);
|
||||
GLStateManager.glAlphaFunc(alphaState.getFunction(), alphaState.getReference());
|
||||
ItemIdManager.setItemIdRaw(itemId);
|
||||
if (cull) GLStateManager.enableCull(); else GLStateManager.disableCull();
|
||||
if (alpha) GLStateManager.enableAlphaTest(); else GLStateManager.disableAlphaTest();
|
||||
if (lighting) GLStateManager.enableLighting(); else GLStateManager.disableLighting();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
if (lightmap) GLStateManager.enableTexture(); else GLStateManager.disableTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
GLStateManager.glCallList(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,6 +419,10 @@ public class AngelicaConfig {
|
||||
@Config.DefaultBoolean(false)
|
||||
public static boolean disableF3Additions;
|
||||
|
||||
@Config.Comment("Shows developer counters (FFP, streaming, TESR, transfer) on the F3 screen. Toggle in game with F3+V.")
|
||||
@Config.DefaultBoolean(false)
|
||||
public static boolean verboseF3;
|
||||
|
||||
@Config.Comment("Replaces various FFP uploads with statically allocated VBO's.")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean replaceFFPUploads;
|
||||
@@ -468,6 +472,32 @@ public class AngelicaConfig {
|
||||
"minecraft:andesite", "minecraft:dirt", "minecraft:granite", "minecraft:grass", "minecraft:mycelium", "minecraft:sand", "minecraft:soul_sand", "etfuturum:calcite", "etfuturum:coarse_dirt", "etfuturum:concrete_powder", "etfuturum:grass_path", "BiomesOPlenty:ash", "BiomesOPlenty:driedDirt", "BiomesOPlenty:hardDirt", "BiomesOPlenty:hardSand", "BiomesOPlenty:mud", "BiomesOPlenty:newBopDirt", "Botania:dirtPath", "Botania:enchantedSoil", "Botania:livingrock", "Botania:prismarine", "Botania:shimmerrock", "Botany:loam", "Botany:loamNoWeed", "Botany:soil", "Botany:soilNoWeed", "chisel:moss", "chisel:moss_carpet", "ExtraUtilities:color_hellsand", "ExtraUtilities:cursedearthside", "GalaxySpace:acentauribbgrunt", "GalaxySpace:acentauribbsubgrunt", "GalaxySpace:barnardaCdirt", "GalaxySpace:barnardaEgrunt", "GalaxySpace:barnardaEsubgrunt", "GalaxySpace:barnardaFgrunt", "GalaxySpace:barnardaFsubgrunt", "GalaxySpace:callistoblocks", "GalaxySpace:ceresblocks", "GalaxySpace:deimosblocks", "GalaxySpace:europagrunt", "GalaxySpace:ganymedeblocks", "GalaxySpace:haumeablocks", "GalaxySpace:ioblocks", "GalaxySpace:makemakegrunt", "GalaxySpace:mercuryblocks", "GalaxySpace:mirandablocks", "GalaxySpace:oberonblocks", "GalaxySpace:phobosblocks", "GalaxySpace:proteusblocks", "GalaxySpace:tcetieblocks", "GalaxySpace:titanblocks", "GalaxySpace:tritonblocks", "GalaxySpace:vegabgrunt", "GalaxySpace:vegabsubgrunt", "GalaxySpace:venusblocks", "gregtech:gt.blockgranites", "IC2:blockBasalt", "MagicBees:magicbees.enchantedEarth", "RandomThings:fertilizedDirt", "ToxicEverglades:blockDarkWorldGround2", "VillageNames:concretePowder", "witchery:pitdirt"
|
||||
};
|
||||
|
||||
@Config.Comment("Enable the Tracy profiler backend. Requires angelica-tracy.jar on the classpath. Overridden by -Dangelica.tracy")
|
||||
@Config.DefaultBoolean(false)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean enableTracy;
|
||||
|
||||
@Config.Comment("Allow Tracy to accept connections from other machines, not just localhost")
|
||||
@Config.DefaultBoolean(false)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean tracyAllowRemote;
|
||||
|
||||
@Config.Comment("Emit Tracy zones for many more call sites. More detail, more overhead. Overridden by -Dangelica.tracy.fineZones")
|
||||
@Config.DefaultBoolean(false)
|
||||
@Config.RequiresMcRestart
|
||||
public static boolean tracyFineZones;
|
||||
|
||||
@Config.Comment("Max distinct Tracy zone/message call sites to preallocate. Overridden by -Dangelica.tracy.maxSrcLocs")
|
||||
@Config.DefaultInt(4096)
|
||||
@Config.RangeInt(min = 1024, max = 16384)
|
||||
@Config.RequiresMcRestart
|
||||
public static int tracyMaxSrcLocs;
|
||||
|
||||
@Config.Comment("Default length in seconds for a Tracy capture started from the command or video settings. 0 = until stopped")
|
||||
@Config.DefaultInt(60)
|
||||
@Config.RangeInt(min = 0, max = 3600)
|
||||
public static int tracyCaptureSeconds;
|
||||
|
||||
public static void applyGpuCullingMode() {
|
||||
GpuCulling.setMode(gpuCullingMode == null ? GpuCullingMode.CPU_ONLY : gpuCullingMode);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.gtnewhorizons.angelica.debug;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.GTNHLib;
|
||||
import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasDebugScreenHandler;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.InputEvent;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.StatCollector;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
/** F3+V toggles {@link AngelicaConfig#verboseF3} */
|
||||
public class DebugKeyHandler {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onKey(InputEvent.KeyInputEvent event) {
|
||||
if (!Keyboard.getEventKeyState() || Keyboard.getEventKey() != Keyboard.KEY_V || !Keyboard.isKeyDown(Keyboard.KEY_F3)
|
||||
|| Minecraft.getMinecraft().currentScreen != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AngelicaConfig.verboseF3 = !AngelicaConfig.verboseF3;
|
||||
ConfigurationManager.save(AngelicaConfig.class);
|
||||
CeleritasDebugScreenHandler.invalidate();
|
||||
GTNHLib.proxy.addDebugToChat(StatCollector.translateToLocal(AngelicaConfig.verboseF3 ? "angelica.debug.verbose.on" : "angelica.debug.verbose.off"));
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ public final class AsprofRecorder {
|
||||
if (!available()) return "async-profiler is not available on this platform/build";
|
||||
if (SESSION.recordingId() != 0) return "already recording to " + SESSION.outputPath();
|
||||
|
||||
File file = SystemProperties.PROFILE_OUTPUT.isEmpty() ? new File(SystemProperties.PROFILE_DIR, fileName(tag, System.currentTimeMillis())) : new File(SystemProperties.PROFILE_OUTPUT);
|
||||
File file = SystemProperties.PROFILE_OUTPUT.isEmpty() ? new File(SystemProperties.PROFILE_DIR, fileName(tag, "jfr", System.currentTimeMillis())) : new File(SystemProperties.PROFILE_OUTPUT);
|
||||
file = file.getAbsoluteFile();
|
||||
if (file.exists()) return "output file already exists: " + file.getPath();
|
||||
final File parent = file.getParentFile();
|
||||
@@ -121,9 +121,9 @@ public final class AsprofRecorder {
|
||||
return opts.isEmpty() ? "start,jfr,file=" + file : "start,jfr," + opts + ",file=" + file;
|
||||
}
|
||||
|
||||
static String fileName(String tag, long nowMillis) {
|
||||
static String fileName(String tag, String ext, long nowMillis) {
|
||||
final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.ROOT);
|
||||
return "angelica-" + tag + "-" + format.format(new Date(nowMillis)) + ".jfr";
|
||||
return "angelica-" + tag + "-" + format.format(new Date(nowMillis)) + "." + ext;
|
||||
}
|
||||
|
||||
static String firstLine(String s) {
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.gtnewhorizons.angelica.debug.profiling;
|
||||
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.TracyBackend;
|
||||
import cpw.mods.fml.client.registry.ClientRegistry;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class TracyCaptureNotifier {
|
||||
private static final Logger LOGGER = LogManager.getLogger("Angelica/Tracy");
|
||||
|
||||
public static final TracyCaptureNotifier INSTANCE = new TracyCaptureNotifier();
|
||||
|
||||
private File file;
|
||||
private boolean inFlight;
|
||||
private KeyBinding captureKey;
|
||||
|
||||
private TracyCaptureNotifier() {}
|
||||
|
||||
public void register() {
|
||||
this.captureKey = new KeyBinding("angelica.keybind.tracyCapture", 0, AngelicaMod.KEY_CATEGORY);
|
||||
ClientRegistry.registerKeyBinding(this.captureKey);
|
||||
FMLCommonHandler.instance().bus().register(this);
|
||||
}
|
||||
|
||||
public void toggle() {
|
||||
final int state = Tracy.captureState();
|
||||
if (state == TracyBackend.CAPTURE_CONNECTING || state == TracyBackend.CAPTURE_RECORDING) {
|
||||
Tracy.captureStop();
|
||||
} else if (state != TracyBackend.CAPTURE_SAVING) {
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
final String error = this.startCapture(AngelicaConfig.tracyCaptureSeconds);
|
||||
if (error != null) {
|
||||
report(mc, true, "Tracy capture failed to start: " + error);
|
||||
} else {
|
||||
report(mc, false, "Tracy capture started: " + this.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String startCapture(int seconds) {
|
||||
final File candidate = new File(SystemProperties.PROFILE_DIR, AsprofRecorder.fileName("tracy", "tracy", System.currentTimeMillis()));
|
||||
final String error = Tracy.captureStart(candidate.getPath(), seconds);
|
||||
if (error != null) return error;
|
||||
this.file = candidate;
|
||||
this.inFlight = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
public String path() {
|
||||
return this.file == null ? null : this.file.getAbsolutePath();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onClientTick(TickEvent.ClientTickEvent event) {
|
||||
if (event.phase != TickEvent.Phase.END) return;
|
||||
|
||||
if (this.captureKey != null && this.captureKey.isPressed()) {
|
||||
this.toggle();
|
||||
}
|
||||
|
||||
if (!this.inFlight) return;
|
||||
final Minecraft mc = Minecraft.getMinecraft();
|
||||
final int state = Tracy.captureState();
|
||||
if (state == TracyBackend.CAPTURE_DONE) {
|
||||
report(mc, false, "Tracy capture saved: " + this.path() + sizeSuffix(this.file));
|
||||
this.inFlight = false;
|
||||
} else if (state == TracyBackend.CAPTURE_FAILED) {
|
||||
report(mc, true, "Tracy capture failed: " + Tracy.captureError());
|
||||
this.inFlight = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String sizeSuffix(File file) {
|
||||
if (file == null) return "";
|
||||
final long bytes = file.length();
|
||||
return String.format(Locale.ROOT, " (%.1f MB)", bytes / (1024.0 * 1024.0));
|
||||
}
|
||||
|
||||
private static void report(Minecraft mc, boolean failure, String text) {
|
||||
if (mc.thePlayer != null) {
|
||||
mc.thePlayer.addChatMessage(new ChatComponentText((failure ? EnumChatFormatting.RED : EnumChatFormatting.AQUA) + "[Angelica] " + text));
|
||||
} else if (failure) {
|
||||
LOGGER.warn(text);
|
||||
} else {
|
||||
LOGGER.info(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
package com.gtnewhorizons.angelica.iris;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.CommandRecorder;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.DisplayListCommand;
|
||||
import com.gtnewhorizons.angelica.shadercompat.ShaderGlint;
|
||||
import com.gtnewhorizons.angelica.rendering.FallingBlockRendering;
|
||||
import it.unimi.dsi.fastutil.booleans.BooleanArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
|
||||
import net.coderbot.iris.layer.GbufferPrograms;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPhase;
|
||||
import net.coderbot.iris.shaderpack.materialmap.NamespacedId;
|
||||
import net.coderbot.iris.uniforms.CapturedRenderingState;
|
||||
import net.coderbot.iris.uniforms.EntityIdHelper;
|
||||
import net.coderbot.iris.uniforms.ItemIdManager;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
/**
|
||||
* Records Java-side rendering scopes alongside cached geometry.
|
||||
*/
|
||||
public final class IrisDisplayListState {
|
||||
|
||||
private IrisDisplayListState() {}
|
||||
|
||||
public static void runProgramTransition(Runnable transition) {
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
runUnrecorded(transition);
|
||||
} finally {
|
||||
BatchStateGuard.resume();
|
||||
}
|
||||
}
|
||||
|
||||
public static void runUnrecorded(Runnable transition) {
|
||||
final DisplayListManager.RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode == DisplayListManager.RecordMode.COMPILE) return;
|
||||
if (mode == DisplayListManager.RecordMode.NONE) {
|
||||
transition.run();
|
||||
return;
|
||||
}
|
||||
final CommandRecorder recorder = DisplayListManager.pauseRecording();
|
||||
try {
|
||||
transition.run();
|
||||
} finally {
|
||||
DisplayListManager.resumeRecording(recorder);
|
||||
}
|
||||
}
|
||||
|
||||
private static void record(DisplayListCommand cmd) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.recordStateCommand(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
public static void recordSpecialCondition(SpecialCondition condition, boolean begin) {
|
||||
if (DisplayListManager.isRecording()) record(new SpecialConditionCmd(condition, begin));
|
||||
}
|
||||
|
||||
public static void recordGlintSpan(boolean begin) {
|
||||
if (DisplayListManager.isRecording()) record(new GlintSpanCmd(begin));
|
||||
}
|
||||
|
||||
/** Keep nested items on the entity pass, including its depth and TAA behavior. */
|
||||
public static void recordOverridePhase(WorldRenderingPhase phase) {
|
||||
if (DisplayListManager.isRecording()) record(new OverridePhaseCmd(phase));
|
||||
}
|
||||
|
||||
public static void recordPhaseScope(WorldRenderingPhase phase, boolean begin) {
|
||||
if (DisplayListManager.isRecording()) record(new PhaseScopeCmd(phase, begin));
|
||||
}
|
||||
|
||||
public static void recordNestedEntityScope(boolean begin) {
|
||||
if (DisplayListManager.isRecording()) record(new NestedEntityScopeCmd(begin));
|
||||
}
|
||||
|
||||
public static void recordTranslucency(Boolean translucent) {
|
||||
if (DisplayListManager.isRecording()) record(new TranslucencyCmd(translucent));
|
||||
}
|
||||
|
||||
public static void recordTranslucencyScope(Boolean translucent, boolean begin) {
|
||||
if (DisplayListManager.isRecording()) record(new TranslucencyScopeCmd(translucent, begin));
|
||||
}
|
||||
|
||||
/** Record explicit IDs as well as item identities so replay cannot inherit a previous draw's ID. */
|
||||
public static void recordItemId(int itemId) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.recordStateCommandIfChanged(new ItemIdCmd(itemId));
|
||||
}
|
||||
}
|
||||
|
||||
public static void recordItem(ItemStack item) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
record(new ItemCmd(item == null ? null : item.copy()));
|
||||
}
|
||||
}
|
||||
|
||||
public static void recordItem(Item item, int metadata) {
|
||||
if (DisplayListManager.isRecording()) record(new ItemTypeCmd(item, metadata));
|
||||
}
|
||||
|
||||
public static void recordNamedItem(NamespacedId item) {
|
||||
if (DisplayListManager.isRecording()) record(new NamedItemCmd(item));
|
||||
}
|
||||
|
||||
public static void recordBlockItem(Block block, int metadata) {
|
||||
if (DisplayListManager.isRecording()) record(new BlockItemCmd(block, metadata));
|
||||
}
|
||||
|
||||
public static void recordItemScope(boolean push) {
|
||||
if (DisplayListManager.isRecording()) record(new ItemScopeCmd(push));
|
||||
}
|
||||
|
||||
public static void recordEntity(Entity entity) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
record(new EntityCmd(EntityIdHelper.snapshot(entity)));
|
||||
}
|
||||
}
|
||||
|
||||
public static void recordEntityScope(boolean push) {
|
||||
if (DisplayListManager.isRecording()) record(new EntityScopeCmd(push));
|
||||
}
|
||||
|
||||
public static void recordEntityAndItem(int entity, int item) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.recordStateCommandIfChanged(new EntityAndItemCmd(entity, item));
|
||||
}
|
||||
}
|
||||
|
||||
public static void recordNamedEntity(NamespacedId entity) {
|
||||
if (DisplayListManager.isRecording()) record(new NamedEntityCmd(entity));
|
||||
}
|
||||
|
||||
public static void recordBlockEntity(int id) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.recordStateCommandIfChanged(new BlockEntityIdCmd(id));
|
||||
}
|
||||
}
|
||||
|
||||
public static void recordBlockEntity(Block block, int metadata) {
|
||||
if (DisplayListManager.isRecording()) record(new BlockEntityCmd(block, metadata));
|
||||
}
|
||||
|
||||
public static void recordBlockEntityScope(boolean push) {
|
||||
if (DisplayListManager.isRecording()) record(new BlockEntityScopeCmd(push));
|
||||
}
|
||||
|
||||
public static void recordEntityColor(float r, float g, float b, float a) {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
DisplayListManager.recordStateCommandIfChanged(new EntityColorCmd(r, g, b, a));
|
||||
}
|
||||
}
|
||||
|
||||
public static void recordEntityColorScope(boolean push) {
|
||||
if (DisplayListManager.isRecording()) record(new EntityColorScopeCmd(push));
|
||||
}
|
||||
|
||||
public static void recordBlockEntityAttribute(Block block, int metadata) {
|
||||
if (DisplayListManager.isRecording()) record(new BlockEntityAttributeCmd(block, metadata));
|
||||
}
|
||||
|
||||
private record BlockEntityAttributeCmd(Block block, int metadata) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
if (block == null) FallingBlockRendering.resetEntityAttribute();
|
||||
else FallingBlockRendering.setEntityAttribute(block, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
private record EntityAndItemCmd(int entity, int item) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() { CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(entity, item); }
|
||||
}
|
||||
|
||||
private record NamedEntityCmd(NamespacedId entity) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() { CapturedRenderingState.INSTANCE.setCurrentNamedEntity(entity); }
|
||||
}
|
||||
|
||||
private record BlockEntityIdCmd(int id) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() { CapturedRenderingState.INSTANCE.setCurrentBlockEntity(id); }
|
||||
}
|
||||
|
||||
private record BlockEntityCmd(Block block, int metadata) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() { CapturedRenderingState.INSTANCE.setCurrentBlockEntity(block, metadata); }
|
||||
}
|
||||
|
||||
private record BlockEntityScopeCmd(boolean push) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
if (push) CapturedRenderingState.INSTANCE.pushCurrentBlockEntity();
|
||||
else CapturedRenderingState.INSTANCE.popCurrentBlockEntity();
|
||||
}
|
||||
}
|
||||
|
||||
private record EntityColorCmd(float r, float g, float b, float a) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() { CapturedRenderingState.INSTANCE.setCurrentEntityColor(r, g, b, a); }
|
||||
}
|
||||
|
||||
private record EntityColorScopeCmd(boolean push) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
if (push) CapturedRenderingState.INSTANCE.pushCurrentEntityColor();
|
||||
else CapturedRenderingState.INSTANCE.popCurrentEntityColor();
|
||||
}
|
||||
}
|
||||
|
||||
private record EntityCmd(EntityIdHelper.EntityIdentity identity) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(identity == null ? -1 : identity.resolve(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private record EntityScopeCmd(boolean push) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
if (push) CapturedRenderingState.INSTANCE.pushCurrentEntityAndItem();
|
||||
else CapturedRenderingState.INSTANCE.popCurrentEntityAndItem();
|
||||
}
|
||||
}
|
||||
|
||||
private record ItemScopeCmd(boolean push) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
if (push) {
|
||||
CapturedRenderingState.INSTANCE.pushCurrentRenderedItem();
|
||||
} else {
|
||||
CapturedRenderingState.INSTANCE.popCurrentRenderedItem();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private record SpecialConditionCmd(SpecialCondition condition, boolean begin) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
if (begin) {
|
||||
GbufferPrograms.setupSpecialRenderCondition(condition);
|
||||
} else {
|
||||
GbufferPrograms.teardownSpecialRenderCondition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private record GlintSpanCmd(boolean begin) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
if (begin) {
|
||||
ShaderGlint.beginGlint();
|
||||
} else {
|
||||
ShaderGlint.endGlint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private record OverridePhaseCmd(WorldRenderingPhase phase) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
GbufferPrograms.setOverridePhase(phase);
|
||||
}
|
||||
}
|
||||
|
||||
private record PhaseScopeCmd(WorldRenderingPhase phase, boolean begin) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
if (begin) GbufferPrograms.pushOverridePhase(phase);
|
||||
else GbufferPrograms.popOverridePhase();
|
||||
}
|
||||
}
|
||||
|
||||
private static final BooleanArrayList replayNestedEntityStack = new BooleanArrayList();
|
||||
|
||||
private record NestedEntityScopeCmd(boolean begin) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
if (begin) replayNestedEntityStack.push(GbufferPrograms.beginNestedEntityPhase());
|
||||
else GbufferPrograms.endNestedEntityPhase(replayNestedEntityStack.popBoolean());
|
||||
}
|
||||
}
|
||||
|
||||
private record TranslucencyCmd(Boolean translucent) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
GbufferPrograms.setTranslucencyDeclaration(translucent);
|
||||
}
|
||||
}
|
||||
|
||||
private static final ObjectArrayList<Boolean> replayTranslucencyStack = new ObjectArrayList<>();
|
||||
|
||||
private record TranslucencyScopeCmd(Boolean translucent, boolean begin) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
if (begin) replayTranslucencyStack.add(GbufferPrograms.beginTranslucencyDeclaration(translucent));
|
||||
else GbufferPrograms.endTranslucencyDeclaration(replayTranslucencyStack.pop());
|
||||
}
|
||||
}
|
||||
|
||||
private record ItemIdCmd(int itemId) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
CapturedRenderingState.INSTANCE.setCurrentRenderedItem(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
private record ItemCmd(ItemStack item) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
ItemIdManager.setItemId(item);
|
||||
}
|
||||
}
|
||||
|
||||
private record ItemTypeCmd(Item item, int metadata) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() { CapturedRenderingState.INSTANCE.setCurrentRenderedItem(item, metadata); }
|
||||
}
|
||||
|
||||
private record NamedItemCmd(NamespacedId item) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() { CapturedRenderingState.INSTANCE.setCurrentNamedItem(item); }
|
||||
}
|
||||
|
||||
private record BlockItemCmd(Block block, int metadata) implements DisplayListCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
ItemIdManager.setBlockId(block, metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.iris;
|
||||
|
||||
import com.gtnewhorizons.angelica.client.rendering.TextureTracker;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredAlphaHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler;
|
||||
@@ -20,6 +21,7 @@ import net.coderbot.iris.gl.blending.AlphaTestStorage;
|
||||
import net.coderbot.iris.gl.blending.BlendModeStorage;
|
||||
import net.coderbot.iris.gl.blending.DepthColorStorage;
|
||||
import net.coderbot.iris.gl.program.ProgramUniforms;
|
||||
import net.coderbot.iris.gl.program.Program;
|
||||
import net.coderbot.iris.gl.state.StateUpdateNotifiers;
|
||||
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
|
||||
@@ -87,6 +89,18 @@ public class IrisGLSMBridge {
|
||||
};
|
||||
}
|
||||
|
||||
static void installVanillaStateLayers() {
|
||||
// Forge initializes mods with the splash screen's shared drawable current.
|
||||
// Iris overrides belong to the display context used for world rendering.
|
||||
final GLContextState context = GLStateManager.primaryContext();
|
||||
context.blendMode.setVanillaLayer(BlendModeStorage.ENABLE_LAYER);
|
||||
context.blendState.setVanillaLayer(BlendModeStorage.FUNC_LAYER);
|
||||
context.alphaTest.setVanillaLayer(AlphaTestStorage.ENABLE_LAYER);
|
||||
context.alphaState.setVanillaLayer(AlphaTestStorage.FUNC_LAYER);
|
||||
context.depthState.setVanillaLayer(DepthColorStorage.DEPTH_LAYER);
|
||||
context.colorMask.setVanillaLayer(DepthColorStorage.COLOR_LAYER);
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
GLSMConfig.expandVertexFormats = Iris.enabled;
|
||||
IrisSamplers.initRenderer();
|
||||
@@ -124,12 +138,7 @@ public class IrisGLSMBridge {
|
||||
}
|
||||
};
|
||||
|
||||
GLStateManager.getBlendMode().setVanillaLayer(BlendModeStorage.ENABLE_LAYER);
|
||||
GLStateManager.getBlendState().setVanillaLayer(BlendModeStorage.FUNC_LAYER);
|
||||
GLStateManager.getAlphaTest().setVanillaLayer(AlphaTestStorage.ENABLE_LAYER);
|
||||
GLStateManager.getAlphaState().setVanillaLayer(AlphaTestStorage.FUNC_LAYER);
|
||||
GLStateManager.getDepthState().setVanillaLayer(DepthColorStorage.DEPTH_LAYER);
|
||||
GLStateManager.getColorMask().setVanillaLayer(DepthColorStorage.COLOR_LAYER);
|
||||
installVanillaStateLayers();
|
||||
|
||||
GLSMHooks.alphaHandler = new DeferredAlphaHandler() {
|
||||
@Override
|
||||
@@ -270,7 +279,7 @@ public class IrisGLSMBridge {
|
||||
if (!drp.shouldOverrideShaders()) return;
|
||||
DepthColorStorage.unlockDepthColor();
|
||||
|
||||
if (event.newProgram != 0 && !DepthColorStorage.isOwnedProgram(event.newProgram)) {
|
||||
if (event.newProgram != 0 && !Program.isManagedBind(event.newProgram) && !DepthColorStorage.isOwnedProgram(event.newProgram)) {
|
||||
drp.onModProgramOverride();
|
||||
}
|
||||
});
|
||||
@@ -294,12 +303,15 @@ public class IrisGLSMBridge {
|
||||
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
|
||||
if (!Iris.enabled) return;
|
||||
if (!event.postBind) return;
|
||||
if (Program.isManagedBind(event.newProgram)) return;
|
||||
WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
|
||||
if (pipeline instanceof DeferredWorldRenderingPipeline drp) {
|
||||
DeferredWorldRenderingPipeline.Pass activePass = drp.getActivePassProgram();
|
||||
if (activePass != null && activePass.getProgram() != null && activePass.getProgram().getProgramId() == event.newProgram) {
|
||||
final int frame = SystemTimeUniforms.COUNTER.getAsInt();
|
||||
if (programLastUpdatedFrame.get(event.newProgram) != frame) {
|
||||
if (programLastUpdatedFrame.get(event.newProgram) != frame
|
||||
|| !ProgramUniforms.isActiveProgramBound()
|
||||
|| ProgramUniforms.activeHasDeferredUploads()) {
|
||||
activePass.getProgram().getUniforms().update();
|
||||
programLastUpdatedFrame.put(event.newProgram, frame);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.gtnewhorizons.angelica.config.FontConfig;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.DependencyVerifier;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.Lwjgl3ifyExclusions;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.TracyOptions;
|
||||
import com.gtnewhorizons.angelica.loading.fml.compat.CompatHandlers;
|
||||
import com.gtnewhorizons.angelica.lwjgl3.MissingDependencySdl;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SDLGPUGate;
|
||||
@@ -66,6 +67,7 @@ public final class AngelicaClientTweaker implements IFMLLoadingPlugin, IEarlyMix
|
||||
try {
|
||||
// Angelica Config
|
||||
ConfigurationManager.registerConfig(AngelicaConfig.class);
|
||||
TracyOptions.latch(AngelicaConfig.enableTracy, AngelicaConfig.tracyAllowRemote, AngelicaConfig.tracyFineZones, AngelicaConfig.tracyMaxSrcLocs);
|
||||
ConfigurationManager.registerConfig(CompatConfig.class);
|
||||
ConfigurationManager.registerConfig(FontConfig.class);
|
||||
MCPatcherForgeConfig.registerConfig();
|
||||
|
||||
@@ -2,12 +2,12 @@ package com.gtnewhorizons.angelica.mixins;
|
||||
|
||||
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
|
||||
import com.gtnewhorizon.gtnhmixins.builders.MixinBuilder;
|
||||
import com.gtnewhorizons.angelica.AngelicaMod;
|
||||
import com.gtnewhorizons.angelica.api.BlockLightProvider;
|
||||
import com.gtnewhorizons.angelica.config.AngelicaConfig;
|
||||
import com.gtnewhorizons.angelica.config.CompatConfig;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.CaptureGate;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.TracyOptions;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SDLGPUGate;
|
||||
import jss.notfine.config.MCPatcherForgeConfig;
|
||||
import jss.notfine.config.NotFineConfig;
|
||||
@@ -138,6 +138,12 @@ public enum Mixins implements IMixins {
|
||||
.addClientMixins("angelica.gui.MixinGuiMainMenuSplash")
|
||||
),
|
||||
|
||||
ANGELICA_TEXT_HIGHLIGHT(
|
||||
new MixinBuilder("Draw text field selection without color logic op")
|
||||
.setPhase(Phase.EARLY)
|
||||
.addClientMixins("angelica.gui.MixinGuiTextField")
|
||||
),
|
||||
|
||||
ANGELICA_FONT_RENDERER(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.enableFontRenderer)
|
||||
@@ -209,6 +215,13 @@ public enum Mixins implements IMixins {
|
||||
)
|
||||
),
|
||||
|
||||
THAUMCRAFT_SCANNER_SCREEN(new MixinBuilder("Render the held Thaumometer screen after world transparency")
|
||||
.setPhase(Phase.LATE)
|
||||
.addRequiredMod(TargetedMod.THAUMCRAFT)
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris)
|
||||
.addClientMixins("client.thaumcraft.MixinItemThaumometerRenderer")
|
||||
),
|
||||
|
||||
THAUMCRAFT_TESR_JAR_CACHE(new MixinBuilder("Batch TC4 jar liquid via the retained TESR mesh cache")
|
||||
.setPhase(Phase.LATE)
|
||||
.setApplyIf(() -> AngelicaConfig.enableTESRJarCache)
|
||||
@@ -236,7 +249,7 @@ public enum Mixins implements IMixins {
|
||||
|
||||
ANGELICA_TRACY(new MixinBuilder("Tracy profiler zones from vanilla Profiler sections")
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaMod.tracyEnabled)
|
||||
.setApplyIf(() -> TracyOptions.enabled() && TracyOptions.backendPresent())
|
||||
.addCommonMixins(
|
||||
"angelica.tracy.MixinProfiler_Tracy"
|
||||
, "angelica.tracy.MixinNetHandlerPlayServer_Tracy"
|
||||
@@ -250,14 +263,14 @@ public enum Mixins implements IMixins {
|
||||
|
||||
ANGELICA_ENABLE_DEBUG(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaMod.lwjglDebug)
|
||||
.setApplyIf(() -> SystemProperties.LWJGL_DEBUG)
|
||||
.addClientMixins(
|
||||
"angelica.debug.MixinSplashProgress"
|
||||
)
|
||||
),
|
||||
ANGELICA_DEBUG_MARKERS(new MixinBuilder("RenderDoc/RGP debug groups + object labels")
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaMod.lwjglDebug || CaptureGate.enabledAtStartup())
|
||||
.setApplyIf(() -> SystemProperties.LWJGL_DEBUG || CaptureGate.enabledAtStartup())
|
||||
.addClientMixins(
|
||||
"angelica.debug.MixinProfiler"
|
||||
, "angelica.debug.MixinTextureManager"
|
||||
@@ -327,7 +340,7 @@ public enum Mixins implements IMixins {
|
||||
// Not compatible with the lwjgl debug callbacks, so disable if that's enabled
|
||||
ARCHAIC_SPLASH(new MixinBuilder()
|
||||
.setPhase(Phase.EARLY)
|
||||
.setApplyIf(() -> AngelicaConfig.showSplashMemoryBar && !AngelicaMod.lwjglDebug)
|
||||
.setApplyIf(() -> AngelicaConfig.showSplashMemoryBar && !SystemProperties.LWJGL_DEBUG)
|
||||
.addClientMixins(
|
||||
"angelica.archaic.MixinSplashProgress$3",
|
||||
"angelica.archaic.AccessorSplashProgress"
|
||||
@@ -410,6 +423,8 @@ public enum Mixins implements IMixins {
|
||||
, "celeritas.biome_blending.MixinBlockGrass"
|
||||
, "celeritas.biome_blending.MixinBlockLeaves"
|
||||
, "celeritas.biome_blending.MixinBlockLiquid"
|
||||
, "celeritas.biome_blending.MixinWorld"
|
||||
, "celeritas.biome_blending.MixinRenderBlocks"
|
||||
, "celeritas.threading.MixinForgeHooksClient"
|
||||
, "celeritas.terrain.MixinChunk"
|
||||
, "celeritas.terrain.MixinWorldClient_WorkerAccess"
|
||||
@@ -419,7 +434,9 @@ public enum Mixins implements IMixins {
|
||||
, "celeritas.terrain.MixinWorld_AwaitingDescriptor"
|
||||
, "celeritas.terrain.MixinRenderRegion"
|
||||
, "celeritas.terrain.MixinSectionRenderDataStorage"
|
||||
, "celeritas.terrain.MixinDefaultChunkShaderInterface"
|
||||
, "celeritas.terrain.MixinSharedQuadIndexBuffer"
|
||||
, "celeritas.terrain.MixinRenderRegionManager"
|
||||
, "celeritas.terrain.MixinShaderLoader"
|
||||
)
|
||||
),
|
||||
|
||||
@@ -804,6 +821,11 @@ public enum Mixins implements IMixins {
|
||||
.setApplyIf(() -> AngelicaConfig.enableIris)
|
||||
.addClientMixins("client.etfuturum.MixinTileEntityNewBeaconRenderer")
|
||||
),
|
||||
DRACONIC_PLACED_ITEM_RENDERER(new MixinBuilder("Keep custom placed-item renderers live")
|
||||
.setPhase(Phase.LATE)
|
||||
.addRequiredMod(TargetedMod.DRACONIC_EVOLUTION)
|
||||
.addClientMixins("client.draconicevolution.MixinRenderTilePlacedItem")
|
||||
),
|
||||
OPENBLOCKS_TROPHY_ENTITY_GBUFFER(new MixinBuilder("Render OpenBlocks trophies with the entity gbuffer programs")
|
||||
.setPhase(Phase.LATE)
|
||||
.addRequiredMod(TargetedMod.OPENBLOCKS)
|
||||
|
||||
@@ -16,6 +16,7 @@ public enum TargetedMod implements ITargetMod {
|
||||
DYNAMIC_SURROUNDINGS_MIST("org.blockartistry.mod.DynSurround.mixinplugin.DynamicSurroundingsEarlyMixins", "dsurround"),
|
||||
DYNAMIC_SURROUNDINGS_ORIGINAL("org.blockartistry.mod.DynSurround.asm.TransformLoader", "dsurround"),
|
||||
DRAGON_API("Reika.DragonAPI.Auxiliary.DragonAPIASMHandler", "DragonAPI"),
|
||||
DRACONIC_EVOLUTION(null, "DraconicEvolution"),
|
||||
ET_FUTURUM_REQUIEM(null, "etfuturum"),
|
||||
EXTRAUTILS(null, "ExtraUtilities"),
|
||||
FARSEEK("farseek.core.FarseekCoreMod", "farseek"),
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.gtnewhorizons.angelica.mixins.hooks;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.StateSet;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class TextHighlightHooks {
|
||||
|
||||
public static void draw(int x1, int y1, int x2, int y2) {
|
||||
final int depth = GLStateManager.pushState(StateSet.BLEND);
|
||||
|
||||
GLStateManager.glDisable(GL11.GL_TEXTURE_2D);
|
||||
GLStateManager.glEnable(GL11.GL_BLEND);
|
||||
|
||||
GLStateManager.glBlendFuncSeparate(GL11.GL_ONE_MINUS_DST_COLOR, GL11.GL_ONE_MINUS_SRC_COLOR, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
quad(x1, y1, x2, y2);
|
||||
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
|
||||
GLStateManager.glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
quad(x1, y1, x2, y2);
|
||||
|
||||
GLStateManager.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GLStateManager.popStateTo(depth);
|
||||
}
|
||||
|
||||
private static void quad(int x1, int y1, int x2, int y2) {
|
||||
final Tessellator tessellator = Tessellator.instance;
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.addVertex(x1, y2, 0.0D);
|
||||
tessellator.addVertex(x2, y2, 0.0D);
|
||||
tessellator.addVertex(x2, y1, 0.0D);
|
||||
tessellator.addVertex(x1, y1, 0.0D);
|
||||
tessellator.draw();
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -2,8 +2,8 @@ package com.gtnewhorizons.angelica.mixins.interfaces;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public interface IPatchedTextureAtlasSprite {
|
||||
void markNeedsAnimationUpdate();
|
||||
boolean needsAnimationUpdate();
|
||||
void unmarkNeedsAnimationUpdate();
|
||||
void updateAnimationsDryRun();
|
||||
void angelica$markNeedsAnimationUpdate();
|
||||
boolean angelica$needsAnimationUpdate();
|
||||
void angelica$unmarkNeedsAnimationUpdate();
|
||||
void angelica$updateAnimationsDryRun();
|
||||
}
|
||||
-3
@@ -1,14 +1,11 @@
|
||||
package com.gtnewhorizons.angelica.mixins.interfaces;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ReferenceMap;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.RenderSection;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkTaskOutput;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.executor.ChunkJobResult;
|
||||
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
|
||||
public interface RenderSectionManagerAccessor {
|
||||
Long2ReferenceMap<RenderSection> angelica$getSectionByPosition();
|
||||
ConcurrentLinkedDeque<Runnable> angelica$getAsyncSubmittedTasks();
|
||||
ConcurrentLinkedDeque<ChunkJobResult<? extends ChunkTaskOutput>> angelica$getBuildResults();
|
||||
}
|
||||
Loaded 100 of 259 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user