mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
159f7ff8cc | ||
|
|
0d959c2b90 | ||
|
|
ce2462cad3 | ||
|
|
5457df5369 | ||
|
|
acedba4eac | ||
|
|
21a054a4db | ||
|
|
d056bf6fa7 | ||
|
|
5ed1c92590 | ||
|
|
3389b952d0 | ||
|
|
b49d37d827 | ||
|
|
12857006c6 | ||
|
|
38af64f10c | ||
|
|
10922edca3 | ||
|
|
2a8c8756d3 | ||
|
|
23e36f2b76 | ||
|
|
fcf8ad064a | ||
|
|
6dfb0312b1 | ||
|
|
0a4d48964e | ||
|
|
231a2ab216 | ||
|
|
61bb779dea | ||
|
|
7cd97d1216 | ||
|
|
fe82cd320a | ||
|
|
c7a0f3a208 | ||
|
|
338d94197c | ||
|
|
75a0278138 |
No files matched your search
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# usage: companion-version.sh <name> <catalog-key>; TAG is <name>-v<version>[-N] or unset
|
||||
set -euo pipefail
|
||||
|
||||
name="${1:?name required}"
|
||||
key="${2:?catalog key required}"
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")/../.."
|
||||
base="$(sed -nE "s/^$key = \"(.*)\"\$/\\1/p" gradle/libs.versions.toml)"
|
||||
[[ -n "$base" ]] || { echo "$key not found" >&2; exit 1; }
|
||||
|
||||
tag="${TAG:-}"
|
||||
if [[ -z "$tag" ]]; then
|
||||
echo "$base"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
version="${tag#"$name"-v}"
|
||||
if [[ "$version" == "$tag" || ( "$version" != "$base" && ! "$version" =~ ^"$base"-[1-9][0-9]*$ ) ]]; then
|
||||
echo "bad tag '$tag': want $name-v$base[-N]" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$version"
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# usage: release-companion-jar.sh <jar> <notes>; TAG is <name>-v<version>
|
||||
set -euo pipefail
|
||||
|
||||
: "${TAG:?TAG required}"
|
||||
jar="${1:?jar required}"
|
||||
notes="${2:?notes required}"
|
||||
|
||||
gh release create "$TAG" "$jar" --latest=false --title "angelica-${TAG%%-v*} ${TAG#*-v}" --notes "$notes"
|
||||
@@ -0,0 +1,53 @@
|
||||
name: Build angelica-moltenvk
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "moltenvk-v*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
publish:
|
||||
description: Publish angelica-moltenvk.jar to the GTNH maven
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
- uses: gradle/actions/setup-gradle@v4
|
||||
- name: Compute version
|
||||
shell: bash
|
||||
env:
|
||||
TAG: ${{ github.ref_type == 'tag' && github.ref_name || '' }}
|
||||
run: |
|
||||
version="$(bash .github/scripts/companion-version.sh moltenvk lwjgl3Release)"
|
||||
echo "MOLTENVK_VERSION=$version" >> "$GITHUB_ENV"
|
||||
- name: Build angelica-moltenvk.jar
|
||||
run: ./gradlew -PmoltenvkVersion="$MOLTENVK_VERSION" :moltenvk:distJar
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: angelica-moltenvk
|
||||
path: moltenvk/build/libs/angelica-moltenvk-*.jar
|
||||
if-no-files-found: error
|
||||
- name: Publish to GTNH maven
|
||||
if: github.ref_type == 'tag' || inputs.publish
|
||||
env:
|
||||
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
||||
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
||||
run: ./gradlew -PpublishMoltenVK=true -PmoltenvkVersion="$MOLTENVK_VERSION" :moltenvk:publish
|
||||
- name: Create GitHub release
|
||||
if: github.ref_type == 'tag'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ github.ref_name }}
|
||||
run: bash .github/scripts/release-companion-jar.sh moltenvk/build/libs/angelica-moltenvk-*.jar "MoltenVK for the SDL GPU Vulkan driver on macOS."
|
||||
@@ -12,6 +12,9 @@ on:
|
||||
required: false
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
@@ -66,8 +69,15 @@ jobs:
|
||||
cp "$d"/* "$dest/"
|
||||
done
|
||||
find tracy-client/src/main/resources/natives -type f
|
||||
- name: Compute version
|
||||
shell: bash
|
||||
env:
|
||||
TAG: ${{ github.ref_type == 'tag' && github.ref_name || '' }}
|
||||
run: |
|
||||
version="$(bash .github/scripts/companion-version.sh tracy tracy)"
|
||||
echo "TRACY_JAR_VERSION=$version" >> "$GITHUB_ENV"
|
||||
- name: Build angelica-tracy.jar
|
||||
run: ./gradlew :tracy-client:distJar
|
||||
run: ./gradlew -PtracyJarVersion="$TRACY_JAR_VERSION" :tracy-client:distJar
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: angelica-tracy
|
||||
@@ -78,4 +88,10 @@ jobs:
|
||||
env:
|
||||
MAVEN_USER: ${{ secrets.MAVEN_USER }}
|
||||
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
|
||||
run: ./gradlew -PpublishTracy=true :tracy-client:publish
|
||||
run: ./gradlew -PpublishTracy=true -PtracyJarVersion="$TRACY_JAR_VERSION" :tracy-client:publish
|
||||
- name: Create GitHub release
|
||||
if: github.ref_type == 'tag'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ github.ref_name }}
|
||||
run: bash .github/scripts/release-companion-jar.sh tracy-client/build/libs/angelica-tracy-*.jar "Tracy profiler backend for Angelica."
|
||||
@@ -3,7 +3,7 @@ name: Release tagged build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: [ '*', '!tracy-v*' ]
|
||||
tags: [ '*', '!tracy-v*', '!moltenvk-v*' ]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -6,6 +6,8 @@ plugins {
|
||||
id("com.gtnewhorizons.gtnhconvention")
|
||||
}
|
||||
|
||||
apply(from = "gradle/mod-version.gradle.kts")
|
||||
|
||||
val lwjglDebug = false
|
||||
val gpuHud = false
|
||||
val renderdoc = false
|
||||
@@ -146,6 +148,17 @@ fun Test.configureAngelicaJava8() {
|
||||
}
|
||||
}
|
||||
|
||||
if (isMacOs && providers.gradleProperty("clientJvmVendor").isPresent) {
|
||||
for (javaVersion in listOf(17, 21, 25)) {
|
||||
tasks.named<JavaExec>("runClient$javaVersion") {
|
||||
javaLauncher = javaToolchains.launcherFor {
|
||||
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||
vendor = JvmVendorSpec.matching(providers.gradleProperty("clientJvmVendor").get())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform { excludeTags = setOf("gl-core") }
|
||||
configureAngelicaJava8()
|
||||
|
||||
@@ -93,6 +93,9 @@ DependencyHandlerScope.of(dependencies).apply {
|
||||
shadowImplementation(project(":lwjgl3-backend")) { isTransitive = false }
|
||||
shadowImplementation(project(":sdl-gpu")) { isTransitive = false }
|
||||
runtimeOnlyNonPublishable(project(":tracy-client")) { isTransitive = false }
|
||||
if (System.getProperty("os.name").lowercase().contains("mac")) {
|
||||
runtimeOnlyNonPublishable(project(":moltenvk")) { isTransitive = false }
|
||||
}
|
||||
devOnlyNonPublishable(libs.async.profiler)
|
||||
shadowImplementation(libs.eventbus)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ public final class SystemProperties {
|
||||
|
||||
// Backend selection
|
||||
public static final String KEY_USE_SDL_GPU = "angelica.sdlgpu.enable";
|
||||
public static final boolean USE_SDL_GPU = Boolean.getBoolean(KEY_USE_SDL_GPU);
|
||||
public static final Boolean SDL_GPU_OVERRIDE = parseBooleanOverride(KEY_USE_SDL_GPU);
|
||||
public static final String GL_PROFILE = System.getProperty("angelica.glProfile", "");
|
||||
public static final boolean DISABLE_NVIDIA_WORKAROUND = Boolean.getBoolean("angelica.disableNvidiaWorkaround");
|
||||
public static final boolean DISABLE_LTW_WORKAROUND = Boolean.getBoolean("angelica.disableLtwWorkaround");
|
||||
@@ -28,7 +28,8 @@ public final class SystemProperties {
|
||||
|
||||
// SDL-GPU
|
||||
public static final String KEY_SDL_GPU_DRIVER = "angelica.sdlgpu.driver";
|
||||
public static final String SDL_GPU_DRIVER = System.getProperty(KEY_SDL_GPU_DRIVER, "");
|
||||
public static final String SDL_GPU_DRIVER_OVERRIDE = parseStringOverride(KEY_SDL_GPU_DRIVER);
|
||||
public static final String MOLTENVK_DIR = System.getProperty("angelica.moltenvk.dir", "angelica" + File.separator + "natives" + File.separator + "moltenvk");
|
||||
public static final boolean SDL_GPU_DEBUG = Boolean.getBoolean("angelica.sdlgpu.debug");
|
||||
private static final SdlAssertionMode ENCODER_ASSERTIONS = parseEnum("angelica.sdlgpu.encoderAssertions", SdlAssertionMode.OFF, SdlAssertionMode.WARN, SdlAssertionMode.class);
|
||||
public static final boolean SDL_ENCODER_ASSERTIONS = ENCODER_ASSERTIONS != SdlAssertionMode.OFF;
|
||||
@@ -61,8 +62,10 @@ public final class SystemProperties {
|
||||
public static final long FLYBY_TIME_OF_DAY = Math.floorMod(Long.getLong("angelica.flyby.timeOfDay", 6000L), 24000L);
|
||||
public static final String FLYBY_COMMANDS = System.getProperty("angelica.flyby.commands", "");
|
||||
public static final String FLYBY_ORIGIN = System.getProperty("angelica.flyby.origin", "");
|
||||
public static final Integer FLYBY_DIMENSION = Integer.getInteger("angelica.flyby.dimension");
|
||||
public static final FlybyPacing FLYBY_PACING = parseEnum("angelica.flyby.pacing", FlybyPacing.UNCAPPED, FlybyPacing.UNCAPPED, FlybyPacing.class);
|
||||
public static final FlybyWeather FLYBY_WEATHER = parseEnum("angelica.flyby.weather", FlybyWeather.CLEAR, FlybyWeather.CLEAR, FlybyWeather.class);
|
||||
public static final FlybyCamera FLYBY_CAMERA = parseEnum("angelica.flyby.camera", FlybyCamera.FIRST_PERSON, FlybyCamera.FIRST_PERSON, FlybyCamera.class);
|
||||
public static final boolean FLYBY_JFR = Boolean.getBoolean("angelica.flyby.jfr");
|
||||
public static final float FLYBY_PITCH = parseFloat("angelica.flyby.pitch");
|
||||
public static final int FLYBY_SCREENSHOTS = Integer.getInteger("angelica.flyby.screenshots", 0);
|
||||
@@ -131,6 +134,12 @@ public final class SystemProperties {
|
||||
CONFIGURED
|
||||
}
|
||||
|
||||
public enum FlybyCamera {
|
||||
FIRST_PERSON,
|
||||
THIRD_PERSON_BACK,
|
||||
THIRD_PERSON_FRONT
|
||||
}
|
||||
|
||||
public enum FlybyWeather {
|
||||
CLEAR,
|
||||
RAIN,
|
||||
@@ -158,7 +167,12 @@ public final class SystemProperties {
|
||||
|
||||
private static Boolean parseBooleanOverride(String key) {
|
||||
final String raw = System.getProperty(key);
|
||||
return raw == null ? null : Boolean.parseBoolean(raw);
|
||||
return raw == null || raw.isEmpty() ? null : Boolean.parseBoolean(raw);
|
||||
}
|
||||
|
||||
private static String parseStringOverride(String key) {
|
||||
final String raw = System.getProperty(key);
|
||||
return raw == null || raw.isEmpty() ? null : raw;
|
||||
}
|
||||
|
||||
private static double parseDouble(String key) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.ShaderDiskCache;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.ShaderIndex;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.SpirvShaderTranslator;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
@@ -456,12 +457,7 @@ public class CompatShaderTransformer {
|
||||
continue;
|
||||
}
|
||||
|
||||
final List<GLSLParser.Typeless_declarationContext> declarators = new ArrayList<>();
|
||||
declarators.add(decl.typeless_declaration());
|
||||
if (decl.getParent() instanceof GLSLParser.Init_declarator_listContext list) {
|
||||
declarators.addAll(list.typeless_declaration());
|
||||
}
|
||||
for (GLSLParser.Typeless_declarationContext d : declarators) {
|
||||
for (GLSLParser.Typeless_declarationContext d : ShaderIndex.declarators((GLSLParser.Init_declarator_listContext) decl.getParent())) {
|
||||
if (d.IDENTIFIER() == null) continue;
|
||||
final int slots = baseSlots(baseType) * typeArraySlots * arraySlots(d.array_specifier());
|
||||
final String arraySuffix = typeArray + arraySuffix(d.array_specifier());
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.gtnewhorizons.angelica.glsm.stacks.MaterialStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.MatrixModeStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.PointStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.PolygonStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.RetainedState;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ScissorStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StackIdAllocator;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
|
||||
@@ -29,7 +30,7 @@ import com.gtnewhorizons.angelica.glsm.states.AlphaState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelStoreState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ImageUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.SamplerUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray;
|
||||
@@ -106,6 +107,7 @@ public class GLContextState {
|
||||
public int maxBoundTextureUnit = 0;
|
||||
public int maxBoundImageUnit = 0;
|
||||
public final StateSet[] attribSets = new StateSet[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
public RetainedState retainedOwner;
|
||||
public int attribDepth = 0;
|
||||
|
||||
public boolean savesState(int id) {
|
||||
@@ -229,9 +231,16 @@ public class GLContextState {
|
||||
public int boundCopyReadBuffer;
|
||||
public int boundCopyWriteBuffer;
|
||||
public final Int2IntMap boundOtherBuffers = new Int2IntOpenHashMap();
|
||||
public PixelUnpackState pixelUnpackState = PixelUnpackState.DEFAULT;
|
||||
public PixelStoreState pixelUnpackState = PixelStoreState.DEFAULT;
|
||||
public PixelStoreState pixelPackState = PixelStoreState.DEFAULT;
|
||||
public final int[] clientAttribSavedMask = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final PixelStoreState[] clientAttribSavedPixelUnpack = new PixelStoreState[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final PixelStoreState[] clientAttribSavedPixelPack = new PixelStoreState[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final int[] clientAttribSavedUnpackBuffer = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final int[] clientAttribSavedPackBuffer = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final int[] clientAttribSavedTextureUnit = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final int[] clientAttribSavedVertexFlags = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final boolean[] clientAttribSavedVertexArray = new boolean[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public int clientAttribStackPointer = 0;
|
||||
public int restoreChangedMask;
|
||||
public long restoreUnitChangedMask;
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawCapture;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.TexImage2DCmd;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.commands.TexSubImage2DCmd;
|
||||
import com.gtnewhorizons.angelica.glsm.shader.ProgramBinaryCache;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.AlphaStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BlendStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
@@ -49,6 +50,7 @@ import com.gtnewhorizons.angelica.glsm.stacks.MaterialStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.MatrixModeStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.PointStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.PolygonStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.RetainedState;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.ScissorStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StackIdAllocator;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
|
||||
@@ -59,7 +61,7 @@ import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ColorMask;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelStoreState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PolygonState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.SamplerUnitArray;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ImageUnitBinding;
|
||||
@@ -307,17 +309,25 @@ public class GLStateManager {
|
||||
return Thread.currentThread() == MainThread;
|
||||
}
|
||||
|
||||
public static void requireContext(String call) {
|
||||
if (Thread.currentThread() == MainThread || RENDER_BACKEND.hasContextOnThread()) return;
|
||||
throw new IllegalStateException(call + " on thread " + Thread.currentThread().getName() + ", which has no GL context");
|
||||
}
|
||||
|
||||
@Getter private static GLSMInitConfig initConfig;
|
||||
|
||||
public static ContextCapabilities capabilities;
|
||||
|
||||
// Software stack depths for FFP emulation
|
||||
public static final int MAX_ATTRIB_STACK_DEPTH = 32;
|
||||
public static final int RETAINED_SLOT = MAX_ATTRIB_STACK_DEPTH;
|
||||
public static final int STATE_SLOTS = MAX_ATTRIB_STACK_DEPTH + 1;
|
||||
public static final int MAX_MODELVIEW_STACK_DEPTH = 32 + 2;
|
||||
public static final int MAX_PROJECTION_STACK_DEPTH = 4;
|
||||
public static final int MAX_TEXTURE_STACK_DEPTH = 4;
|
||||
public static final int MAX_CLIP_PLANES = 8;
|
||||
public static final int MAX_TEXTURE_UNITS = RENDER_BACKEND.getInteger(GL20.GL_MAX_TEXTURE_IMAGE_UNITS);
|
||||
public static final int MAX_LEGACY_TEXTURE_UNITS = Math.min(VertexKey.MAX_UNITS, MAX_TEXTURE_UNITS);
|
||||
|
||||
public static final GLFeatureSet HAS_MULTIPLE_SET = new GLFeatureSet();
|
||||
|
||||
@@ -542,6 +552,7 @@ public class GLStateManager {
|
||||
final GLContextState glCtx = ctx();
|
||||
DisplayListManager.abortCompilation();
|
||||
glCtx.attribDepth = 0;
|
||||
glCtx.retainedOwner = null;
|
||||
for (int i = 0; i < glCtx.attribSets.length; i++) {
|
||||
glCtx.attribSets[i] = null;
|
||||
}
|
||||
@@ -1182,11 +1193,35 @@ public class GLStateManager {
|
||||
case GL11.GL_LINE_STIPPLE_PATTERN -> glCtx.lineState.getStipplePattern() & 0xFFFF;
|
||||
case GL11.GL_LINE_STIPPLE_REPEAT -> glCtx.lineState.getStippleFactor();
|
||||
case GL11.GL_LIST_INDEX -> DisplayListManager.isRecording() ? Math.max(DisplayListManager.getRecordingListId(), 0) : 0;
|
||||
case GL11.GL_ATTRIB_STACK_DEPTH -> glCtx.attribDepth;
|
||||
case GL11.GL_CLIENT_ATTRIB_STACK_DEPTH -> glCtx.clientAttribStackPointer;
|
||||
case GL11.GL_TEXTURE_STACK_DEPTH -> getMatrixStackDepth(glCtx.textures.getTextureUnitMatrix(glCtx.activeTextureUnit.getValue()));
|
||||
case GL11.GL_MAX_ATTRIB_STACK_DEPTH -> MAX_ATTRIB_STACK_DEPTH;
|
||||
case GL11.GL_MAX_CLIENT_ATTRIB_STACK_DEPTH -> CLIENT_ATTRIB_STACK_DEPTH;
|
||||
case GL11.GL_MAX_MODELVIEW_STACK_DEPTH -> MAX_MODELVIEW_STACK_DEPTH;
|
||||
case GL11.GL_MAX_PROJECTION_STACK_DEPTH -> MAX_PROJECTION_STACK_DEPTH;
|
||||
case GL11.GL_MAX_TEXTURE_STACK_DEPTH -> MAX_TEXTURE_STACK_DEPTH;
|
||||
case GL13.GL_MAX_TEXTURE_UNITS -> MAX_LEGACY_TEXTURE_UNITS;
|
||||
case GL11.GL_MAX_LIGHTS -> glCtx.lightStates.length;
|
||||
case GL11.GL_STENCIL_BITS -> drawFramebufferBits(glCtx, true);
|
||||
case GL11.GL_DEPTH_BITS -> drawFramebufferBits(glCtx, false);
|
||||
default -> RENDER_BACKEND.getInteger(pname);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
private static int drawFramebufferBits(GLContextState glCtx, boolean stencil) {
|
||||
final int sizePname = stencil ? GL30.GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE : GL30.GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE;
|
||||
if (glCtx.drawFramebuffer == 0) {
|
||||
return RENDER_BACKEND.getFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, stencil ? GL11.GL_STENCIL : GL11.GL_DEPTH, sizePname);
|
||||
}
|
||||
final int attachment = stencil ? GL30.GL_STENCIL_ATTACHMENT : GL30.GL_DEPTH_ATTACHMENT;
|
||||
if (RENDER_BACKEND.getFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, attachment, GL30.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE) == GL11.GL_NONE) {
|
||||
return 0;
|
||||
}
|
||||
return RENDER_BACKEND.getFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, attachment, sizePname);
|
||||
}
|
||||
|
||||
public static void glGetInteger(int pname, IntBuffer params) {
|
||||
if (!isCachingEnabled()) {
|
||||
RENDER_BACKEND.getInteger(pname, params);
|
||||
@@ -1235,6 +1270,48 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static void putLightingInts(FloatBuffer values, int pname, boolean material, IntBuffer params) {
|
||||
final int count = switch (pname) {
|
||||
case GL11.GL_AMBIENT, GL11.GL_DIFFUSE, GL11.GL_SPECULAR -> 4;
|
||||
case GL11.GL_EMISSION -> material ? 4 : 0;
|
||||
case GL11.GL_COLOR_INDEXES -> material ? 3 : 0;
|
||||
case GL11.GL_SHININESS -> material ? 1 : 0;
|
||||
case GL11.GL_POSITION -> material ? 0 : 4;
|
||||
case GL11.GL_SPOT_DIRECTION -> material ? 0 : 3;
|
||||
case GL11.GL_SPOT_EXPONENT, GL11.GL_SPOT_CUTOFF, GL11.GL_CONSTANT_ATTENUATION, GL11.GL_LINEAR_ATTENUATION,
|
||||
GL11.GL_QUADRATIC_ATTENUATION -> material ? 0 : 1;
|
||||
default -> 0;
|
||||
};
|
||||
final boolean color = count == 4 && pname != GL11.GL_POSITION;
|
||||
final int pos = params.position();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final float value = values.get(i);
|
||||
final int converted;
|
||||
if (color) {
|
||||
converted = (int) (2147483647.0 * value);
|
||||
} else if (material) {
|
||||
converted = value < 0 ? -Math.round(-value) : Math.round(value);
|
||||
} else {
|
||||
converted = (int) value;
|
||||
}
|
||||
params.put(pos + i, converted);
|
||||
}
|
||||
}
|
||||
|
||||
public static void glGetMaterial(int face, int pname, IntBuffer params) {
|
||||
final FloatBuffer scratch = ctx().queryScratch;
|
||||
scratch.clear();
|
||||
glGetMaterial(face, pname, scratch);
|
||||
putLightingInts(scratch, pname, true, params);
|
||||
}
|
||||
|
||||
public static void glGetLight(int light, int pname, IntBuffer params) {
|
||||
final FloatBuffer scratch = ctx().queryScratch;
|
||||
scratch.clear();
|
||||
glGetLight(light, pname, scratch);
|
||||
putLightingInts(scratch, pname, false, params);
|
||||
}
|
||||
|
||||
public static void glGetLight(int light, int pname, FloatBuffer params) {
|
||||
final LightStateStack state = ctx().lightDataStates[light - GL11.GL_LIGHT0];
|
||||
switch (pname) {
|
||||
@@ -1287,12 +1364,25 @@ public class GLStateManager {
|
||||
params.put(pos + 2, normal.z);
|
||||
}
|
||||
case GL11.GL_CURRENT_TEXTURE_COORDS -> {
|
||||
final Vector4f tc = glCtx.ffp.currentTexCoords[0];
|
||||
final int unit = glCtx.activeTextureUnit.getValue();
|
||||
final int pos = params.position();
|
||||
params.put(pos, tc.x);
|
||||
params.put(pos + 1, tc.y);
|
||||
params.put(pos + 2, tc.z);
|
||||
params.put(pos + 3, tc.w);
|
||||
if (unit == 1) {
|
||||
params.put(pos, glCtx.lastBrightnessX);
|
||||
params.put(pos + 1, glCtx.lastBrightnessY);
|
||||
params.put(pos + 2, 0.0f);
|
||||
params.put(pos + 3, 1.0f);
|
||||
} else if (unit >= 0 && unit < glCtx.ffp.currentTexCoords.length) {
|
||||
final Vector4f tc = glCtx.ffp.currentTexCoords[unit];
|
||||
params.put(pos, tc.x);
|
||||
params.put(pos + 1, tc.y);
|
||||
params.put(pos + 2, tc.z);
|
||||
params.put(pos + 3, tc.w);
|
||||
} else {
|
||||
params.put(pos, 0.0f);
|
||||
params.put(pos + 1, 0.0f);
|
||||
params.put(pos + 2, 0.0f);
|
||||
params.put(pos + 3, 1.0f);
|
||||
}
|
||||
}
|
||||
case GL11.GL_FOG_COLOR -> {
|
||||
final FloatBuffer fogBuf = glCtx.fogState.getFogColorBuffer();
|
||||
@@ -1604,58 +1694,25 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static void glBlendFunc(int srcFactor, int dstFactor) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final RecordMode mode = DisplayListManager.getRecordMode();
|
||||
if (mode != RecordMode.NONE) {
|
||||
DisplayListManager.recordBlendFunc(srcFactor, dstFactor, srcFactor, dstFactor);
|
||||
if (mode == RecordMode.COMPILE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final boolean snapshotted = snapshotVanillaBlendFunc();
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null && bh.isBlendLocked()) {
|
||||
mod(glCtx.blendState);
|
||||
bh.deferBlendFunc(srcFactor, dstFactor, srcFactor, dstFactor);
|
||||
postVanillaBlendChangeIfMoved(snapshotted);
|
||||
return;
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
tryBlendFuncSeparate(srcFactor, dstFactor, srcFactor, dstFactor);
|
||||
}
|
||||
|
||||
private static void issueBlendFunc(BlendStateStack blend) {
|
||||
if (GLSMConfig.hudCacheOverride) {
|
||||
mod(glCtx.blendState);
|
||||
glCtx.blendState.setAll(srcFactor, dstFactor, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
glCtx.blendState.clearFuncUnknownState();
|
||||
RENDER_BACKEND.blendFuncSeparate(srcFactor, dstFactor, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
if (GLSMHooks.BLEND_FUNC_CHANGE.hasListeners()) {
|
||||
GLSMHooks.blendFuncChangeEvent.srcRgb = srcFactor;
|
||||
GLSMHooks.blendFuncChangeEvent.dstRgb = dstFactor;
|
||||
GLSMHooks.blendFuncChangeEvent.srcAlpha = GL11.GL_ONE;
|
||||
GLSMHooks.blendFuncChangeEvent.dstAlpha = GL11.GL_ONE_MINUS_SRC_ALPHA;
|
||||
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
|
||||
}
|
||||
postVanillaBlendChangeIfMoved(snapshotted);
|
||||
return;
|
||||
RENDER_BACKEND.blendFuncSeparate(blend.getSrcRgb(), blend.getDstRgb(), GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
} else {
|
||||
RENDER_BACKEND.blendFuncSeparate(blend.getSrcRgb(), blend.getDstRgb(), blend.getSrcAlpha(), blend.getDstAlpha());
|
||||
}
|
||||
final boolean bypass = !caching;
|
||||
if (bypass
|
||||
|| glCtx.blendState.getSrcRgb() != srcFactor
|
||||
|| glCtx.blendState.getDstRgb() != dstFactor
|
||||
|| glCtx.blendState.getSrcAlpha() != srcFactor
|
||||
|| glCtx.blendState.getDstAlpha() != dstFactor
|
||||
|| glCtx.blendState.isFuncUnknown()) {
|
||||
mod(glCtx.blendState);
|
||||
glCtx.blendState.setAll(srcFactor, dstFactor, srcFactor, dstFactor);
|
||||
glCtx.blendState.clearFuncUnknownState();
|
||||
RENDER_BACKEND.blendFunc(srcFactor, dstFactor);
|
||||
if (GLSMHooks.BLEND_FUNC_CHANGE.hasListeners()) {
|
||||
GLSMHooks.blendFuncChangeEvent.srcRgb = srcFactor;
|
||||
GLSMHooks.blendFuncChangeEvent.dstRgb = dstFactor;
|
||||
GLSMHooks.blendFuncChangeEvent.srcAlpha = srcFactor;
|
||||
GLSMHooks.blendFuncChangeEvent.dstAlpha = dstFactor;
|
||||
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
|
||||
}
|
||||
}
|
||||
postVanillaBlendChangeIfMoved(snapshotted);
|
||||
}
|
||||
|
||||
public static void setHudCacheOverride(boolean enabled) {
|
||||
if (GLSMConfig.hudCacheOverride == enabled) return;
|
||||
GLSMConfig.hudCacheOverride = enabled;
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null && bh.isOverrideHeld()) return;
|
||||
final BlendStateStack blend = ctx().blendState;
|
||||
issueBlendFunc(blend);
|
||||
blend.clearFuncUnknownState();
|
||||
}
|
||||
|
||||
public static void glBlendEquation(int mode) {
|
||||
@@ -1707,22 +1764,19 @@ public class GLStateManager {
|
||||
postVanillaBlendChangeIfMoved(snapshotted);
|
||||
return;
|
||||
}
|
||||
if (GLSMConfig.hudCacheOverride && dstAlpha != GL11.GL_ONE_MINUS_SRC_ALPHA) {
|
||||
srcAlpha = GL11.GL_ONE;
|
||||
dstAlpha = GL11.GL_ONE_MINUS_SRC_ALPHA;
|
||||
}
|
||||
final boolean caching = isCachingEnabled();
|
||||
final boolean bypass = !caching;
|
||||
if (bypass || glCtx.blendState.getSrcRgb() != srcRgb || glCtx.blendState.getDstRgb() != dstRgb || glCtx.blendState.getSrcAlpha() != srcAlpha || glCtx.blendState.getDstAlpha() != dstAlpha || glCtx.blendState.isFuncUnknown()) {
|
||||
mod(glCtx.blendState);
|
||||
glCtx.blendState.setAll(srcRgb, dstRgb, srcAlpha, dstAlpha);
|
||||
glCtx.blendState.clearFuncUnknownState();
|
||||
RENDER_BACKEND.blendFuncSeparate(srcRgb, dstRgb, srcAlpha, dstAlpha);
|
||||
issueBlendFunc(glCtx.blendState);
|
||||
if (GLSMHooks.BLEND_FUNC_CHANGE.hasListeners()) {
|
||||
final boolean hudCache = GLSMConfig.hudCacheOverride;
|
||||
GLSMHooks.blendFuncChangeEvent.srcRgb = srcRgb;
|
||||
GLSMHooks.blendFuncChangeEvent.dstRgb = dstRgb;
|
||||
GLSMHooks.blendFuncChangeEvent.srcAlpha = srcAlpha;
|
||||
GLSMHooks.blendFuncChangeEvent.dstAlpha = dstAlpha;
|
||||
GLSMHooks.blendFuncChangeEvent.srcAlpha = hudCache ? GL11.GL_ONE : srcAlpha;
|
||||
GLSMHooks.blendFuncChangeEvent.dstAlpha = hudCache ? GL11.GL_ONE_MINUS_SRC_ALPHA : dstAlpha;
|
||||
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
|
||||
}
|
||||
}
|
||||
@@ -2557,11 +2611,13 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static int glGenTextures() {
|
||||
requireContext("glGenTextures");
|
||||
flushDeferredTextureDeletes();
|
||||
return RENDER_BACKEND.genTextures();
|
||||
}
|
||||
|
||||
public static void glGenTextures(IntBuffer textures) {
|
||||
requireContext("glGenTextures");
|
||||
flushDeferredTextureDeletes();
|
||||
RENDER_BACKEND.genTextures(textures);
|
||||
}
|
||||
@@ -3282,22 +3338,63 @@ public class GLStateManager {
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isPositionArrayEnabled(GLContextState glCtx) {
|
||||
final VAOManager.Attrib position = glCtx.vaos.attrib(Usage.POSITION.getAttributeLocation());
|
||||
return position != null && position.enabled;
|
||||
}
|
||||
|
||||
static final int CLIENT_ATTRIB_STACK_DEPTH = 16;
|
||||
public static void glPushClientAttrib(int mask) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (glCtx.clientAttribStackPointer < CLIENT_ATTRIB_STACK_DEPTH) {
|
||||
glCtx.clientAttribSavedTextureUnit[glCtx.clientAttribStackPointer] = glCtx.clientActiveTextureUnit;
|
||||
glCtx.clientAttribSavedVertexFlags[glCtx.clientAttribStackPointer] = glCtx.vaos.getVertexFlags();
|
||||
glCtx.clientAttribStackPointer++;
|
||||
final int sp = glCtx.clientAttribStackPointer;
|
||||
if (sp < CLIENT_ATTRIB_STACK_DEPTH) {
|
||||
glCtx.clientAttribSavedMask[sp] = mask;
|
||||
if ((mask & GL11.GL_CLIENT_PIXEL_STORE_BIT) != 0) {
|
||||
glCtx.clientAttribSavedPixelPack[sp] = glCtx.pixelPackState;
|
||||
glCtx.clientAttribSavedPixelUnpack[sp] = glCtx.pixelUnpackState;
|
||||
glCtx.clientAttribSavedPackBuffer[sp] = glCtx.boundPixelPackBuffer;
|
||||
glCtx.clientAttribSavedUnpackBuffer[sp] = glCtx.boundPixelUnpackBuffer;
|
||||
}
|
||||
if ((mask & GL11.GL_CLIENT_VERTEX_ARRAY_BIT) != 0) {
|
||||
glCtx.clientAttribSavedTextureUnit[sp] = glCtx.clientActiveTextureUnit;
|
||||
glCtx.clientAttribSavedVertexFlags[sp] = glCtx.vaos.getVertexFlags();
|
||||
glCtx.clientAttribSavedVertexArray[sp] = isPositionArrayEnabled(glCtx);
|
||||
}
|
||||
glCtx.clientAttribStackPointer = sp + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static void glPopClientAttrib() {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (glCtx.clientAttribStackPointer > 0) {
|
||||
glCtx.clientAttribStackPointer--;
|
||||
glCtx.clientActiveTextureUnit = glCtx.clientAttribSavedTextureUnit[glCtx.clientAttribStackPointer];
|
||||
glCtx.vaos.setVertexFlags(glCtx.clientAttribSavedVertexFlags[glCtx.clientAttribStackPointer]);
|
||||
final int sp = --glCtx.clientAttribStackPointer;
|
||||
final int mask = glCtx.clientAttribSavedMask[sp];
|
||||
if ((mask & GL11.GL_CLIENT_PIXEL_STORE_BIT) != 0) {
|
||||
final PixelStoreState pack = glCtx.clientAttribSavedPixelPack[sp];
|
||||
final PixelStoreState unpack = glCtx.clientAttribSavedPixelUnpack[sp];
|
||||
glCtx.clientAttribSavedPixelPack[sp] = null;
|
||||
glCtx.clientAttribSavedPixelUnpack[sp] = null;
|
||||
PixelStoreState.applyDiff(glCtx.pixelPackState, pack, true);
|
||||
glCtx.pixelPackState = pack;
|
||||
PixelStoreState.applyDiff(glCtx.pixelUnpackState, unpack, false);
|
||||
glCtx.pixelUnpackState = unpack;
|
||||
final int packBuffer = glCtx.clientAttribSavedPackBuffer[sp];
|
||||
if (glCtx.boundPixelPackBuffer != packBuffer) glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, packBuffer);
|
||||
final int unpackBuffer = glCtx.clientAttribSavedUnpackBuffer[sp];
|
||||
if (glCtx.boundPixelUnpackBuffer != unpackBuffer) glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, unpackBuffer);
|
||||
}
|
||||
if ((mask & GL11.GL_CLIENT_VERTEX_ARRAY_BIT) != 0) {
|
||||
glCtx.clientActiveTextureUnit = glCtx.clientAttribSavedTextureUnit[sp];
|
||||
glCtx.vaos.setVertexFlags(glCtx.clientAttribSavedVertexFlags[sp]);
|
||||
final boolean vertexArray = glCtx.clientAttribSavedVertexArray[sp];
|
||||
if (isPositionArrayEnabled(glCtx) != vertexArray) {
|
||||
if (vertexArray) {
|
||||
glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
} else {
|
||||
glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4177,6 +4274,89 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void retainModifiedState(int depth, RetainedState out) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (depth < 0 || depth != glCtx.attribDepth - 1) {
|
||||
throw new IllegalStateException("retainModifiedState: depth " + depth + " is not the open push (attrib depth " + glCtx.attribDepth + ")");
|
||||
}
|
||||
glCtx.retainedOwner = out;
|
||||
final int count = glCtx.modifiedCount[depth];
|
||||
final int[] ids = glCtx.modifiedIds[depth];
|
||||
final CowStateStack<?>[] stackById = glCtx.stackById;
|
||||
out.ensureCapacity(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
final int id = ids[i];
|
||||
stackById[id].captureSlot(RETAINED_SLOT);
|
||||
out.ids[i] = id;
|
||||
}
|
||||
out.count = count;
|
||||
out.program = glCtx.activeProgram;
|
||||
out.programGen = programLifetimeGeneration;
|
||||
}
|
||||
|
||||
public static void applyRetainedState(RetainedState in) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (glCtx.retainedOwner != in) return;
|
||||
final CowStateStack<?>[] stackById = glCtx.stackById;
|
||||
final byte[] kindById = glCtx.kindById;
|
||||
final int[] ids = in.ids;
|
||||
final int count = in.count;
|
||||
int first = 0;
|
||||
while (first < count && !CowDispatch.slotChanged(kindById[ids[first]], stackById[ids[first]], RETAINED_SLOT)) first++;
|
||||
if (first < count) applyRetainedChanges(glCtx, in, first);
|
||||
if (in.program != glCtx.activeProgram && (in.program == 0 || in.programGen == programLifetimeGeneration)) glUseProgram(in.program);
|
||||
}
|
||||
|
||||
private static void applyRetainedChanges(GLContextState glCtx, RetainedState in, int first) {
|
||||
beforeUncapturedStateChange();
|
||||
final boolean blendSnapshotted = snapshotVanillaBlendFunc();
|
||||
final int alphaFuncId = glCtx.alphaState.stackId();
|
||||
final int alphaTestId = glCtx.alphaTest.stackId();
|
||||
boolean alphaChanged = false;
|
||||
final boolean prevPoppingAttributes = glCtx.poppingAttributes;
|
||||
glCtx.poppingAttributes = true;
|
||||
try {
|
||||
final CowStateStack<?>[] stackById = glCtx.stackById;
|
||||
final int[] restoreBitById = glCtx.restoreBitById;
|
||||
final int[] restoreUnitById = glCtx.restoreUnitById;
|
||||
final byte[] kindById = glCtx.kindById;
|
||||
int changed = 0;
|
||||
long unitsChanged = 0L;
|
||||
for (int i = first; i < in.count; i++) {
|
||||
final int id = in.ids[i];
|
||||
final CowStateStack<?> cow = stackById[id];
|
||||
final byte kind = kindById[id];
|
||||
if (!CowDispatch.slotChanged(kind, cow, RETAINED_SLOT)) continue;
|
||||
cow.beforeModify();
|
||||
final int bit = restoreBitById[id];
|
||||
if (bit != 0) {
|
||||
changed |= bit;
|
||||
final int unit = restoreUnitById[id];
|
||||
if (unit >= 0) unitsChanged |= 1L << Math.min(unit, 63);
|
||||
}
|
||||
CowDispatch.restoreSlot(kind, cow, RETAINED_SLOT);
|
||||
if (id == alphaFuncId || id == alphaTestId) alphaChanged = true;
|
||||
}
|
||||
glCtx.restoreChangedMask = changed;
|
||||
glCtx.restoreUnitChangedMask = unitsChanged;
|
||||
issueRestoredBackendState(glCtx, -1);
|
||||
glCtx.lightingGeneration++;
|
||||
glCtx.fragmentGeneration++;
|
||||
glCtx.colorGeneration++;
|
||||
glCtx.ffp.normalGeneration++;
|
||||
glCtx.ffp.texCoordGeneration++;
|
||||
glCtx.dirtyColorAttrib = true;
|
||||
glCtx.dirtyNormalAttrib = true;
|
||||
glCtx.dirtyTexCoordAttrib = true;
|
||||
postVanillaBlendChangeIfMoved(blendSnapshotted);
|
||||
} finally {
|
||||
glCtx.poppingAttributes = prevPoppingAttributes;
|
||||
}
|
||||
if (alphaChanged && GLSMHooks.ALPHA_STATE_CHANGE.hasListeners()) {
|
||||
GLSMHooks.ALPHA_STATE_CHANGE.post(GLSMHooks.alphaStateChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isDepthColorOverridden() {
|
||||
final DeferredDepthColorHandler dch = GLSMHooks.depthColorHandler;
|
||||
return dch != null && dch.isOverrideHeld();
|
||||
@@ -4195,7 +4375,7 @@ public class GLStateManager {
|
||||
RENDER_BACKEND.depthMask(glCtx.depthState.isEnabled());
|
||||
RENDER_BACKEND.clearDepth(glCtx.depthState.getClearValue());
|
||||
|
||||
RENDER_BACKEND.blendFuncSeparate(glCtx.blendState.getSrcRgb(), glCtx.blendState.getDstRgb(), glCtx.blendState.getSrcAlpha(), glCtx.blendState.getDstAlpha());
|
||||
issueBlendFunc(glCtx.blendState);
|
||||
glCtx.blendState.clearFuncUnknownState();
|
||||
RENDER_BACKEND.blendEquationSeparate(glCtx.blendState.getEquationRgb(), glCtx.blendState.getEquationAlpha());
|
||||
RENDER_BACKEND.blendColor(glCtx.blendState.getBlendColorR(), glCtx.blendState.getBlendColorG(), glCtx.blendState.getBlendColorB(), glCtx.blendState.getBlendColorA());
|
||||
@@ -4256,12 +4436,7 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After popping GLSM stacks, apply restored state to the GL driver.
|
||||
*/
|
||||
private static void applyRestoredState(StateSet set, boolean blendSnapshotted) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final int restore = set.restore;
|
||||
private static void issueRestoredBackendState(GLContextState glCtx, int restore) {
|
||||
final int changed = glCtx.restoreChangedMask;
|
||||
if ((restore & StateSet.R_DEPTH) != 0 && (changed & StateSet.R_DEPTH) != 0) {
|
||||
attribValueRestores++;
|
||||
@@ -4280,7 +4455,7 @@ public class GLStateManager {
|
||||
attribValueRestores++;
|
||||
final DeferredBlendHandler restoreBlendHandler = GLSMHooks.blendHandler;
|
||||
if (restoreBlendHandler == null || !restoreBlendHandler.isOverrideHeld()) {
|
||||
RENDER_BACKEND.blendFuncSeparate(blend.getSrcRgb(), blend.getDstRgb(), blend.getSrcAlpha(), blend.getDstAlpha());
|
||||
issueBlendFunc(blend);
|
||||
attribBackendCalls++;
|
||||
blend.clearFuncUnknownState();
|
||||
}
|
||||
@@ -4377,6 +4552,16 @@ public class GLStateManager {
|
||||
attribBackendCalls++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After popping GLSM stacks, apply restored state to the GL driver.
|
||||
*/
|
||||
private static void applyRestoredState(StateSet set, boolean blendSnapshotted) {
|
||||
final GLContextState glCtx = ctx();
|
||||
final int restore = set.restore;
|
||||
final int changed = glCtx.restoreChangedMask;
|
||||
issueRestoredBackendState(glCtx, restore);
|
||||
|
||||
final int bump = set.bump;
|
||||
final int depth = glCtx.attribDepth;
|
||||
@@ -4466,6 +4651,10 @@ public class GLStateManager {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (ctx().attribDepth == 0) {
|
||||
warnOnce("popattrib-underflow", "glPopAttrib called with an empty attrib stack; ignoring");
|
||||
return;
|
||||
}
|
||||
GLDebug.popGroup();
|
||||
GLDebug.pushGroup("popState");
|
||||
try {
|
||||
@@ -5269,13 +5458,18 @@ public class GLStateManager {
|
||||
}
|
||||
public static void glClearTexImage(int texture, int level, int format, int type, ByteBuffer data) { RENDER_BACKEND.clearTexImage(texture, level, format, type); }
|
||||
|
||||
public static int glGenSamplers() { return RENDER_BACKEND.genSamplers(); }
|
||||
public static int glGenSamplers() {
|
||||
requireContext("glGenSamplers");
|
||||
return RENDER_BACKEND.genSamplers();
|
||||
}
|
||||
|
||||
public static void glGenSamplers(IntBuffer samplers) {
|
||||
requireContext("glGenSamplers");
|
||||
for (int i = samplers.position(); i < samplers.limit(); i++) samplers.put(i, RENDER_BACKEND.genSamplers());
|
||||
}
|
||||
|
||||
public static void glGenSamplers(int[] samplers) {
|
||||
requireContext("glGenSamplers");
|
||||
for (int i = 0; i < samplers.length; i++) samplers[i] = RENDER_BACKEND.genSamplers();
|
||||
}
|
||||
|
||||
@@ -6342,7 +6536,11 @@ public class GLStateManager {
|
||||
public static void glPixelStorei(int pname, int param) {
|
||||
final GLContextState glCtx = ctx();
|
||||
if (isCachingEnabled()) {
|
||||
glCtx.pixelUnpackState = glCtx.pixelUnpackState.with(pname, param);
|
||||
if (PixelStoreState.isPack(pname)) {
|
||||
glCtx.pixelPackState = glCtx.pixelPackState.with(pname, param);
|
||||
} else {
|
||||
glCtx.pixelUnpackState = glCtx.pixelUnpackState.with(pname, param);
|
||||
}
|
||||
}
|
||||
RENDER_BACKEND.pixelStorei(pname, param);
|
||||
}
|
||||
@@ -6643,12 +6841,12 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static void forcePixelUnpackState(PixelUnpackState target) {
|
||||
PixelUnpackState.applyDiff(ctx().pixelUnpackState, target);
|
||||
public static void forcePixelUnpackState(PixelStoreState target) {
|
||||
PixelStoreState.applyDiff(ctx().pixelUnpackState, target, false);
|
||||
}
|
||||
|
||||
public static void restorePixelUnpackState(PixelUnpackState applied) {
|
||||
PixelUnpackState.applyDiff(applied, ctx().pixelUnpackState);
|
||||
public static void restorePixelUnpackState(PixelStoreState applied) {
|
||||
PixelStoreState.applyDiff(applied, ctx().pixelUnpackState, false);
|
||||
}
|
||||
|
||||
private static boolean warnedUntrackedPixelBuffer;
|
||||
@@ -6692,26 +6890,32 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static int glGenBuffers() {
|
||||
requireContext("glGenBuffers");
|
||||
return RENDER_BACKEND.genBuffers();
|
||||
}
|
||||
|
||||
public static void glGenBuffers(IntBuffer buffers) {
|
||||
requireContext("glGenBuffers");
|
||||
for (int i = buffers.position(); i < buffers.limit(); i++) buffers.put(i, RENDER_BACKEND.genBuffers());
|
||||
}
|
||||
|
||||
public static void glGenBuffers(int[] buffers) {
|
||||
requireContext("glGenBuffers");
|
||||
for (int i = 0; i < buffers.length; i++) buffers[i] = RENDER_BACKEND.genBuffers();
|
||||
}
|
||||
|
||||
public static int glCreateBuffers() {
|
||||
requireContext("glCreateBuffers");
|
||||
return RENDER_BACKEND.createBuffers();
|
||||
}
|
||||
|
||||
public static void glCreateBuffers(IntBuffer buffers) {
|
||||
requireContext("glCreateBuffers");
|
||||
for (int i = buffers.position(); i < buffers.limit(); i++) buffers.put(i, RENDER_BACKEND.createBuffers());
|
||||
}
|
||||
|
||||
public static void glCreateBuffers(int[] buffers) {
|
||||
requireContext("glCreateBuffers");
|
||||
for (int i = 0; i < buffers.length; i++) buffers[i] = RENDER_BACKEND.createBuffers();
|
||||
}
|
||||
|
||||
@@ -6783,7 +6987,7 @@ public class GLStateManager {
|
||||
|
||||
private static void onBufferStorage(int target, int flags) {
|
||||
if ((flags & MAP_PERSISTENT_BIT) != 0) {
|
||||
markPersistentlyMapped(target);
|
||||
if (!RENDER_BACKEND.isBufferImmutable(bufferForTarget(ctx(), target))) markPersistentlyMapped(target);
|
||||
return;
|
||||
}
|
||||
onBufferRespec(target);
|
||||
@@ -6866,14 +7070,27 @@ public class GLStateManager {
|
||||
|
||||
public static void nglBufferSubData(int target, long offset, long size, long data) { glBufferSubData(target, offset, MemoryUtilities.memByteBuffer(data, checkedSize(size))); }
|
||||
|
||||
public static ByteBuffer glMapBuffer(int target, int access) { onBufferMap(target, access); return RENDER_BACKEND.mapBuffer(target, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access) {
|
||||
final ByteBuffer buf = RENDER_BACKEND.mapBuffer(target, access);
|
||||
if (buf != null) onBufferMap(target, access);
|
||||
return buf;
|
||||
}
|
||||
public static ByteBuffer glMapBuffer(int target, int access, ByteBuffer old_buffer) { return glMapBuffer(target, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access, long length, ByteBuffer old_buffer) { onBufferMap(target, access); return RENDER_BACKEND.mapBuffer(target, access, length, old_buffer); }
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access) { onBufferMapRange(target, access); return RENDER_BACKEND.mapBufferRange(target, offset, length, access); }
|
||||
public static ByteBuffer glMapBuffer(int target, int access, long length, ByteBuffer old_buffer) {
|
||||
final ByteBuffer buf = RENDER_BACKEND.mapBuffer(target, access, length, old_buffer);
|
||||
if (buf != null) onBufferMap(target, access);
|
||||
return buf;
|
||||
}
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access) {
|
||||
final ByteBuffer buf = RENDER_BACKEND.mapBufferRange(target, offset, length, access);
|
||||
if (buf != null) onBufferMapRange(target, access);
|
||||
return buf;
|
||||
}
|
||||
public static ByteBuffer glMapBufferRange(int target, long offset, long length, int access, ByteBuffer old_buffer) { return glMapBufferRange(target, offset, length, access); }
|
||||
public static long glMapBufferRangeAddress(int target, long offset, long length, int access) {
|
||||
onBufferMapRange(target, access);
|
||||
return RENDER_BACKEND.mapBufferRangeAddress(target, offset, length, access);
|
||||
final long address = RENDER_BACKEND.mapBufferRangeAddress(target, offset, length, access);
|
||||
if (address != 0L) onBufferMapRange(target, access);
|
||||
return address;
|
||||
}
|
||||
public static void glFlushMappedBufferRange(int target, long offset, long length) { RENDER_BACKEND.flushMappedBufferRange(target, offset, length); }
|
||||
public static boolean glUnmapBuffer(int target) { onBufferUnmap(target); return RENDER_BACKEND.unmapBuffer(target); }
|
||||
@@ -7102,6 +7319,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static int glGenVertexArrays() {
|
||||
requireContext("glGenVertexArrays");
|
||||
return RENDER_BACKEND.genVertexArrays();
|
||||
}
|
||||
|
||||
@@ -7155,6 +7373,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static int glGenFramebuffers() {
|
||||
requireContext("glGenFramebuffers");
|
||||
return RENDER_BACKEND.genFramebuffers();
|
||||
}
|
||||
|
||||
@@ -7172,15 +7391,24 @@ public class GLStateManager {
|
||||
|
||||
public static void glFramebufferTexture(int target, int attachment, int texture, int level) { RENDER_BACKEND.framebufferTexture(target, attachment, texture, level); }
|
||||
|
||||
public static int glGenRenderbuffers() { return RENDER_BACKEND.genRenderbuffers(); }
|
||||
public static int glGenRenderbuffers() {
|
||||
requireContext("glGenRenderbuffers");
|
||||
return RENDER_BACKEND.genRenderbuffers();
|
||||
}
|
||||
public static void glDeleteRenderbuffers(int renderbuffer) { RENDER_BACKEND.deleteRenderbuffers(renderbuffer); }
|
||||
public static void glBindRenderbuffer(int target, int renderbuffer) { RENDER_BACKEND.bindRenderbuffer(target, renderbuffer); }
|
||||
public static void glRenderbufferStorage(int target, int internalformat, int width, int height) { RENDER_BACKEND.renderbufferStorage(target, internalformat, width, height); }
|
||||
public static void glRenderbufferStorageMultisample(int target, int samples, int internalformat, int width, int height) { RENDER_BACKEND.renderbufferStorageMultisample(target, samples, internalformat, width, height); }
|
||||
public static void glFramebufferRenderbuffer(int target, int attachment, int renderbuffertarget, int renderbuffer) { RENDER_BACKEND.framebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); }
|
||||
|
||||
public static void glGenQueries(IntBuffer ids) { RENDER_BACKEND.genQueries(ids); }
|
||||
public static int glGenQueries() { return RENDER_BACKEND.genQueries(); }
|
||||
public static void glGenQueries(IntBuffer ids) {
|
||||
requireContext("glGenQueries");
|
||||
RENDER_BACKEND.genQueries(ids);
|
||||
}
|
||||
public static int glGenQueries() {
|
||||
requireContext("glGenQueries");
|
||||
return RENDER_BACKEND.genQueries();
|
||||
}
|
||||
public static void glDeleteQueries(int id) { RENDER_BACKEND.deleteQueries(id); }
|
||||
public static void glBeginQuery(int target, int id) { RENDER_BACKEND.beginQuery(target, id); }
|
||||
public static void glEndQuery(int target) { RENDER_BACKEND.endQuery(target); }
|
||||
@@ -7251,8 +7479,8 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
public static long nglMapBuffer(int target, int access) {
|
||||
onBufferMap(target, access);
|
||||
final ByteBuffer buf = RENDER_BACKEND.mapBuffer(target, access);
|
||||
if (buf != null) onBufferMap(target, access);
|
||||
return buf == null ? 0L : MemoryUtilities.memAddress0(buf);
|
||||
}
|
||||
public static void nglMultiDrawElementsBaseVertex(int mode, long count, int type, long indices, int primcount, long basevertex) {
|
||||
@@ -7779,6 +8007,9 @@ public class GLStateManager {
|
||||
if (ShaderManager.isEnabled()) {
|
||||
generateVertexShaderIfNeeded(program);
|
||||
}
|
||||
if (ProgramBinaryCache.isEnabled()) {
|
||||
ProgramBinaryCache.markRetrievable(program);
|
||||
}
|
||||
RENDER_BACKEND.linkProgram(program);
|
||||
if (ShaderManager.isEnabled() && RENDER_BACKEND.getProgrami(program, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
|
||||
LOGGER.warn("Program {} failed to link: {}", program, RENDER_BACKEND.getProgramInfoLog(program));
|
||||
@@ -7786,6 +8017,21 @@ public class GLStateManager {
|
||||
CompatUniformManager.onLinkProgram(program);
|
||||
}
|
||||
|
||||
public static void glProgramParameteri(int program, int pname, int value) {
|
||||
RENDER_BACKEND.programParameteri(program, pname, value);
|
||||
}
|
||||
|
||||
public static void glGetProgramBinary(int program, IntBuffer length, IntBuffer binaryFormat, ByteBuffer binary) {
|
||||
RENDER_BACKEND.getProgramBinary(program, length, binaryFormat, binary);
|
||||
}
|
||||
|
||||
public static void glProgramBinary(int program, int binaryFormat, ByteBuffer binary) {
|
||||
RENDER_BACKEND.programBinary(program, binaryFormat, binary);
|
||||
if (RENDER_BACKEND.getProgrami(program, GL20.GL_LINK_STATUS) == GL11.GL_TRUE) {
|
||||
CompatUniformManager.onLinkProgram(program);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a program has a fragment shader but no vertex shader, generate a passthrough vertex shader from the fragment's in declarations and attach it.
|
||||
*/
|
||||
@@ -7860,6 +8106,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static int glCreateShader(int type) {
|
||||
requireContext("glCreateShader");
|
||||
return RENDER_BACKEND.createShader(type);
|
||||
}
|
||||
|
||||
@@ -7868,6 +8115,7 @@ public class GLStateManager {
|
||||
}
|
||||
|
||||
public static int glCreateProgram() {
|
||||
requireContext("glCreateProgram");
|
||||
final int program = RENDER_BACKEND.createProgram();
|
||||
programsPendingDeletion.remove(program);
|
||||
programLifetimeGeneration++;
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.antlr.v4.runtime.BailErrorStrategy;
|
||||
import org.antlr.v4.runtime.BufferedTokenStream;
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonToken;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.DefaultErrorStrategy;
|
||||
import org.antlr.v4.runtime.Parser;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.WritableToken;
|
||||
import org.antlr.v4.runtime.atn.PredictionMode;
|
||||
import org.antlr.v4.runtime.misc.ParseCancellationException;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.taumc.glsl.grammar.GLSLLexer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
import org.taumc.glsl.grammar.GLSLPreParser;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -72,6 +76,10 @@ public class GlslTransformUtils {
|
||||
}
|
||||
|
||||
public static String renameReservedWords(String source, int targetVersion) {
|
||||
if (!containsWholeWord(source, "sample") && !containsWholeWord(source, "new")
|
||||
&& (targetVersion < 400 || !containsWholeWord(source, "sampler"))) {
|
||||
return source;
|
||||
}
|
||||
for (var entry : VERSIONED_RESERVED_WORDS.entrySet()) {
|
||||
// Rename identifiers that become reserved words at or above this GLSL version
|
||||
if (targetVersion >= entry.getKey()) {
|
||||
@@ -83,6 +91,23 @@ public class GlslTransformUtils {
|
||||
return source;
|
||||
}
|
||||
|
||||
static boolean containsWholeWord(String source, String word) {
|
||||
int from = 0;
|
||||
while (true) {
|
||||
final int match = source.indexOf(word, from);
|
||||
if (match < 0) return false;
|
||||
final int end = match + word.length();
|
||||
if ((match == 0 || !isAsciiWordChar(source.charAt(match - 1))) && (end == source.length() || !isAsciiWordChar(source.charAt(end)))) {
|
||||
return true;
|
||||
}
|
||||
from = match + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAsciiWordChar(char c) {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
|
||||
}
|
||||
|
||||
public static String restoreReservedWords(String source) {
|
||||
source = source.replace(RENAMED_PREFIX + "texture", "texture");
|
||||
source = source.replace(RENAMED_PREFIX + "sampler", "sampler");
|
||||
@@ -110,72 +135,35 @@ public class GlslTransformUtils {
|
||||
public record QuietParse(GLSLParser.Translation_unitContext full, GLSLPreParser.Translation_unitContext pre) {}
|
||||
|
||||
public static GLSLParser.Translation_unitContext parseFullQuiet(String source) {
|
||||
final GLSLLexer lexer = new GLSLLexer(CharStreams.fromString(source));
|
||||
lexer.removeErrorListeners();
|
||||
final GLSLParser parser = new GLSLParser(new CommonTokenStream(lexer));
|
||||
parser.removeErrorListeners();
|
||||
parser.setBuildParseTree(true);
|
||||
return parser.translation_unit();
|
||||
}
|
||||
|
||||
public static GLSLPreParser.Translation_unitContext parsePreQuiet(String source) {
|
||||
final GLSLLexer lexer = new GLSLLexer(CharStreams.fromString(source));
|
||||
lexer.removeErrorListeners();
|
||||
final GLSLPreParser preParser = new GLSLPreParser(new CommonTokenStream(lexer, GLSLLexer.DIRECTIVES));
|
||||
preParser.removeErrorListeners();
|
||||
preParser.setBuildParseTree(true);
|
||||
return preParser.translation_unit();
|
||||
return parseQuiet(new GLSLParser(new CommonTokenStream(quietLexer(source))), GLSLParser::translation_unit);
|
||||
}
|
||||
|
||||
public static QuietParse parseBothQuiet(String source) {
|
||||
final GLSLLexer lexer = new GLSLLexer(CharStreams.fromString(source));
|
||||
lexer.removeErrorListeners();
|
||||
final GLSLPreParser preParser = new GLSLPreParser(new BufferedTokenStream(lexer));
|
||||
preParser.removeErrorListeners();
|
||||
preParser.setBuildParseTree(true);
|
||||
final GLSLPreParser.Translation_unitContext pre = preParser.translation_unit();
|
||||
final GLSLLexer lexer = quietLexer(source);
|
||||
final GLSLPreParser.Translation_unitContext pre = parseQuiet(new GLSLPreParser(new BufferedTokenStream(lexer)), GLSLPreParser::translation_unit);
|
||||
lexer.reset();
|
||||
final GLSLParser parser = new GLSLParser(new CommonTokenStream(lexer));
|
||||
parser.removeErrorListeners();
|
||||
parser.setBuildParseTree(true);
|
||||
return new QuietParse(parser.translation_unit(), pre);
|
||||
return new QuietParse(parseQuiet(new GLSLParser(new CommonTokenStream(lexer)), GLSLParser::translation_unit), pre);
|
||||
}
|
||||
|
||||
public static Set<String> identifiersInDirectiveText(String source) {
|
||||
private static <P extends Parser, T> T parseQuiet(P parser, Function<P, T> rule) {
|
||||
parser.removeErrorListeners();
|
||||
parser.setErrorHandler(new BailErrorStrategy());
|
||||
parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
|
||||
try {
|
||||
return rule.apply(parser);
|
||||
} catch (ParseCancellationException e) {
|
||||
// Rewinds the token stream, which keeps every token SLL already pulled from the lexer.
|
||||
parser.reset();
|
||||
parser.setErrorHandler(new DefaultErrorStrategy());
|
||||
parser.getInterpreter().setPredictionMode(PredictionMode.LL);
|
||||
return rule.apply(parser);
|
||||
}
|
||||
}
|
||||
|
||||
public static GLSLLexer quietLexer(String source) {
|
||||
final GLSLLexer lexer = new GLSLLexer(CharStreams.fromString(source));
|
||||
lexer.removeErrorListeners();
|
||||
final BufferedTokenStream tokens = new BufferedTokenStream(lexer);
|
||||
tokens.fill();
|
||||
|
||||
final Set<String> names = new HashSet<>();
|
||||
for (Token token : tokens.getTokens()) {
|
||||
switch (token.getType()) {
|
||||
case GLSLLexer.PROGRAM_TEXT, GLSLLexer.MACRO_TEXT, GLSLLexer.CONSTANT_EXPRESSION -> collectIdentifiers(token.getText(), names);
|
||||
default -> { }
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
private static void collectIdentifiers(String text, Set<String> out) {
|
||||
if (text == null) return;
|
||||
final int len = text.length();
|
||||
int i = 0;
|
||||
while (i < len) {
|
||||
final char c = text.charAt(i);
|
||||
if (c != '_' && !Character.isLetter(c)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
int end = i + 1;
|
||||
while (end < len) {
|
||||
final char n = text.charAt(end);
|
||||
if (n != '_' && !Character.isLetterOrDigit(n)) break;
|
||||
end++;
|
||||
}
|
||||
out.add(text.substring(i, end));
|
||||
i = end;
|
||||
}
|
||||
return lexer;
|
||||
}
|
||||
|
||||
public static String getFormattedShader(ParseTree tree, String header) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelStoreState;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
@@ -25,10 +25,10 @@ public final class LTWWorkaround {
|
||||
oneTexel = BufferUtils.createByteBuffer(4);
|
||||
}
|
||||
oneTexel.clear();
|
||||
GLStateManager.forcePixelUnpackState(PixelUnpackState.DEFAULT);
|
||||
GLStateManager.forcePixelUnpackState(PixelStoreState.DEFAULT);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texSubImage2D(target, level, 0, 0, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, oneTexel);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
GLStateManager.restorePixelUnpackState(PixelUnpackState.DEFAULT);
|
||||
GLStateManager.restorePixelUnpackState(PixelStoreState.DEFAULT);
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,7 @@ public class RenderSystem {
|
||||
private static boolean supportsClearTexture;
|
||||
private static boolean supportsTesselation;
|
||||
private static boolean supportsSamplerObjects;
|
||||
private static boolean supportsProgramBinary;
|
||||
private static int maxImageUnits;
|
||||
@Getter private static int maxCombinedTextureImageUnits;
|
||||
private static int maxSSBOBindings;
|
||||
@@ -159,6 +160,9 @@ public class RenderSystem {
|
||||
maxCombinedTextureImageUnits = RENDER_BACKEND.getInteger(GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
|
||||
}
|
||||
|
||||
supportsProgramBinary = RENDER_BACKEND.supportsProgramBinary();
|
||||
GLStateManager.LOGGER.info("Program Binary: {}", supportsProgramBinary);
|
||||
|
||||
maxGlslVersion = Integer.parseInt(parseGlVersionString(RENDER_BACKEND.getString(GL20.GL_SHADING_LANGUAGE_VERSION)));
|
||||
supportsGpuShader4 = GLStateManager.capabilities.GL_EXT_gpu_shader4;
|
||||
|
||||
@@ -611,6 +615,10 @@ public class RenderSystem {
|
||||
return supportsSamplerObjects;
|
||||
}
|
||||
|
||||
public static boolean supportsProgramBinary() {
|
||||
return supportsProgramBinary;
|
||||
}
|
||||
|
||||
public static int genSampler() {
|
||||
if (!supportsSamplerObjects) return 0;
|
||||
return GLStateManager.glGenSamplers();
|
||||
|
||||
@@ -107,9 +107,11 @@ public final class StateSet {
|
||||
GLStateManager.getTextures().getTextureUnitBindings(0), GLStateManager.getTextures().getTextureUnitBindings(1),
|
||||
GLStateManager.getActiveTextureUnitStack(), GLStateManager.getProgramStack(),
|
||||
GLStateManager.getBlendMode(), GLStateManager.getAlphaTest(), GLStateManager.getDepthTest(),
|
||||
GLStateManager.getTextures().getTextureUnitStates(0)
|
||||
GLStateManager.getTextures().getTextureUnitStates(0),
|
||||
GLStateManager.getColorMask(), GLStateManager.getCullState(),
|
||||
GLStateManager.getPolygonState(), GLStateManager.getPolygonOffsetFillState()
|
||||
),
|
||||
R_BLEND | R_DEPTH | R_TEXTURE | R_ACTIVE_UNIT | R_PROGRAM,
|
||||
R_BLEND | R_DEPTH | R_COLOR_MASK | R_TEXTURE | R_ACTIVE_UNIT | R_PROGRAM | R_POLYGON,
|
||||
0,
|
||||
true);
|
||||
}
|
||||
@@ -121,9 +123,11 @@ public final class StateSet {
|
||||
GLStateManager.getTextures().getTextureUnitBindings(0), GLStateManager.getTextures().getTextureUnitBindings(1),
|
||||
GLStateManager.getActiveTextureUnitStack(),
|
||||
GLStateManager.getBlendMode(), GLStateManager.getAlphaTest(), GLStateManager.getDepthTest(),
|
||||
GLStateManager.getTextures().getTextureUnitStates(0)
|
||||
GLStateManager.getTextures().getTextureUnitStates(0),
|
||||
GLStateManager.getColorMask(), GLStateManager.getCullState(),
|
||||
GLStateManager.getPolygonState(), GLStateManager.getPolygonOffsetFillState()
|
||||
),
|
||||
R_BLEND | R_DEPTH | R_TEXTURE | R_ACTIVE_UNIT,
|
||||
R_BLEND | R_DEPTH | R_COLOR_MASK | R_TEXTURE | R_ACTIVE_UNIT | R_POLYGON,
|
||||
0,
|
||||
true);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
|
||||
public final class BackendOptions {
|
||||
|
||||
private static boolean configSdlGpu;
|
||||
private static String configDriver = "";
|
||||
private static int configGlVersion;
|
||||
|
||||
private BackendOptions() {}
|
||||
|
||||
public static void latch(boolean sdlGpu, String driver, int glVersion) {
|
||||
configSdlGpu = sdlGpu;
|
||||
configDriver = driver;
|
||||
configGlVersion = glVersion;
|
||||
}
|
||||
|
||||
public static boolean sdlGpuRequested() {
|
||||
return SystemProperties.SDL_GPU_OVERRIDE != null ? SystemProperties.SDL_GPU_OVERRIDE : configSdlGpu;
|
||||
}
|
||||
|
||||
public static String sdlGpuDriver() {
|
||||
return SystemProperties.SDL_GPU_DRIVER_OVERRIDE != null ? SystemProperties.SDL_GPU_DRIVER_OVERRIDE : configDriver;
|
||||
}
|
||||
|
||||
public static boolean configSdlGpu() {
|
||||
return configSdlGpu;
|
||||
}
|
||||
|
||||
public static String configDriver() {
|
||||
return configDriver;
|
||||
}
|
||||
|
||||
public static int configGlVersion() {
|
||||
return configGlVersion;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public final class BackendStartGuard {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("Angelica/BackendStartGuard");
|
||||
|
||||
private static File pending;
|
||||
private static boolean tripped;
|
||||
|
||||
private BackendStartGuard() {}
|
||||
|
||||
public static boolean begin(File marker, boolean sdlGpuConfigured, boolean flagForced) {
|
||||
pending = null;
|
||||
tripped = false;
|
||||
if (!sdlGpuConfigured || flagForced) {
|
||||
delete(marker);
|
||||
return false;
|
||||
}
|
||||
if (marker.isFile()) {
|
||||
delete(marker);
|
||||
tripped = true;
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
final File dir = marker.getParentFile();
|
||||
if (dir != null && !dir.isDirectory() && !dir.mkdirs()) throw new IOException("cannot create " + dir);
|
||||
if (marker.createNewFile()) pending = marker;
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Could not write {}: {}", marker, e.toString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void finish() {
|
||||
if (pending == null) return;
|
||||
delete(pending);
|
||||
pending = null;
|
||||
}
|
||||
|
||||
public static boolean tripped() {
|
||||
return tripped;
|
||||
}
|
||||
|
||||
private static void delete(File marker) {
|
||||
if (marker.isFile() && !marker.delete()) {
|
||||
LOGGER.warn("Could not delete {}", marker);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.AMDDebugOutput;
|
||||
import org.lwjgl.opengl.AMDDebugOutputCallback;
|
||||
@@ -23,6 +24,7 @@ import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
import org.lwjgl.opengl.GL40;
|
||||
import org.lwjgl.opengl.GL41;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
import org.lwjgl.opengl.GL42;
|
||||
@@ -807,6 +809,28 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
return GL20.glIsProgram(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsProgramBinary() {
|
||||
final ContextCapabilities caps = GLStateManager.capabilities;
|
||||
return !RenderSystem.isGLES() && (caps.OpenGL41 || caps.GL_ARB_get_program_binary)
|
||||
&& GL11.glGetInteger(GL41.GL_NUM_PROGRAM_BINARY_FORMATS) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void programParameteri(int program, int pname, int value) {
|
||||
GL41.glProgramParameteri(program, pname, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getProgramBinary(int program, IntBuffer length, IntBuffer binaryFormat, ByteBuffer binary) {
|
||||
GL41.glGetProgramBinary(program, length, binaryFormat, binary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void programBinary(int program, int binaryFormat, ByteBuffer binary) {
|
||||
GL41.glProgramBinary(program, binaryFormat, binary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateProgram(int program) {
|
||||
GL20.glValidateProgram(program);
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class MoltenVK {
|
||||
|
||||
public enum Source {
|
||||
ENV,
|
||||
JAR,
|
||||
SYSTEM,
|
||||
NONE
|
||||
}
|
||||
|
||||
public static final String ENV_VULKAN_LIBRARY = "SDL_VULKAN_LIBRARY";
|
||||
public static final String JAR_NAME = "angelica-moltenvk";
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("Angelica/MoltenVK");
|
||||
private static final String LIB_NAME = "libMoltenVK.dylib";
|
||||
private static final String[] SYSTEM_PATHS = { "/opt/homebrew/lib/" + LIB_NAME, "/usr/local/lib/" + LIB_NAME };
|
||||
private static final String ARCH = archName(System.getProperty("os.arch", ""));
|
||||
|
||||
private static Source source;
|
||||
private static String path;
|
||||
private static boolean located;
|
||||
|
||||
private MoltenVK() {}
|
||||
|
||||
public static synchronized Source source() {
|
||||
if (source == null) {
|
||||
final boolean macos = System.getProperty("os.name", "").toLowerCase().contains("mac");
|
||||
source = detect(macos, System.getenv(ENV_VULKAN_LIBRARY), MoltenVK.class.getResource(bundledResource(ARCH)) != null, SYSTEM_PATHS);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
public static synchronized String locate() {
|
||||
if (!located) {
|
||||
path = resolve(source(), System.getenv(ENV_VULKAN_LIBRARY), bundledResource(ARCH), new File(SystemProperties.MOLTENVK_DIR), ARCH, SYSTEM_PATHS);
|
||||
located = true;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
static String archName(String osArch) {
|
||||
final String arch = osArch.toLowerCase();
|
||||
return arch.contains("aarch64") || arch.contains("arm64") ? "arm64" : "x64";
|
||||
}
|
||||
|
||||
static String bundledResource(String arch) {
|
||||
return "/macos/" + arch + "/org/lwjgl/vulkan/" + LIB_NAME;
|
||||
}
|
||||
|
||||
static Source detect(boolean macos, String env, boolean bundled, String[] systemPaths) {
|
||||
if (!macos) return Source.NONE;
|
||||
if (env != null && !env.isEmpty()) return Source.ENV;
|
||||
if (bundled) return Source.JAR;
|
||||
return systemPath(systemPaths) != null ? Source.SYSTEM : Source.NONE;
|
||||
}
|
||||
|
||||
static String resolve(Source source, String env, String resource, File dir, String arch, String[] systemPaths) {
|
||||
return switch (source) {
|
||||
case ENV -> env;
|
||||
case JAR -> {
|
||||
final String extracted = extract(resource, dir, arch);
|
||||
yield extracted != null ? extracted : systemPath(systemPaths);
|
||||
}
|
||||
case SYSTEM -> systemPath(systemPaths);
|
||||
case NONE -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private static String systemPath(String[] systemPaths) {
|
||||
for (final String candidate : systemPaths) {
|
||||
if (new File(candidate).isFile()) return candidate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String extract(String resource, File dir, String arch) {
|
||||
final byte[] lib;
|
||||
try (InputStream in = MoltenVK.class.getResourceAsStream(resource)) {
|
||||
if (in == null) return null;
|
||||
lib = readAll(in);
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("MoltenVK: failed reading {}: {}", resource, e.toString());
|
||||
return null;
|
||||
}
|
||||
|
||||
final File target = new File(dir, "libMoltenVK-" + arch + ".dylib");
|
||||
File tmp = null;
|
||||
try {
|
||||
if (!target.isFile() || target.length() != lib.length || !Arrays.equals(Files.readAllBytes(target.toPath()), lib)) {
|
||||
if (!dir.isDirectory() && !dir.mkdirs()) throw new IOException("cannot create " + dir);
|
||||
tmp = File.createTempFile("libMoltenVK", ".dylib", dir);
|
||||
Files.write(tmp.toPath(), lib);
|
||||
Files.move(tmp.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("MoltenVK: failed extracting to {}: {}", target, e.toString());
|
||||
if (tmp != null) tmp.delete();
|
||||
return null;
|
||||
}
|
||||
return target.getAbsolutePath();
|
||||
}
|
||||
|
||||
private static byte[] readAll(InputStream in) throws IOException {
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream(1 << 20);
|
||||
final byte[] buf = new byte[16384];
|
||||
int n;
|
||||
while ((n = in.read(buf)) != -1) {
|
||||
out.write(buf, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,9 @@ public abstract class RenderBackend {
|
||||
/** Returns true if the current thread has a valid render context. */
|
||||
public abstract boolean hasContext();
|
||||
|
||||
/** Returns true if the calling thread may issue render commands. */
|
||||
public boolean hasContextOnThread() { return hasContext(); }
|
||||
|
||||
/** Returns true if the calling thread owns the "GL Context" */
|
||||
public abstract boolean isCurrent();
|
||||
|
||||
@@ -56,6 +59,7 @@ public abstract class RenderBackend {
|
||||
|
||||
public boolean framebufferCompletenessIsMeaningful() { return true; }
|
||||
public void onPersistentBufferWrite(int glId, long offset, long size) {}
|
||||
public boolean isBufferImmutable(int glId) { return false; }
|
||||
|
||||
public boolean isAnisotropicSupported() { return true; }
|
||||
|
||||
@@ -272,6 +276,12 @@ public abstract class RenderBackend {
|
||||
public void voxelizeRegion(long pass, boolean rebindVertexBuffer, float x, float y, float z, int rangeBase, int rangeCount, int vertexTotal) {}
|
||||
public void endVoxelizationBatch(long pass) {}
|
||||
|
||||
public void setVertexWriteReplay(int graphicsProgram, VertexWriteReplaySetup setup) {}
|
||||
|
||||
public boolean supportsComputeImageAtomics() { return true; }
|
||||
|
||||
public boolean supportsComputeImageAtomics(int width, int height, int depth) { return supportsComputeImageAtomics(); }
|
||||
|
||||
public abstract void enable(int cap);
|
||||
public abstract void enablei(int cap, int index);
|
||||
public abstract void disable(int cap);
|
||||
@@ -504,6 +514,10 @@ public abstract class RenderBackend {
|
||||
public abstract int getUniformLocation(int program, ByteBuffer name);
|
||||
public boolean isShader(int obj) { return false; }
|
||||
public boolean isProgram(int obj) { return false; }
|
||||
public boolean supportsProgramBinary() { return false; }
|
||||
public void programParameteri(int program, int pname, int value) {}
|
||||
public void getProgramBinary(int program, IntBuffer length, IntBuffer binaryFormat, ByteBuffer binary) {}
|
||||
public void programBinary(int program, int binaryFormat, ByteBuffer binary) {}
|
||||
public void validateProgram(int program) {}
|
||||
public void getAttachedShaders(int program, IntBuffer count, IntBuffer shaders) {
|
||||
if (count != null) count.put(0, 0);
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import java.util.function.IntSupplier;
|
||||
|
||||
/**
|
||||
* How to replay a graphics program's vertex-stage image writes.
|
||||
*
|
||||
* @param inputLocations per input, the declared location or -1 for whatever the graphics program linked
|
||||
* @param images per image the replay uses, in binding order: the written ones, then the read-only ones
|
||||
* @param writtenImageCount how many of {@code images} the replay writes
|
||||
* @param samplerNames samplers the graphics program may not have, because only the stripped writes read them
|
||||
* @param samplerUnits per sampler name, the texture unit it reads at each draw, or -1 to use samplerTextures
|
||||
* @param samplerTextures per sampler name, its texture when samplerUnits is -1
|
||||
* @param samplerObjects per sampler name, the GL sampler object to sample it with when samplerUnits is -1, or 0
|
||||
* @param vertexBufferBinding storage buffer binding of the first vertex buffer the replay program declares
|
||||
* @param vertexBufferCount how many vertex buffers it declares, at consecutive bindings
|
||||
* @param indexBufferBinding storage buffer binding of its index buffer
|
||||
*/
|
||||
public record VertexWriteReplaySetup(int replayProgram, String[] inputNames, int[] inputLocations,
|
||||
IntSupplier[] images, int writtenImageCount,
|
||||
String[] samplerNames, int[] samplerUnits, IntSupplier[] samplerTextures, int[] samplerObjects,
|
||||
int vertexBufferBinding, int vertexBufferCount, int indexBufferBinding) {
|
||||
}
|
||||
@@ -133,11 +133,13 @@ public class DSAARB extends DSAUnsupported {
|
||||
|
||||
@Override
|
||||
public int createFramebuffer() {
|
||||
GLStateManager.requireContext("glCreateFramebuffers");
|
||||
return RENDER_BACKEND.createFramebuffers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int createTexture(int target) {
|
||||
GLStateManager.requireContext("glCreateTextures");
|
||||
return RENDER_BACKEND.createTextures(target);
|
||||
}
|
||||
}
|
||||
@@ -165,7 +165,7 @@ public class DSAUnsupported implements DSAAccess {
|
||||
|
||||
@Override
|
||||
public int createTexture(int target) {
|
||||
final int texture = RENDER_BACKEND.genTextures();
|
||||
final int texture = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
+25
-13
@@ -54,9 +54,17 @@ public final class GeometryShaderGenerator {
|
||||
sb.append(" vec4 c0 = gl_in[0].gl_Position;\n");
|
||||
sb.append(" vec4 c1 = gl_in[1].gl_Position;\n\n");
|
||||
|
||||
sb.append(" float d0 = c0.z + c0.w;\n");
|
||||
sb.append(" float d1 = c1.z + c1.w;\n");
|
||||
sb.append(" if (d0 < 0.0 && d1 < 0.0) return;\n");
|
||||
sb.append(" float t0 = d0 < 0.0 ? d0 / (d0 - d1) : 0.0;\n");
|
||||
sb.append(" float t1 = d1 < 0.0 ? d0 / (d0 - d1) : 1.0;\n");
|
||||
sb.append(" vec4 p0 = mix(c0, c1, t0);\n");
|
||||
sb.append(" vec4 p1 = mix(c0, c1, t1);\n\n");
|
||||
|
||||
sb.append(" // NDC positions\n");
|
||||
sb.append(" vec2 n0 = c0.xy / c0.w;\n");
|
||||
sb.append(" vec2 n1 = c1.xy / c1.w;\n\n");
|
||||
sb.append(" vec2 n0 = p0.xy / p0.w;\n");
|
||||
sb.append(" vec2 n1 = p1.xy / p1.w;\n\n");
|
||||
|
||||
sb.append(" // Screen-space direction and perpendicular\n");
|
||||
sb.append(" vec2 dir = normalize((n1 - n0) * u_ViewportSize);\n");
|
||||
@@ -74,22 +82,22 @@ public final class GeometryShaderGenerator {
|
||||
}
|
||||
|
||||
private static void emitVertex(StringBuilder sb, VertexKey key, int endpointIdx, String offsetExpr) {
|
||||
final String ci = "c" + endpointIdx;
|
||||
final String pi = "p" + endpointIdx;
|
||||
final String ni = "n" + endpointIdx;
|
||||
final String idx = "[" + endpointIdx + "]";
|
||||
final String ti = "t" + endpointIdx;
|
||||
|
||||
sb.append(" // Endpoint ").append(endpointIdx).append(' ').append(offsetExpr).append('\n');
|
||||
|
||||
sb.append(" v_Color = v_Color_gs").append(idx).append(";\n");
|
||||
sb.append(" v_Color = ").append(lerp("v_Color_gs", ti)).append(";\n");
|
||||
if (key.separateSpecular()) {
|
||||
sb.append(" v_SpecularColor = v_SpecularColor_gs").append(idx).append(";\n");
|
||||
sb.append(" v_SpecularColor = ").append(lerp("v_SpecularColor_gs", ti)).append(";\n");
|
||||
}
|
||||
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) sb.append(" v_TexCoord0 = v_TexCoord0_gs").append(idx).append(";\n");
|
||||
if (key.lightmapEnabled()) sb.append(" v_TexCoord1 = v_TexCoord1_gs").append(idx).append(";\n");
|
||||
if (key.unitTexCoordEnabled(2)) sb.append(" v_TexCoord2 = v_TexCoord2_gs").append(idx).append(";\n");
|
||||
if (key.unitTexCoordEnabled(3)) sb.append(" v_TexCoord3 = v_TexCoord3_gs").append(idx).append(";\n");
|
||||
if (key.unitTexCoordEnabled(0) || key.texGenEnabled()) sb.append(" v_TexCoord0 = ").append(lerp("v_TexCoord0_gs", ti)).append(";\n");
|
||||
if (key.lightmapEnabled()) sb.append(" v_TexCoord1 = ").append(lerp("v_TexCoord1_gs", ti)).append(";\n");
|
||||
if (key.unitTexCoordEnabled(2)) sb.append(" v_TexCoord2 = ").append(lerp("v_TexCoord2_gs", ti)).append(";\n");
|
||||
if (key.unitTexCoordEnabled(3)) sb.append(" v_TexCoord3 = ").append(lerp("v_TexCoord3_gs", ti)).append(";\n");
|
||||
if (key.fogEnabled()) {
|
||||
sb.append(" v_FogCoord = v_FogCoord_gs").append(idx).append(";\n");
|
||||
sb.append(" v_FogCoord = ").append(lerp("v_FogCoord_gs", ti)).append(";\n");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append(" v_LineStart = v_LineStart_gs[0];\n");
|
||||
@@ -97,11 +105,15 @@ public final class GeometryShaderGenerator {
|
||||
|
||||
if (key.clipPlanesEnabled()) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
sb.append(" gl_ClipDistance[").append(i).append("] = gl_in").append(idx).append(".gl_ClipDistance[").append(i).append("];\n");
|
||||
sb.append(" gl_ClipDistance[").append(i).append("] = mix(gl_in[0].gl_ClipDistance[").append(i).append("], gl_in[1].gl_ClipDistance[").append(i).append("], ").append(ti).append(");\n");
|
||||
}
|
||||
}
|
||||
|
||||
sb.append(" gl_Position = vec4((").append(ni).append(' ').append(offsetExpr).append(") * ").append(ci).append(".w, ").append(ci).append(".z, ").append(ci).append(".w);\n");
|
||||
sb.append(" gl_Position = vec4((").append(ni).append(' ').append(offsetExpr).append(") * ").append(pi).append(".w, ").append(pi).append(".z, ").append(pi).append(".w);\n");
|
||||
sb.append(" EmitVertex();\n\n");
|
||||
}
|
||||
|
||||
private static String lerp(String input, String t) {
|
||||
return "mix(" + input + "[0], " + input + "[1], " + t + ")";
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,11 @@ public final class EcosystemNarrowRules {
|
||||
"com.tihyo.legends.management.asm.AbstractTransformer",
|
||||
"com.tihyo.legends.management.asm.ASMHelper",
|
||||
"com.tihyo.legends.management.asm.Transformer"
|
||||
)),
|
||||
|
||||
new NarrowRule("PowerConverters", "covers1624.powerconverters.", List.of(
|
||||
"covers1624.powerconverters.asm",
|
||||
"covers1624.powerconverters.randomdebugshit.CoFHDevEnvHacks"
|
||||
))
|
||||
);
|
||||
|
||||
|
||||
+6
-6
@@ -3,7 +3,7 @@ package com.gtnewhorizons.angelica.glsm.recording.commands;
|
||||
import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLTypes;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelStoreState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -28,7 +28,7 @@ public record TexImage2DCmd(
|
||||
int format,
|
||||
int type,
|
||||
@Nullable ByteBuffer pixels,
|
||||
@Nullable PixelUnpackState unpack
|
||||
@Nullable PixelStoreState unpack
|
||||
) implements DisplayListCommand {
|
||||
|
||||
public TexImage2DCmd {
|
||||
@@ -37,19 +37,19 @@ public record TexImage2DCmd(
|
||||
}
|
||||
}
|
||||
|
||||
public static TexImage2DCmd fromIntBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable IntBuffer pixels, PixelUnpackState unpack) {
|
||||
public static TexImage2DCmd fromIntBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable IntBuffer pixels, PixelStoreState unpack) {
|
||||
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
public static TexImage2DCmd fromFloatBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable FloatBuffer pixels, PixelUnpackState unpack) {
|
||||
public static TexImage2DCmd fromFloatBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable FloatBuffer pixels, PixelStoreState unpack) {
|
||||
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
public static TexImage2DCmd fromDoubleBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable DoubleBuffer pixels, PixelUnpackState unpack) {
|
||||
public static TexImage2DCmd fromDoubleBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable DoubleBuffer pixels, PixelStoreState unpack) {
|
||||
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
public static TexImage2DCmd fromByteBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable ByteBuffer pixels, PixelUnpackState unpack) {
|
||||
public static TexImage2DCmd fromByteBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable ByteBuffer pixels, PixelStoreState unpack) {
|
||||
return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -3,7 +3,7 @@ package com.gtnewhorizons.angelica.glsm.recording.commands;
|
||||
import com.gtnewhorizons.angelica.glsm.GLDebug;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GLTypes;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelStoreState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -28,7 +28,7 @@ public record TexSubImage2DCmd(
|
||||
int format,
|
||||
int type,
|
||||
@Nullable ByteBuffer pixels,
|
||||
@Nullable PixelUnpackState unpack
|
||||
@Nullable PixelStoreState unpack
|
||||
) implements DisplayListCommand {
|
||||
|
||||
public TexSubImage2DCmd {
|
||||
@@ -37,19 +37,19 @@ public record TexSubImage2DCmd(
|
||||
}
|
||||
}
|
||||
|
||||
public static TexSubImage2DCmd fromIntBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable IntBuffer pixels, PixelUnpackState unpack) {
|
||||
public static TexSubImage2DCmd fromIntBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable IntBuffer pixels, PixelStoreState unpack) {
|
||||
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
public static TexSubImage2DCmd fromFloatBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable FloatBuffer pixels, PixelUnpackState unpack) {
|
||||
public static TexSubImage2DCmd fromFloatBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable FloatBuffer pixels, PixelStoreState unpack) {
|
||||
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
public static TexSubImage2DCmd fromDoubleBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable DoubleBuffer pixels, PixelUnpackState unpack) {
|
||||
public static TexSubImage2DCmd fromDoubleBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable DoubleBuffer pixels, PixelStoreState unpack) {
|
||||
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
public static TexSubImage2DCmd fromByteBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable ByteBuffer pixels, PixelUnpackState unpack) {
|
||||
public static TexSubImage2DCmd fromByteBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable ByteBuffer pixels, PixelStoreState unpack) {
|
||||
return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack);
|
||||
}
|
||||
|
||||
|
||||
@@ -626,6 +626,7 @@ public class GLSMRedirector {
|
||||
.add("glGenRenderbuffers")
|
||||
.add("glRenderbufferStorage")
|
||||
.add("glFramebufferRenderbuffer")
|
||||
.add("glGenerateMipmap")
|
||||
);
|
||||
methodRedirects.put(EXTFramebufferObject, RedirectMap.newMap()
|
||||
.add("glBindFramebufferEXT", "glBindFramebuffer")
|
||||
@@ -638,6 +639,7 @@ public class GLSMRedirector {
|
||||
.add("glGenRenderbuffersEXT", "glGenRenderbuffers")
|
||||
.add("glRenderbufferStorageEXT", "glRenderbufferStorage")
|
||||
.add("glFramebufferRenderbufferEXT", "glFramebufferRenderbuffer")
|
||||
.add("glGenerateMipmapEXT", "glGenerateMipmap")
|
||||
);
|
||||
methodRedirects.put(ARBOcclusionQuery, RedirectMap.newMap()
|
||||
.add("glGenQueriesARB", "glGenQueries")
|
||||
|
||||
+90
-136
@@ -4,17 +4,11 @@ import com.gtnewhorizons.angelica.glsm.GlslTransformUtils;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.taumc.glsl.grammar.GLSLLexer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
import org.taumc.glsl.grammar.GLSLPreParser;
|
||||
import org.taumc.glsl.grammar.GLSLParserBaseListener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
@@ -99,7 +93,7 @@ public final class GlslVulkanPreprocess {
|
||||
}
|
||||
|
||||
final List<Edit> edits = new ArrayList<>();
|
||||
final Metadata meta = collectEdits(source, root, glShaderType, debugName, separateReadOnlyImages, edits);
|
||||
final Metadata meta = collectEdits(new ShaderIndex(root, source), glShaderType, debugName, separateReadOnlyImages, edits);
|
||||
|
||||
final Result out = new Result(applyEdits(source, edits), meta.boolUniforms(), meta.explicitVsInputs());
|
||||
synchronized (CACHE) {
|
||||
@@ -111,129 +105,100 @@ public final class GlslVulkanPreprocess {
|
||||
|
||||
public record Metadata(Set<String> boolUniforms, Set<String> explicitVsInputs, boolean needsSamplerless) {}
|
||||
|
||||
public static Metadata collectEdits(String source, GLSLParser.Translation_unitContext root, int glShaderType, String debugName, boolean separateReadOnlyImages, List<Edit> edits) {
|
||||
public static Metadata collectEdits(ShaderIndex index, int glShaderType, String debugName, boolean separateReadOnlyImages, List<Edit> edits) {
|
||||
final boolean isVertex = glShaderType == GL20.GL_VERTEX_SHADER;
|
||||
final Set<String> bools = new HashSet<>();
|
||||
final Set<String> explicitInputs = new HashSet<>();
|
||||
final Set<String> readOnlyImages = new HashSet<>();
|
||||
final boolean[] needsSamplerless = { false };
|
||||
boolean needsSamplerless = false;
|
||||
|
||||
final int[] maxExplicitLoc = { -1 };
|
||||
int maxExplicitLoc = -1;
|
||||
final List<Integer> unlocatedVsInputStarts = new ArrayList<>();
|
||||
|
||||
ParseTreeWalker.DEFAULT.walk(new GLSLParserBaseListener() {
|
||||
@Override
|
||||
public void enterInit_declarator_list(GLSLParser.Init_declarator_listContext ctx) {
|
||||
final GLSLParser.Single_declarationContext single = ctx.single_declaration();
|
||||
if (single == null || single.fully_specified_type() == null) return;
|
||||
for (GLSLParser.DeclarationContext declaration : index.globalDeclarations()) {
|
||||
final GLSLParser.Init_declarator_listContext list = declaration.init_declarator_list();
|
||||
if (list == null) continue;
|
||||
final GLSLParser.Single_declarationContext single = list.single_declaration();
|
||||
if (single == null || single.fully_specified_type() == null) continue;
|
||||
|
||||
final GLSLParser.Fully_specified_typeContext fst = single.fully_specified_type();
|
||||
final GLSLParser.Type_qualifierContext tq = fst.type_qualifier();
|
||||
if (tq == null) return;
|
||||
final GLSLParser.Fully_specified_typeContext fst = single.fully_specified_type();
|
||||
final GLSLParser.Type_qualifierContext tq = fst.type_qualifier();
|
||||
if (tq == null) continue;
|
||||
|
||||
boolean hasUniform = false, hasIn = false, hasLocation = false, hasReadonly = false;
|
||||
int locValue = -1;
|
||||
int bindingValue = -1;
|
||||
for (GLSLParser.Single_type_qualifierContext stq : tq.single_type_qualifier()) {
|
||||
if (stq.storage_qualifier() != null) {
|
||||
final String s = stq.storage_qualifier().getText();
|
||||
if ("uniform".equals(s)) hasUniform = true;
|
||||
else if ("in".equals(s)) hasIn = true;
|
||||
else if ("readonly".equals(s)) hasReadonly = true;
|
||||
} else if (stq.layout_qualifier() != null) {
|
||||
for (GLSLParser.Layout_qualifier_idContext id : stq.layout_qualifier().layout_qualifier_id_list().layout_qualifier_id()) {
|
||||
if (id.IDENTIFIER() == null) continue;
|
||||
final String qualifier = id.IDENTIFIER().getText();
|
||||
if ("location".equals(qualifier)) {
|
||||
hasLocation = true;
|
||||
if (id.constant_expression() != null) {
|
||||
try { locValue = Integer.parseInt(id.constant_expression().getText()); } catch (NumberFormatException ignored) {}
|
||||
}
|
||||
} else if ("binding".equals(qualifier) && id.constant_expression() != null) {
|
||||
try { bindingValue = Integer.parseInt(id.constant_expression().getText()); } catch (NumberFormatException ignored) {}
|
||||
boolean hasUniform = false, hasIn = false, hasLocation = false, hasReadonly = false;
|
||||
int locValue = -1;
|
||||
int bindingValue = -1;
|
||||
for (GLSLParser.Single_type_qualifierContext stq : tq.single_type_qualifier()) {
|
||||
if (stq.storage_qualifier() != null) {
|
||||
final String s = stq.storage_qualifier().getText();
|
||||
if ("uniform".equals(s)) hasUniform = true;
|
||||
else if ("in".equals(s)) hasIn = true;
|
||||
else if ("readonly".equals(s)) hasReadonly = true;
|
||||
} else if (stq.layout_qualifier() != null) {
|
||||
for (GLSLParser.Layout_qualifier_idContext id : stq.layout_qualifier().layout_qualifier_id_list().layout_qualifier_id()) {
|
||||
if (id.IDENTIFIER() == null) continue;
|
||||
final String qualifier = id.IDENTIFIER().getText();
|
||||
if ("location".equals(qualifier)) {
|
||||
hasLocation = true;
|
||||
if (id.constant_expression() != null) {
|
||||
try { locValue = Integer.parseInt(id.constant_expression().getText()); } catch (NumberFormatException ignored) {}
|
||||
}
|
||||
} else if ("binding".equals(qualifier) && id.constant_expression() != null) {
|
||||
try { bindingValue = Integer.parseInt(id.constant_expression().getText()); } catch (NumberFormatException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final boolean boolDecl = hasUniform && isBool(fst);
|
||||
final boolean explicitInputDecl = isVertex && hasIn && hasLocation;
|
||||
final boolean boolDecl = hasUniform && isBool(fst);
|
||||
final boolean explicitInputDecl = isVertex && hasIn && hasLocation;
|
||||
final List<GLSLParser.Typeless_declarationContext> declarators = ShaderIndex.declarators(list);
|
||||
for (GLSLParser.Typeless_declarationContext td : declarators) {
|
||||
handleDeclarator(td, hasUniform, boolDecl, explicitInputDecl, bools, explicitInputs, edits);
|
||||
}
|
||||
|
||||
if (single.typeless_declaration() != null) {
|
||||
handleDeclarator(single.typeless_declaration(), hasUniform, boolDecl, explicitInputDecl, bools, explicitInputs, edits);
|
||||
}
|
||||
for (GLSLParser.Typeless_declarationContext td : ctx.typeless_declaration()) {
|
||||
handleDeclarator(td, hasUniform, boolDecl, explicitInputDecl, bools, explicitInputs, edits);
|
||||
}
|
||||
|
||||
if (separateReadOnlyImages && hasUniform && fst.type_specifier() != null) {
|
||||
final String typeText = fst.type_specifier().getText();
|
||||
if (separateTextureType(typeText) != null) {
|
||||
needsSamplerless[0] = true;
|
||||
} else if (hasReadonly) {
|
||||
final String asTexture = separateTextureTypeForImage(typeText);
|
||||
if (asTexture != null) {
|
||||
needsSamplerless[0] = true;
|
||||
collectDeclaredNames(single, ctx, readOnlyImages);
|
||||
final String binding = bindingValue >= 0 ? "layout(binding = " + bindingValue + ") " : "";
|
||||
edits.add(new Edit(startIdx(fst), stopIdx(fst), binding + "uniform " + asTexture));
|
||||
if (separateReadOnlyImages && hasUniform && fst.type_specifier() != null) {
|
||||
final String typeText = fst.type_specifier().getText();
|
||||
if (separateTextureType(typeText) != null) {
|
||||
needsSamplerless = true;
|
||||
} else if (hasReadonly) {
|
||||
final String asTexture = separateTextureTypeForImage(typeText);
|
||||
if (asTexture != null) {
|
||||
needsSamplerless = true;
|
||||
for (GLSLParser.Typeless_declarationContext td : declarators) {
|
||||
if (td.IDENTIFIER() != null) readOnlyImages.add(td.IDENTIFIER().getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isVertex && hasIn) {
|
||||
if (hasLocation && locValue >= 0) {
|
||||
if (locValue > maxExplicitLoc[0]) maxExplicitLoc[0] = locValue;
|
||||
} else if (!hasLocation) {
|
||||
unlocatedVsInputStarts.add(ctx.getStart().getStartIndex());
|
||||
final String binding = bindingValue >= 0 ? "layout(binding = " + bindingValue + ") " : "";
|
||||
edits.add(new Edit(startIdx(fst), stopIdx(fst), binding + "uniform " + asTexture));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterPostfix_expression(GLSLParser.Postfix_expressionContext ctx) {
|
||||
if (readOnlyImages.isEmpty() || ctx.LEFT_PAREN() == null || ctx.RIGHT_PAREN() == null) return;
|
||||
final GLSLParser.Function_call_parametersContext params = ctx.function_call_parameters();
|
||||
if (params == null || params.assignment_expression() == null || params.assignment_expression().isEmpty()) return;
|
||||
if (!readOnlyImages.contains(params.assignment_expression(0).getText())) return;
|
||||
|
||||
final ParseTree callee = ctx.getChild(0);
|
||||
if (!(callee instanceof ParserRuleContext calleeCtx)) return;
|
||||
final String replacement = switch (calleeCtx.getText()) {
|
||||
case "imageLoad" -> "texelFetch";
|
||||
case "imageSize" -> "textureSize";
|
||||
default -> null;
|
||||
};
|
||||
if (replacement == null) return;
|
||||
|
||||
edits.add(new Edit(startIdx(calleeCtx), stopIdx(calleeCtx), replacement));
|
||||
final int rparen = ctx.RIGHT_PAREN().getSymbol().getStartIndex();
|
||||
edits.add(new Edit(rparen, rparen - 1, ", 0"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTerminal(TerminalNode node) {
|
||||
final Token tok = node.getSymbol();
|
||||
if (tok.getType() != GLSLLexer.IDENTIFIER) return;
|
||||
final String text = tok.getText();
|
||||
switch (text) {
|
||||
case "sampler" -> edits.add(new Edit(tok.getStartIndex(), tok.getStopIndex(), SAMPLER_RENAMED));
|
||||
case "gl_VertexID" -> edits.add(new Edit(tok.getStartIndex(), tok.getStopIndex(), "gl_VertexIndex"));
|
||||
case "gl_InstanceID" -> edits.add(new Edit(tok.getStartIndex(), tok.getStopIndex(), "gl_InstanceIndex"));
|
||||
default -> {
|
||||
if (TARGET_RESERVED.contains(text)) edits.add(new Edit(tok.getStartIndex(), tok.getStopIndex(), RESERVED_RENAMED_PREFIX + text));
|
||||
}
|
||||
if (isVertex && hasIn) {
|
||||
if (hasLocation && locValue >= 0) {
|
||||
if (locValue > maxExplicitLoc) maxExplicitLoc = locValue;
|
||||
} else if (!hasLocation) {
|
||||
unlocatedVsInputStarts.add(list.getStart().getStartIndex());
|
||||
}
|
||||
}
|
||||
}, root);
|
||||
|
||||
if (needsSamplerless[0] && source != null) {
|
||||
final int at = versionDirectiveEnd(source);
|
||||
if (at >= 0) edits.add(new Edit(at, at - 1, "\n" + SAMPLERLESS_EXTENSION));
|
||||
}
|
||||
|
||||
if (maxExplicitLoc[0] >= 0 && !unlocatedVsInputStarts.isEmpty()) {
|
||||
int next = maxExplicitLoc[0] + 1;
|
||||
if (!readOnlyImages.isEmpty()) {
|
||||
rewriteReadOnlyImageCalls(index.callsTo("imageLoad"), "texelFetch", readOnlyImages, edits);
|
||||
rewriteReadOnlyImageCalls(index.callsTo("imageSize"), "textureSize", readOnlyImages, edits);
|
||||
}
|
||||
if (needsSamplerless) {
|
||||
final int versionEnd = index.directives().versionEnd();
|
||||
if (versionEnd >= 0) edits.add(new Edit(versionEnd, versionEnd - 1, "\n" + SAMPLERLESS_EXTENSION));
|
||||
}
|
||||
|
||||
rename(index, "sampler", SAMPLER_RENAMED, edits);
|
||||
rename(index, "gl_VertexID", "gl_VertexIndex", edits);
|
||||
rename(index, "gl_InstanceID", "gl_InstanceIndex", edits);
|
||||
for (String reserved : TARGET_RESERVED) rename(index, reserved, RESERVED_RENAMED_PREFIX + reserved, edits);
|
||||
|
||||
if (maxExplicitLoc >= 0 && !unlocatedVsInputStarts.isEmpty()) {
|
||||
int next = maxExplicitLoc + 1;
|
||||
int dropped = 0;
|
||||
for (int start : unlocatedVsInputStarts) {
|
||||
if (next >= MAX_VS_INPUT_LOCATIONS) { dropped++; continue; }
|
||||
@@ -245,22 +210,31 @@ public final class GlslVulkanPreprocess {
|
||||
}
|
||||
}
|
||||
|
||||
return new Metadata(bools, explicitInputs, needsSamplerless[0]);
|
||||
return new Metadata(bools, explicitInputs, needsSamplerless);
|
||||
}
|
||||
|
||||
private static void rename(ShaderIndex index, String name, String replacement, List<Edit> edits) {
|
||||
for (Token tok : index.occurrences(name)) edits.add(new Edit(tok.getStartIndex(), tok.getStopIndex(), replacement));
|
||||
}
|
||||
|
||||
private static void rewriteReadOnlyImageCalls(List<GLSLParser.Postfix_expressionContext> calls, String replacement, Set<String> readOnlyImages, List<Edit> edits) {
|
||||
for (GLSLParser.Postfix_expressionContext call : calls) {
|
||||
if (call.RIGHT_PAREN() == null) continue;
|
||||
final GLSLParser.Function_call_parametersContext params = call.function_call_parameters();
|
||||
if (params == null || params.assignment_expression().isEmpty()) continue;
|
||||
if (!readOnlyImages.contains(params.assignment_expression(0).getText())) continue;
|
||||
|
||||
final ParserRuleContext callee = (ParserRuleContext) call.getChild(0);
|
||||
edits.add(new Edit(startIdx(callee), stopIdx(callee), replacement));
|
||||
final int rparen = call.RIGHT_PAREN().getSymbol().getStartIndex();
|
||||
edits.add(new Edit(rparen, rparen - 1, ", 0"));
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearCache() {
|
||||
synchronized (CACHE) { CACHE.clear(); }
|
||||
}
|
||||
|
||||
private static void collectDeclaredNames(GLSLParser.Single_declarationContext single, GLSLParser.Init_declarator_listContext ctx, Set<String> out) {
|
||||
if (single.typeless_declaration() != null && single.typeless_declaration().IDENTIFIER() != null) {
|
||||
out.add(single.typeless_declaration().IDENTIFIER().getText());
|
||||
}
|
||||
for (GLSLParser.Typeless_declarationContext td : ctx.typeless_declaration()) {
|
||||
if (td.IDENTIFIER() != null) out.add(td.IDENTIFIER().getText());
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable String separateTextureTypeForImage(String typeText) {
|
||||
final String dim = switch (typeText) {
|
||||
case "image1D", "iimage1D", "uimage1D" -> "1D";
|
||||
@@ -281,26 +255,6 @@ public final class GlslVulkanPreprocess {
|
||||
};
|
||||
}
|
||||
|
||||
private static int versionDirectiveEnd(String source) {
|
||||
final GLSLPreParser.Translation_unitContext pre;
|
||||
try {
|
||||
pre = GlslTransformUtils.parsePreQuiet(source);
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
final GLSLPreParser.Version_directiveContext ctx = firstVersionDirective(pre);
|
||||
return ctx == null || ctx.getStop() == null ? -1 : ctx.getStop().getStopIndex() + 1;
|
||||
}
|
||||
|
||||
private static GLSLPreParser.@Nullable Version_directiveContext firstVersionDirective(ParseTree node) {
|
||||
if (node instanceof GLSLPreParser.Version_directiveContext v) return v;
|
||||
for (int i = 0; i < node.getChildCount(); i++) {
|
||||
final GLSLPreParser.Version_directiveContext found = firstVersionDirective(node.getChild(i));
|
||||
if (found != null) return found;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void handleDeclarator(GLSLParser.Typeless_declarationContext td, boolean hasUniform, boolean asBool, boolean asInput, Set<String> bools, Set<String> inputs, List<Edit> edits) {
|
||||
if (td.IDENTIFIER() == null) return;
|
||||
final String name = td.IDENTIFIER().getText();
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
package com.gtnewhorizons.angelica.glsm.shader;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.threading.AngelicaWorkers;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL41;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Saves linked GL programs to disk and loads them back with glProgramBinary, skipping compile and link.
|
||||
*/
|
||||
public final class ProgramBinaryCache {
|
||||
private static final Logger LOGGER = LogManager.getLogger("ProgramBinaryCache");
|
||||
static final String LAYER = "gl-program";
|
||||
private static final int PACKS_KEPT = 3;
|
||||
|
||||
private static volatile boolean enabled;
|
||||
private static volatile String packLayer;
|
||||
private static String driverId;
|
||||
private static String fullCompileLayer;
|
||||
private static Set<String> keysInUse;
|
||||
private static List<CompletableFuture<Boolean>> fullCompileWrites;
|
||||
private static Object fullCompileGeneration;
|
||||
private static final AtomicBoolean rejectWarned = new AtomicBoolean();
|
||||
private static final Object WRITE_LOCK = new Object();
|
||||
private static final Map<String, Object> compileGenerations = new HashMap<>();
|
||||
private static int deleteCount;
|
||||
|
||||
private ProgramBinaryCache() {}
|
||||
|
||||
public static void setEnabled(boolean value) {
|
||||
enabled = value;
|
||||
}
|
||||
|
||||
public static boolean isEnabled() {
|
||||
return enabled && packLayer != null && RenderSystem.supportsProgramBinary() && ShaderDiskCache.isEnabled();
|
||||
}
|
||||
|
||||
public static void usePack(String packName) {
|
||||
if (packName == null) {
|
||||
packLayer = null;
|
||||
return;
|
||||
}
|
||||
final String layer = LAYER + '/' + folderName(packName);
|
||||
packLayer = layer;
|
||||
if (!enabled) return;
|
||||
ShaderDiskCache.touchLayer(layer);
|
||||
AngelicaWorkers.run(() -> ShaderDiskCache.keepNewestSubLayers(LAYER, PACKS_KEPT));
|
||||
}
|
||||
|
||||
public static Key key() {
|
||||
return new Key(ShaderDiskCache.key(packLayer).str(driverId()).b(ShaderManager.isEnabled()));
|
||||
}
|
||||
|
||||
public static void markRetrievable(int program) {
|
||||
GLStateManager.glProgramParameteri(program, GL41.GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL11.GL_TRUE);
|
||||
}
|
||||
|
||||
public static int load(Key key) {
|
||||
final ShaderDiskCache.Key diskKey = key.diskKey;
|
||||
final ShaderDiskCache.Blob blob = ShaderDiskCache.getBlob(diskKey);
|
||||
if (blob == null) return 0;
|
||||
final int format;
|
||||
try {
|
||||
format = Integer.parseInt(blob.tag());
|
||||
} catch (NumberFormatException e) {
|
||||
ShaderDiskCache.remove(diskKey);
|
||||
return 0;
|
||||
}
|
||||
final int program = GLStateManager.glCreateProgram();
|
||||
final ByteBuffer data = MemoryUtilities.memAlloc(blob.data().length);
|
||||
try {
|
||||
data.put(blob.data()).flip();
|
||||
GLStateManager.glProgramBinary(program, format, data);
|
||||
} finally {
|
||||
MemoryUtilities.memFree(data);
|
||||
}
|
||||
if (GLStateManager.glGetProgrami(program, GL20.GL_LINK_STATUS) != GL11.GL_TRUE) {
|
||||
GLStateManager.glDeleteProgram(program);
|
||||
ShaderDiskCache.remove(diskKey);
|
||||
if (rejectWarned.compareAndSet(false, true)) LOGGER.error("Driver rejected a saved shader program!! Recompiling rejected programs from source");
|
||||
return 0;
|
||||
}
|
||||
keep(diskKey);
|
||||
return program;
|
||||
}
|
||||
|
||||
public static void save(Key key, int program) {
|
||||
if (!saveBinary(key, program) && fullCompileWrites != null) {
|
||||
fullCompileWrites.add(CompletableFuture.completedFuture(false));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean saveBinary(Key key, int program) {
|
||||
final int length = GLStateManager.glGetProgrami(program, GL41.GL_PROGRAM_BINARY_LENGTH);
|
||||
if (length <= 0) return false;
|
||||
final ByteBuffer data = MemoryUtilities.memAlloc(length);
|
||||
final IntBuffer written = MemoryUtilities.memAllocInt(1);
|
||||
final IntBuffer format = MemoryUtilities.memAllocInt(1);
|
||||
try {
|
||||
GLStateManager.glGetProgramBinary(program, written, format, data);
|
||||
if (written.get(0) <= 0) return false;
|
||||
data.limit(written.get(0));
|
||||
final byte[] binary = ShaderCacheIO.toHeap(data);
|
||||
final String formatTag = Integer.toString(format.get(0));
|
||||
final ShaderDiskCache.Key diskKey = key.diskKey;
|
||||
keep(diskKey);
|
||||
final int deletesBefore;
|
||||
synchronized (WRITE_LOCK) {
|
||||
deletesBefore = deleteCount;
|
||||
}
|
||||
final CompletableFuture<Boolean> write = AngelicaWorkers.submit(() -> {
|
||||
synchronized (WRITE_LOCK) {
|
||||
return deletesBefore == deleteCount && ShaderDiskCache.putBlob(diskKey, formatTag, binary);
|
||||
}
|
||||
});
|
||||
final List<CompletableFuture<Boolean>> writes = fullCompileWrites;
|
||||
if (writes != null) writes.add(write);
|
||||
return true;
|
||||
} finally {
|
||||
MemoryUtilities.memFree(data);
|
||||
MemoryUtilities.memFree(written);
|
||||
MemoryUtilities.memFree(format);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isFullCompileRunning() {
|
||||
return keysInUse != null;
|
||||
}
|
||||
|
||||
public static boolean saveRetained(Key key, int program) {
|
||||
if (ShaderDiskCache.getBlob(key.diskKey) != null) {
|
||||
keep(key.diskKey);
|
||||
return true;
|
||||
}
|
||||
return saveBinary(key, program);
|
||||
}
|
||||
|
||||
public static void beginFullCompile() {
|
||||
fullCompileLayer = packLayer;
|
||||
keysInUse = new HashSet<>();
|
||||
fullCompileWrites = new ArrayList<>();
|
||||
synchronized (WRITE_LOCK) {
|
||||
fullCompileGeneration = new Object();
|
||||
if (fullCompileLayer != null) compileGenerations.put(fullCompileLayer, fullCompileGeneration);
|
||||
}
|
||||
}
|
||||
public static void finishFullCompile(@Nullable String completedSettings) {
|
||||
final String layer = fullCompileLayer;
|
||||
final Set<String> used = keysInUse;
|
||||
final List<CompletableFuture<Boolean>> writes = fullCompileWrites;
|
||||
final Object generation = fullCompileGeneration;
|
||||
fullCompileLayer = null;
|
||||
keysInUse = null;
|
||||
fullCompileWrites = null;
|
||||
fullCompileGeneration = null;
|
||||
if (layer == null || used == null || writes == null) return;
|
||||
|
||||
final ShaderDiskCache.Key marker = completedSettings != null ? compiledSettingsKey(layer, completedSettings) : null;
|
||||
final String markerHex = marker != null ? marker.hex() : null;
|
||||
CompletableFuture.allOf(writes.toArray(new CompletableFuture<?>[0])).whenComplete((ignored, failure) -> {
|
||||
synchronized (WRITE_LOCK) {
|
||||
if (compileGenerations.get(layer) != generation) return;
|
||||
if (marker == null || failure != null) return;
|
||||
for (CompletableFuture<Boolean> write : writes) {
|
||||
if (!write.join()) return;
|
||||
}
|
||||
ShaderDiskCache.retainLayer(layer, used);
|
||||
ShaderDiskCache.retainLayer(layer + "/settings", Collections.singleton(markerHex));
|
||||
ShaderDiskCache.putString(marker, "");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void deleteAll() {
|
||||
synchronized (WRITE_LOCK) {
|
||||
deleteCount++;
|
||||
compileGenerations.clear();
|
||||
ShaderDiskCache.deleteLayer(LAYER);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean wasCompiledFor(String settings) {
|
||||
final String layer = packLayer;
|
||||
return layer != null && ShaderDiskCache.getString(compiledSettingsKey(layer, settings)) != null;
|
||||
}
|
||||
|
||||
private static ShaderDiskCache.Key compiledSettingsKey(String layer, String settings) {
|
||||
return ShaderDiskCache.key(layer + "/settings").str(driverId()).b(ShaderManager.isEnabled()).str(settings);
|
||||
}
|
||||
|
||||
private static String folderName(String packName) {
|
||||
final StringBuilder name = new StringBuilder(Math.min(packName.length(), 64));
|
||||
for (int i = 0; i < packName.length() && name.length() < 64; i++) {
|
||||
final char c = packName.charAt(i);
|
||||
name.append(c < 128 && (Character.isLetterOrDigit(c) || c == '.' || c == '-' || c == '_') ? c : '_');
|
||||
}
|
||||
return name.toString();
|
||||
}
|
||||
|
||||
private static void keep(ShaderDiskCache.Key diskKey) {
|
||||
final String hex = diskKey.hex();
|
||||
final Set<String> used = keysInUse;
|
||||
if (used != null) used.add(hex);
|
||||
}
|
||||
|
||||
private static String driverId() {
|
||||
String id = driverId;
|
||||
if (id == null) {
|
||||
id = RENDER_BACKEND.getString(GL11.GL_VENDOR) + '|' + RENDER_BACKEND.getString(GL11.GL_RENDERER) + '|'
|
||||
+ RENDER_BACKEND.getString(GL11.GL_VERSION) + '|' + RENDER_BACKEND.getString(GL20.GL_SHADING_LANGUAGE_VERSION) + '|'
|
||||
+ RenderSystem.isGLES() + '|' + RENDER_BACKEND.getMinGLSLVersion();
|
||||
driverId = id;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public static final class Key {
|
||||
private final ShaderDiskCache.Key diskKey;
|
||||
|
||||
private Key(ShaderDiskCache.Key diskKey) {
|
||||
this.diskKey = diskKey;
|
||||
}
|
||||
|
||||
public Key stage(int shaderType, String source) {
|
||||
diskKey.str("stage").i(shaderType).str(source);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Key attribute(String name, int index) {
|
||||
diskKey.str("attribute").str(name).i(index);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,14 +19,18 @@ import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Stream;
|
||||
@@ -49,7 +53,10 @@ public final class ShaderDiskCache {
|
||||
|
||||
private ShaderDiskCache() {}
|
||||
|
||||
public static void configure(Path dir, String salt) {
|
||||
private static String configuredSalt;
|
||||
|
||||
public static synchronized void configure(Path dir, String salt) {
|
||||
if (dir.equals(root) && salt.equals(configuredSalt)) return;
|
||||
try {
|
||||
Files.createDirectories(dir);
|
||||
final Path saltFile = dir.resolve("salt");
|
||||
@@ -59,6 +66,7 @@ public final class ShaderDiskCache {
|
||||
Files.write(saltFile, salt.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
root = dir;
|
||||
configuredSalt = salt;
|
||||
AngelicaWorkers.run(() -> enforceCap(dir, MAX_BYTES));
|
||||
LOGGER.info("Shader disk cache at {}", dir);
|
||||
} catch (IOException | RuntimeException e) {
|
||||
@@ -116,9 +124,9 @@ public final class ShaderDiskCache {
|
||||
}
|
||||
}
|
||||
|
||||
public static void put(Key key, byte[] payload) {
|
||||
public static boolean put(Key key, byte[] payload) {
|
||||
final Path r = root;
|
||||
if (r == null) return;
|
||||
if (r == null) return false;
|
||||
final Path target = key.path(r);
|
||||
final Path tmp = target.resolveSibling(key.hex() + '.' + TMP_SEQ.getAndIncrement() + ".tmp");
|
||||
try {
|
||||
@@ -129,11 +137,13 @@ public final class ShaderDiskCache {
|
||||
write(tmp, payload);
|
||||
}
|
||||
Files.move(tmp, target, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
try {
|
||||
Files.deleteIfExists(tmp);
|
||||
} catch (IOException ignored) {}
|
||||
if (writeWarned.compareAndSet(false, true)) LOGGER.warn("Shader disk cache write failed for {}", target, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +212,7 @@ public final class ShaderDiskCache {
|
||||
}
|
||||
}
|
||||
|
||||
public static void putBlob(Key key, String tag, byte[] data) {
|
||||
public static boolean putBlob(Key key, String tag, byte[] data) {
|
||||
final ByteArrayOutputStream bytes = new ByteArrayOutputStream(data.length + 64);
|
||||
try (DataOutputStream out = new DataOutputStream(bytes)) {
|
||||
writeStr(out, tag);
|
||||
@@ -210,11 +220,101 @@ public final class ShaderDiskCache {
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
put(key, bytes.toByteArray());
|
||||
return put(key, bytes.toByteArray());
|
||||
}
|
||||
|
||||
public record Blob(String tag, byte[] data) {}
|
||||
|
||||
public static void remove(Key key) {
|
||||
final Path r = root;
|
||||
if (r == null) return;
|
||||
try {
|
||||
Files.deleteIfExists(key.path(r));
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
public static void retainLayer(String layer, Set<String> keep) {
|
||||
final Path r = root;
|
||||
if (r == null) return;
|
||||
final Path dir = r.resolve(layer);
|
||||
if (!Files.isDirectory(dir)) return;
|
||||
final List<Path> files = new ArrayList<>();
|
||||
try (Stream<Path> list = Files.list(dir)) {
|
||||
list.forEach(files::add);
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Shader disk cache could not list {}", dir, e);
|
||||
return;
|
||||
}
|
||||
for (Path p : files) {
|
||||
final String name = p.getFileName().toString();
|
||||
if (!name.endsWith(".bin")) continue; // in-flight writes are still .tmp
|
||||
if (keep != null && keep.contains(name.substring(0, name.length() - 4))) continue;
|
||||
try {
|
||||
Files.deleteIfExists(p);
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
public static void touchLayer(String layer) {
|
||||
final Path r = root;
|
||||
if (r == null) return;
|
||||
final Path dir = r.resolve(layer);
|
||||
try {
|
||||
Files.createDirectories(dir);
|
||||
Files.setLastModifiedTime(dir, FileTime.fromMillis(System.currentTimeMillis()));
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Shader disk cache could not mark {} as used", dir, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void keepNewestSubLayers(String layer, int keep) {
|
||||
final Path r = root;
|
||||
if (r == null) return;
|
||||
final Path dir = r.resolve(layer);
|
||||
if (!Files.isDirectory(dir)) return;
|
||||
final List<Path> subLayers = new ArrayList<>();
|
||||
try (Stream<Path> list = Files.list(dir)) {
|
||||
list.filter(Files::isDirectory).forEach(subLayers::add);
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Shader disk cache could not list {}", dir, e);
|
||||
return;
|
||||
}
|
||||
subLayers.sort(Comparator.comparingLong(ShaderDiskCache::lastModifiedMillis).reversed());
|
||||
for (int i = keep; i < subLayers.size(); i++) {
|
||||
deleteRecursively(subLayers.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
public static void deleteLayer(String layer) {
|
||||
final Path r = root;
|
||||
if (r != null) deleteRecursively(r.resolve(layer));
|
||||
}
|
||||
|
||||
private static long lastModifiedMillis(Path path) {
|
||||
try {
|
||||
return Files.getLastModifiedTime(path).toMillis();
|
||||
} catch (IOException e) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
private static void deleteRecursively(Path dir) {
|
||||
if (!Files.exists(dir)) return;
|
||||
final List<Path> paths = new ArrayList<>();
|
||||
try (Stream<Path> walk = Files.walk(dir)) {
|
||||
walk.forEach(paths::add);
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Shader disk cache could not list {}", dir, e);
|
||||
return;
|
||||
}
|
||||
Collections.reverse(paths);
|
||||
for (Path p : paths) {
|
||||
try {
|
||||
Files.deleteIfExists(p);
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
static void enforceCap(Path dir, long maxBytes) {
|
||||
try {
|
||||
final long[] total = {0};
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.gtnewhorizons.angelica.glsm.shader;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GlslTransformUtils;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.tree.ParseTree;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.taumc.glsl.grammar.GLSLLexer;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* What the source passes look up in a parsed shader.
|
||||
*/
|
||||
public final class ShaderIndex {
|
||||
|
||||
public record Directives(Set<String> identifiers, boolean hasConditionalOrMacro, int versionEnd) {
|
||||
private static final Directives NONE = new Directives(Set.of(), false, -1);
|
||||
}
|
||||
|
||||
private final GLSLParser.Translation_unitContext root;
|
||||
private final String source;
|
||||
private final Map<String, List<Token>> identifiers = new HashMap<>();
|
||||
private final Map<String, List<GLSLParser.Postfix_expressionContext>> calls = new HashMap<>();
|
||||
private final List<GLSLParser.DeclarationContext> globalDeclarations = new ArrayList<>();
|
||||
private @Nullable Directives directives;
|
||||
|
||||
public ShaderIndex(GLSLParser.Translation_unitContext root, String source) {
|
||||
this.root = root;
|
||||
this.source = source;
|
||||
collect(root);
|
||||
for (GLSLParser.External_declarationContext external : root.external_declaration()) {
|
||||
if (external.declaration() != null) globalDeclarations.add(external.declaration());
|
||||
}
|
||||
}
|
||||
|
||||
private void collect(ParseTree node) {
|
||||
if (node instanceof TerminalNode terminal) {
|
||||
final Token token = terminal.getSymbol();
|
||||
if (token.getType() == GLSLLexer.IDENTIFIER) identifiers.computeIfAbsent(token.getText(), k -> new ArrayList<>(2)).add(token);
|
||||
return;
|
||||
}
|
||||
if (node instanceof GLSLParser.Postfix_expressionContext p && p.LEFT_PAREN() != null) {
|
||||
calls.computeIfAbsent(calleeName(p), k -> new ArrayList<>(2)).add(p);
|
||||
}
|
||||
for (int i = 0, n = node.getChildCount(); i < n; i++) collect(node.getChild(i));
|
||||
}
|
||||
|
||||
public GLSLParser.Translation_unitContext root() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public String source() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public List<Token> occurrences(String name) {
|
||||
return identifiers.getOrDefault(name, List.of());
|
||||
}
|
||||
|
||||
public List<GLSLParser.Postfix_expressionContext> callsTo(String name) {
|
||||
return calls.getOrDefault(name, List.of());
|
||||
}
|
||||
|
||||
public List<GLSLParser.DeclarationContext> globalDeclarations() {
|
||||
return globalDeclarations;
|
||||
}
|
||||
|
||||
/** Lexes the source on first use. */
|
||||
public Directives directives() {
|
||||
if (directives == null) directives = scanDirectives(source);
|
||||
return directives;
|
||||
}
|
||||
|
||||
private static Directives scanDirectives(String source) {
|
||||
if (source.indexOf('#') < 0) return Directives.NONE;
|
||||
final GLSLLexer lexer = GlslTransformUtils.quietLexer(source);
|
||||
final Set<String> names = new HashSet<>();
|
||||
boolean conditionalOrMacro = false;
|
||||
int versionEnd = -1;
|
||||
boolean inVersion = false;
|
||||
for (Token token = lexer.nextToken(); token.getType() != Token.EOF; token = lexer.nextToken()) {
|
||||
final int type = token.getType();
|
||||
if (type == GLSLLexer.VERSION_DIRECTIVE && versionEnd < 0) {
|
||||
inVersion = true;
|
||||
versionEnd = token.getStopIndex() + 1;
|
||||
continue;
|
||||
}
|
||||
if (inVersion && (type == GLSLLexer.NUMBER || type == GLSLLexer.PROFILE)) {
|
||||
versionEnd = token.getStopIndex() + 1;
|
||||
continue;
|
||||
}
|
||||
inVersion = false;
|
||||
switch (type) {
|
||||
case GLSLLexer.PROGRAM_TEXT, GLSLLexer.MACRO_TEXT, GLSLLexer.CONSTANT_EXPRESSION -> collectIdentifiers(token.getText(), names);
|
||||
case GLSLLexer.IF_DIRECTIVE, GLSLLexer.IFDEF_DIRECTIVE, GLSLLexer.IFNDEF_DIRECTIVE, GLSLLexer.DEFINE_DIRECTIVE -> conditionalOrMacro = true;
|
||||
default -> { }
|
||||
}
|
||||
}
|
||||
return new Directives(names, conditionalOrMacro, versionEnd);
|
||||
}
|
||||
|
||||
private static void collectIdentifiers(String text, Set<String> out) {
|
||||
if (text == null) return;
|
||||
final int len = text.length();
|
||||
int i = 0;
|
||||
while (i < len) {
|
||||
final char c = text.charAt(i);
|
||||
if (c != '_' && !Character.isLetter(c)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
int end = i + 1;
|
||||
while (end < len) {
|
||||
final char n = text.charAt(end);
|
||||
if (n != '_' && !Character.isLetterOrDigit(n)) break;
|
||||
end++;
|
||||
}
|
||||
out.add(text.substring(i, end));
|
||||
i = end;
|
||||
}
|
||||
}
|
||||
|
||||
public static String calleeName(GLSLParser.Postfix_expressionContext call) {
|
||||
final ParseTree callee = call.getChild(0);
|
||||
return callee instanceof ParserRuleContext ? callee.getText() : "";
|
||||
}
|
||||
|
||||
public static List<GLSLParser.Typeless_declarationContext> declarators(GLSLParser.Init_declarator_listContext list) {
|
||||
final GLSLParser.Single_declarationContext single = list.single_declaration();
|
||||
final List<GLSLParser.Typeless_declarationContext> rest = list.typeless_declaration();
|
||||
if (single == null || single.typeless_declaration() == null) return rest;
|
||||
final List<GLSLParser.Typeless_declarationContext> all = new ArrayList<>(rest.size() + 1);
|
||||
all.add(single.typeless_declaration());
|
||||
all.addAll(rest);
|
||||
return all;
|
||||
}
|
||||
|
||||
public static boolean hasStorageQualifier(@Nullable GLSLParser.Type_qualifierContext qualifier, String keyword) {
|
||||
if (qualifier == null) return false;
|
||||
for (GLSLParser.Single_type_qualifierContext q : qualifier.single_type_qualifier()) {
|
||||
if (q.storage_qualifier() != null && keyword.equals(q.storage_qualifier().getText())) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+2
-19
@@ -47,14 +47,6 @@ public final class SpirvShaderTranslator {
|
||||
private SpirvShaderTranslator() {}
|
||||
|
||||
public static @Nullable String glslToGlslEs(String source, int glShaderType, String debugName) {
|
||||
return glslToGlslEsImpl(source, glShaderType, debugName, false);
|
||||
}
|
||||
|
||||
static @Nullable String glslToGlslEsRawForTest(String source, int glShaderType, String debugName) {
|
||||
return glslToGlslEsImpl(source, glShaderType, debugName, true);
|
||||
}
|
||||
|
||||
private static @Nullable String glslToGlslEsImpl(String source, int glShaderType, String debugName, boolean rawOutput) {
|
||||
final int shaderKind = shaderKindForGlType(glShaderType);
|
||||
if (shaderKind < 0) {
|
||||
LOGGER.warn("Unknown GL shader type 0x{} for '{}' - skipping SPIR-V translation", Integer.toHexString(glShaderType), debugName);
|
||||
@@ -74,7 +66,7 @@ public final class SpirvShaderTranslator {
|
||||
try {
|
||||
final String emitted = emitGlslEs(cr.spirv(), debugName, glShaderType, pre.explicitVsInputs());
|
||||
if (emitted == null) return null;
|
||||
return rawOutput ? emitted : postProcessEsOutput(emitted, pre.boolUniforms());
|
||||
return postProcessEsOutput(emitted, pre.boolUniforms());
|
||||
} finally {
|
||||
memFree(cr.spirv());
|
||||
}
|
||||
@@ -163,16 +155,7 @@ public final class SpirvShaderTranslator {
|
||||
final GLSLParser.Single_declarationContext single = ctx.single_declaration();
|
||||
if (single == null || single.typeless_declaration() == null) return;
|
||||
final GLSLParser.Fully_specified_typeContext fst = single.fully_specified_type();
|
||||
if (fst == null || fst.type_qualifier() == null) return;
|
||||
|
||||
boolean isUniform = false;
|
||||
for (GLSLParser.Single_type_qualifierContext stq : fst.type_qualifier().single_type_qualifier()) {
|
||||
if (stq.storage_qualifier() != null && "uniform".equals(stq.storage_qualifier().getText())) {
|
||||
isUniform = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isUniform) return;
|
||||
if (fst == null || !ShaderIndex.hasStorageQualifier(fst.type_qualifier(), "uniform")) return;
|
||||
|
||||
if (fst.type_specifier() == null || fst.type_specifier().type_specifier_nonarray() == null) return;
|
||||
final GLSLParser.Type_specifier_nonarrayContext ts = fst.type_specifier().type_specifier_nonarray();
|
||||
|
||||
@@ -14,8 +14,8 @@ public final class AlphaStateStack extends AlphaState implements CowStateStack<A
|
||||
|
||||
public AlphaStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new AlphaState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new AlphaState[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new AlphaState();
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,11 @@ public final class AlphaStateStack extends AlphaState implements CowStateStack<A
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return VanillaStateLayer.isHeld(vanillaLayer) || !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[s].set(this));
|
||||
|
||||
@@ -17,8 +17,8 @@ public final class BlendStateStack extends BlendState implements CowStateStack<B
|
||||
|
||||
public BlendStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new BlendState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new BlendState[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new BlendState();
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,11 @@ public final class BlendStateStack extends BlendState implements CowStateStack<B
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return funcUnknown || VanillaStateLayer.isHeld(vanillaLayer) || !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[s].set(this));
|
||||
|
||||
@@ -44,7 +44,7 @@ public class BooleanStateStack extends BooleanState implements CowStateStack<Boo
|
||||
public BooleanStateStack(int glCap, boolean initialState, boolean ffpStateOnly) {
|
||||
super(glCap, ffpStateOnly);
|
||||
this.enabled = initialState;
|
||||
stack = new boolean[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
stack = new boolean[GLStateManager.STATE_SLOTS];
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,6 +52,11 @@ public class BooleanStateStack extends BooleanState implements CowStateStack<Boo
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return stateUnknown || (vanillaLayer != null && vanillaLayer.isOverrideHeld()) || enabled != stack[slot];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int slot) {
|
||||
stack[slot] = vanillaValue();
|
||||
|
||||
@@ -10,8 +10,8 @@ public final class Color4Stack extends Color4 implements CowStateStack<Color4Sta
|
||||
|
||||
public Color4Stack(int id) {
|
||||
cow.id = id;
|
||||
stack = new Color4[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new Color4[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new Color4();
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,11 @@ public final class Color4Stack extends Color4 implements CowStateStack<Color4Sta
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
|
||||
@@ -15,8 +15,8 @@ public final class ColorMaskStack extends ColorMask implements CowStateStack<Col
|
||||
|
||||
public ColorMaskStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new ColorMask[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new ColorMask[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new ColorMask();
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,11 @@ public final class ColorMaskStack extends ColorMask implements CowStateStack<Col
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return VanillaStateLayer.isHeld(vanillaLayer) || !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[s].set(this));
|
||||
|
||||
@@ -101,6 +101,36 @@ public final class CowDispatch {
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean slotChanged(byte kind, CowStateStack<?> s, int slot) {
|
||||
return switch (kind) {
|
||||
case BLEND -> ((BlendStateStack) s).slotChanged(slot);
|
||||
case BOOLEAN -> ((BooleanStateStack) s).slotChanged(slot);
|
||||
case CLIP_PLANE_BOOLEAN -> ((ClipPlaneBooleanState) s).slotChanged(slot);
|
||||
case TEXTURE_UNIT_BOOLEAN -> ((TextureUnitBooleanStateStack) s).slotChanged(slot);
|
||||
case COLOR_MASK -> ((ColorMaskStack) s).slotChanged(slot);
|
||||
case LIGHT_STATE -> ((LightStateStack) s).slotChanged(slot);
|
||||
case MATERIAL -> ((MaterialStateStack) s).slotChanged(slot);
|
||||
case STENCIL -> ((StencilStateStack) s).slotChanged(slot);
|
||||
case MATRIX_MODE -> ((MatrixModeStack) s).slotChanged(slot);
|
||||
case LIGHT_MODEL -> ((LightModelStateStack) s).slotChanged(slot);
|
||||
case ALPHA -> ((AlphaStateStack) s).slotChanged(slot);
|
||||
case POINT -> ((PointStateStack) s).slotChanged(slot);
|
||||
case FOG -> ((FogStateStack) s).slotChanged(slot);
|
||||
case VEC4F -> ((Vec4fStack) s).slotChanged(slot);
|
||||
case VEC3F -> ((Vec3fStack) s).slotChanged(slot);
|
||||
case INTEGER -> ((IntegerStateStack) s).slotChanged(slot);
|
||||
case DEPTH -> ((DepthStateStack) s).slotChanged(slot);
|
||||
case VIEWPORT -> ((ViewPortStateStack) s).slotChanged(slot);
|
||||
case POLYGON -> ((PolygonStateStack) s).slotChanged(slot);
|
||||
case COLOR4 -> ((Color4Stack) s).slotChanged(slot);
|
||||
case LINE -> ((LineStateStack) s).slotChanged(slot);
|
||||
case TEXTURE_BINDING -> ((TextureBindingStack) s).slotChanged(slot);
|
||||
case TEX_ENV -> ((TexEnvState) s).slotChanged(slot);
|
||||
case SCISSOR -> ((ScissorStateStack) s).slotChanged(slot);
|
||||
default -> throw new IllegalStateException("Unknown kind: " + kind);
|
||||
};
|
||||
}
|
||||
|
||||
public static void restoreSlot(byte kind, CowStateStack<?> s, int slot) {
|
||||
switch (kind) {
|
||||
case BLEND -> ((BlendStateStack) s).restoreSlot(slot);
|
||||
|
||||
@@ -20,6 +20,8 @@ public interface CowStateStack<T> extends IStateStack<T> {
|
||||
|
||||
void restoreSlot(int slot);
|
||||
|
||||
boolean slotChanged(int slot);
|
||||
|
||||
default int stackId() {
|
||||
return cowDepths().id;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ public final class DepthStateStack extends DepthState implements CowStateStack<D
|
||||
|
||||
public DepthStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new DepthState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new DepthState[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new DepthState();
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,11 @@ public final class DepthStateStack extends DepthState implements CowStateStack<D
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return VanillaStateLayer.isHeld(vanillaLayer) || !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
VanillaStateLayer.capture(vanillaLayer, stack[s].set(this));
|
||||
|
||||
@@ -10,8 +10,8 @@ public final class FogStateStack extends FogState implements CowStateStack<FogSt
|
||||
|
||||
public FogStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new FogState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new FogState[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new FogState();
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,11 @@ public final class FogStateStack extends FogState implements CowStateStack<FogSt
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
|
||||
@@ -11,8 +11,8 @@ public final class IntegerStateStack extends IntegerState implements CowStateSta
|
||||
public IntegerStateStack(int val, int id) {
|
||||
cow.id = id;
|
||||
setValue(val);
|
||||
stack = new IntegerState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new IntegerState[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new IntegerState();
|
||||
stack[i].setValue(val);
|
||||
}
|
||||
@@ -30,6 +30,11 @@ public final class IntegerStateStack extends IntegerState implements CowStateSta
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
|
||||
@@ -11,8 +11,8 @@ public final class LightModelStateStack extends LightModelState implements CowSt
|
||||
public LightModelStateStack(int id) {
|
||||
super();
|
||||
cow.id = id;
|
||||
stack = new LightModelState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new LightModelState[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new LightModelState();
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,11 @@ public final class LightModelStateStack extends LightModelState implements CowSt
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
|
||||
@@ -11,8 +11,8 @@ public final class LightStateStack extends LightState implements CowStateStack<L
|
||||
public LightStateStack(int light, int id) {
|
||||
super(light);
|
||||
cow.id = id;
|
||||
stack = new LightState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new LightState[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new LightState(light);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,11 @@ public final class LightStateStack extends LightState implements CowStateStack<L
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
|
||||
@@ -11,8 +11,8 @@ public final class LineStateStack extends LineState implements CowStateStack<Lin
|
||||
|
||||
public LineStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new LineState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new LineState[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new LineState();
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,11 @@ public final class LineStateStack extends LineState implements CowStateStack<Lin
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
|
||||
@@ -11,8 +11,8 @@ public final class MaterialStateStack extends MaterialState implements CowStateS
|
||||
public MaterialStateStack(int face, int id) {
|
||||
super(face);
|
||||
cow.id = id;
|
||||
stack = new MaterialState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new MaterialState[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new MaterialState(face);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,11 @@ public final class MaterialStateStack extends MaterialState implements CowStateS
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
|
||||
@@ -10,8 +10,8 @@ public final class MatrixModeStack extends MatrixMode implements CowStateStack<M
|
||||
|
||||
public MatrixModeStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new MatrixMode[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new MatrixMode[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new MatrixMode();
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,11 @@ public final class MatrixModeStack extends MatrixMode implements CowStateStack<M
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
|
||||
@@ -11,8 +11,8 @@ public final class PointStateStack extends PointState implements CowStateStack<P
|
||||
|
||||
public PointStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new PointState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new PointState[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new PointState();
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,11 @@ public final class PointStateStack extends PointState implements CowStateStack<P
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
|
||||
@@ -11,8 +11,8 @@ public final class PolygonStateStack extends PolygonState implements CowStateSta
|
||||
|
||||
public PolygonStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new PolygonState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new PolygonState[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new PolygonState();
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,11 @@ public final class PolygonStateStack extends PolygonState implements CowStateSta
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.glsm.stacks;
|
||||
|
||||
public final class RetainedState {
|
||||
public int[] ids = new int[64];
|
||||
public int count;
|
||||
public int program;
|
||||
public long programGen;
|
||||
|
||||
public void ensureCapacity(int capacity) {
|
||||
if (ids.length < capacity) ids = new int[capacity];
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@ public final class ScissorStateStack extends ScissorState implements CowStateSta
|
||||
|
||||
public ScissorStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new ScissorState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new ScissorState[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new ScissorState();
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,11 @@ public final class ScissorStateStack extends ScissorState implements CowStateSta
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
|
||||
@@ -11,8 +11,8 @@ public final class StencilStateStack extends StencilState implements CowStateSta
|
||||
|
||||
public StencilStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new StencilState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new StencilState[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new StencilState();
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,11 @@ public final class StencilStateStack extends StencilState implements CowStateSta
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
|
||||
@@ -12,8 +12,8 @@ public final class TextureBindingStack extends TextureBinding implements CowStat
|
||||
public TextureBindingStack(int id, int unit) {
|
||||
cow = new CowDepths(StateSet.R_TEXTURE, unit);
|
||||
cow.id = id;
|
||||
stack = new TextureBinding[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new TextureBinding[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new TextureBinding();
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,11 @@ public final class TextureBindingStack extends TextureBinding implements CowStat
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
|
||||
@@ -15,7 +15,7 @@ public final class Vec3fStack implements CowStateStack<Vec3fStack> {
|
||||
public Vec3fStack(Vector3f value, int id) {
|
||||
this.value = value;
|
||||
cow.id = id;
|
||||
stack = new float[GLStateManager.MAX_ATTRIB_STACK_DEPTH][3];
|
||||
stack = new float[GLStateManager.STATE_SLOTS][3];
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -23,6 +23,11 @@ public final class Vec3fStack implements CowStateStack<Vec3fStack> {
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return value.x != stack[slot][0] || value.y != stack[slot][1] || value.z != stack[slot][2];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s][0] = value.x;
|
||||
|
||||
@@ -15,7 +15,7 @@ public final class Vec4fStack implements CowStateStack<Vec4fStack> {
|
||||
public Vec4fStack(Vector4f value, int id) {
|
||||
this.value = value;
|
||||
cow.id = id;
|
||||
stack = new float[GLStateManager.MAX_ATTRIB_STACK_DEPTH][4];
|
||||
stack = new float[GLStateManager.STATE_SLOTS][4];
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -23,6 +23,11 @@ public final class Vec4fStack implements CowStateStack<Vec4fStack> {
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return value.x != stack[slot][0] || value.y != stack[slot][1] || value.z != stack[slot][2] || value.w != stack[slot][3];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s][0] = value.x;
|
||||
|
||||
@@ -11,8 +11,8 @@ public final class ViewPortStateStack extends ViewportState implements CowStateS
|
||||
|
||||
public ViewPortStateStack(int id) {
|
||||
cow.id = id;
|
||||
stack = new ViewportState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new ViewportState[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new ViewportState();
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,11 @@ public final class ViewPortStateStack extends ViewportState implements CowStateS
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].set(this);
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.gtnewhorizons.angelica.glsm.states;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
public record PixelStoreState(
|
||||
int alignment,
|
||||
int rowLength,
|
||||
int skipRows,
|
||||
int skipPixels,
|
||||
int imageHeight,
|
||||
int skipImages,
|
||||
boolean swapBytes,
|
||||
boolean lsbFirst
|
||||
) {
|
||||
public static final PixelStoreState DEFAULT = new PixelStoreState(4, 0, 0, 0, 0, 0, false, false);
|
||||
|
||||
public boolean isDefault() {
|
||||
return this.equals(DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean isPack(int pname) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_PACK_ALIGNMENT, GL11.GL_PACK_ROW_LENGTH, GL11.GL_PACK_SKIP_ROWS, GL11.GL_PACK_SKIP_PIXELS,
|
||||
GL12.GL_PACK_IMAGE_HEIGHT, GL12.GL_PACK_SKIP_IMAGES, GL11.GL_PACK_SWAP_BYTES, GL11.GL_PACK_LSB_FIRST -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
public PixelStoreState with(int pname, int param) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_UNPACK_ALIGNMENT, GL11.GL_PACK_ALIGNMENT -> (param == alignment || (param != 1 && param != 2 && param != 4 && param != 8)) ? this : new PixelStoreState(param, rowLength, skipRows, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst);
|
||||
case GL11.GL_UNPACK_ROW_LENGTH, GL11.GL_PACK_ROW_LENGTH -> (param == rowLength || param < 0) ? this : new PixelStoreState(alignment, param, skipRows, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst);
|
||||
case GL11.GL_UNPACK_SKIP_ROWS, GL11.GL_PACK_SKIP_ROWS -> (param == skipRows || param < 0) ? this : new PixelStoreState(alignment, rowLength, param, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst);
|
||||
case GL11.GL_UNPACK_SKIP_PIXELS, GL11.GL_PACK_SKIP_PIXELS -> (param == skipPixels || param < 0) ? this : new PixelStoreState(alignment, rowLength, skipRows, param, imageHeight, skipImages, swapBytes, lsbFirst);
|
||||
case GL12.GL_UNPACK_IMAGE_HEIGHT, GL12.GL_PACK_IMAGE_HEIGHT -> (param == imageHeight || param < 0) ? this : new PixelStoreState(alignment, rowLength, skipRows, skipPixels, param, skipImages, swapBytes, lsbFirst);
|
||||
case GL12.GL_UNPACK_SKIP_IMAGES, GL12.GL_PACK_SKIP_IMAGES -> (param == skipImages || param < 0) ? this : new PixelStoreState(alignment, rowLength, skipRows, skipPixels, imageHeight, param, swapBytes, lsbFirst);
|
||||
case GL11.GL_UNPACK_SWAP_BYTES, GL11.GL_PACK_SWAP_BYTES -> (param != 0) == swapBytes ? this : new PixelStoreState(alignment, rowLength, skipRows, skipPixels, imageHeight, skipImages, param != 0, lsbFirst);
|
||||
case GL11.GL_UNPACK_LSB_FIRST, GL11.GL_PACK_LSB_FIRST -> (param != 0) == lsbFirst ? this : new PixelStoreState(alignment, rowLength, skipRows, skipPixels, imageHeight, skipImages, swapBytes, param != 0);
|
||||
default -> this;
|
||||
};
|
||||
}
|
||||
|
||||
public static void applyDiff(PixelStoreState from, PixelStoreState to, boolean pack) {
|
||||
if (from == to) return;
|
||||
if (from.alignment != to.alignment) RENDER_BACKEND.pixelStorei(pack ? GL11.GL_PACK_ALIGNMENT : GL11.GL_UNPACK_ALIGNMENT, to.alignment);
|
||||
if (from.rowLength != to.rowLength) RENDER_BACKEND.pixelStorei(pack ? GL11.GL_PACK_ROW_LENGTH : GL11.GL_UNPACK_ROW_LENGTH, to.rowLength);
|
||||
if (from.skipRows != to.skipRows) RENDER_BACKEND.pixelStorei(pack ? GL11.GL_PACK_SKIP_ROWS : GL11.GL_UNPACK_SKIP_ROWS, to.skipRows);
|
||||
if (from.skipPixels != to.skipPixels) RENDER_BACKEND.pixelStorei(pack ? GL11.GL_PACK_SKIP_PIXELS : GL11.GL_UNPACK_SKIP_PIXELS, to.skipPixels);
|
||||
if (from.imageHeight != to.imageHeight) RENDER_BACKEND.pixelStorei(pack ? GL12.GL_PACK_IMAGE_HEIGHT : GL12.GL_UNPACK_IMAGE_HEIGHT, to.imageHeight);
|
||||
if (from.skipImages != to.skipImages) RENDER_BACKEND.pixelStorei(pack ? GL12.GL_PACK_SKIP_IMAGES : GL12.GL_UNPACK_SKIP_IMAGES, to.skipImages);
|
||||
if (from.swapBytes != to.swapBytes) RENDER_BACKEND.pixelStorei(pack ? GL11.GL_PACK_SWAP_BYTES : GL11.GL_UNPACK_SWAP_BYTES, to.swapBytes ? 1 : 0);
|
||||
if (from.lsbFirst != to.lsbFirst) RENDER_BACKEND.pixelStorei(pack ? GL11.GL_PACK_LSB_FIRST : GL11.GL_UNPACK_LSB_FIRST, to.lsbFirst ? 1 : 0);
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.states;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
|
||||
/**
|
||||
* Immutable client-side pixel unpack state (glPixelStore UNPACK_* params)
|
||||
*/
|
||||
public record PixelUnpackState(
|
||||
int alignment,
|
||||
int rowLength,
|
||||
int skipRows,
|
||||
int skipPixels,
|
||||
int imageHeight,
|
||||
int skipImages,
|
||||
boolean swapBytes,
|
||||
boolean lsbFirst
|
||||
) {
|
||||
public static final PixelUnpackState DEFAULT = new PixelUnpackState(4, 0, 0, 0, 0, 0, false, false);
|
||||
|
||||
public boolean isDefault() {
|
||||
return this.equals(DEFAULT);
|
||||
}
|
||||
|
||||
public PixelUnpackState with(int pname, int param) {
|
||||
return switch (pname) {
|
||||
case GL11.GL_UNPACK_ALIGNMENT -> (param == 1 || param == 2 || param == 4 || param == 8) ? new PixelUnpackState(param, rowLength, skipRows, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst) : this;
|
||||
case GL11.GL_UNPACK_ROW_LENGTH -> param >= 0 ? new PixelUnpackState(alignment, param, skipRows, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst) : this;
|
||||
case GL11.GL_UNPACK_SKIP_ROWS -> param >= 0 ? new PixelUnpackState(alignment, rowLength, param, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst) : this;
|
||||
case GL11.GL_UNPACK_SKIP_PIXELS -> param >= 0 ? new PixelUnpackState(alignment, rowLength, skipRows, param, imageHeight, skipImages, swapBytes, lsbFirst) : this;
|
||||
case GL12.GL_UNPACK_IMAGE_HEIGHT -> param >= 0 ? new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, param, skipImages, swapBytes, lsbFirst) : this;
|
||||
case GL12.GL_UNPACK_SKIP_IMAGES -> param >= 0 ? new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, imageHeight, param, swapBytes, lsbFirst) : this;
|
||||
case GL11.GL_UNPACK_SWAP_BYTES -> new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, imageHeight, skipImages, param != 0, lsbFirst);
|
||||
case GL11.GL_UNPACK_LSB_FIRST -> new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, imageHeight, skipImages, swapBytes, param != 0);
|
||||
default -> this;
|
||||
};
|
||||
}
|
||||
|
||||
public static void applyDiff(PixelUnpackState from, PixelUnpackState to) {
|
||||
if (from.alignment != to.alignment) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_ALIGNMENT, to.alignment);
|
||||
if (from.rowLength != to.rowLength) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_ROW_LENGTH, to.rowLength);
|
||||
if (from.skipRows != to.skipRows) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_SKIP_ROWS, to.skipRows);
|
||||
if (from.skipPixels != to.skipPixels) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, to.skipPixels);
|
||||
if (from.imageHeight != to.imageHeight) RENDER_BACKEND.pixelStorei(GL12.GL_UNPACK_IMAGE_HEIGHT, to.imageHeight);
|
||||
if (from.skipImages != to.skipImages) RENDER_BACKEND.pixelStorei(GL12.GL_UNPACK_SKIP_IMAGES, to.skipImages);
|
||||
if (from.swapBytes != to.swapBytes) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_SWAP_BYTES, to.swapBytes ? 1 : 0);
|
||||
if (from.lsbFirst != to.lsbFirst) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_LSB_FIRST, to.lsbFirst ? 1 : 0);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import com.gtnewhorizons.angelica.glsm.stacks.CowStateStack;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Per-texture-unit GL_TEXTURE_ENV state for FFP emulation. Tracks the simple texenv mode and all GL_COMBINE sub-parameters.
|
||||
*/
|
||||
@@ -45,8 +47,8 @@ public final class TexEnvState implements CowStateStack<TexEnvState> {
|
||||
stack = null;
|
||||
cow = null;
|
||||
} else {
|
||||
stack = new TexEnvState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
|
||||
stack = new TexEnvState[GLStateManager.STATE_SLOTS];
|
||||
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
|
||||
stack[i] = new TexEnvState(true);
|
||||
}
|
||||
cow = new CowDepths();
|
||||
@@ -70,6 +72,14 @@ public final class TexEnvState implements CowStateStack<TexEnvState> {
|
||||
this.envColorA = other.envColorA;
|
||||
}
|
||||
|
||||
private boolean sameAs(TexEnvState other) {
|
||||
return mode == other.mode && combineRgb == other.combineRgb && combineAlpha == other.combineAlpha
|
||||
&& Arrays.equals(sourceRgb, other.sourceRgb) && Arrays.equals(sourceAlpha, other.sourceAlpha)
|
||||
&& Arrays.equals(operandRgb, other.operandRgb) && Arrays.equals(operandAlpha, other.operandAlpha)
|
||||
&& scaleRgb == other.scaleRgb && scaleAlpha == other.scaleAlpha
|
||||
&& envColorR == other.envColorR && envColorG == other.envColorG && envColorB == other.envColorB && envColorA == other.envColorA;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mode = GL11.GL_MODULATE;
|
||||
combineRgb = GL11.GL_MODULATE;
|
||||
@@ -89,6 +99,11 @@ public final class TexEnvState implements CowStateStack<TexEnvState> {
|
||||
return cow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean slotChanged(int slot) {
|
||||
return !sameAs(stack[slot]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void captureSlot(int s) {
|
||||
stack[s].copyFrom(this);
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
@@ -16,6 +17,7 @@ import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class AngelicaWorkers {
|
||||
@@ -153,6 +155,51 @@ public final class AngelicaWorkers {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs every task and returns the results in task order.
|
||||
*/
|
||||
public static <T> List<T> invokeAll(List<? extends Supplier<? extends T>> tasks) {
|
||||
final int count = tasks.size();
|
||||
final Object[] results = new Object[count];
|
||||
if (count == 0) return new ArrayList<>();
|
||||
final AtomicInteger next = new AtomicInteger();
|
||||
final CountDownLatch finished = new CountDownLatch(count);
|
||||
final AtomicReference<Throwable> failure = new AtomicReference<>();
|
||||
final Runnable drain = () -> {
|
||||
int i;
|
||||
while ((i = next.getAndIncrement()) < count) {
|
||||
try {
|
||||
results[i] = tasks.get(i).get();
|
||||
} catch (Throwable t) {
|
||||
failure.compareAndSet(null, t);
|
||||
} finally {
|
||||
finished.countDown();
|
||||
}
|
||||
}
|
||||
};
|
||||
final int helpers = Math.min(threads(), count - 1);
|
||||
for (int h = 0; h < helpers; h++) {
|
||||
run(drain);
|
||||
}
|
||||
drain.run();
|
||||
try {
|
||||
finished.await();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IllegalStateException("Interrupted waiting for worker tasks", e);
|
||||
}
|
||||
final Throwable t = failure.get();
|
||||
if (t instanceof RuntimeException re) throw re;
|
||||
if (t instanceof Error err) throw err;
|
||||
if (t != null) throw new IllegalStateException(t);
|
||||
final List<T> out = new ArrayList<>(count);
|
||||
for (Object result : results) {
|
||||
@SuppressWarnings("unchecked") final T typed = (T) result;
|
||||
out.add(typed);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static void checkIdleShutdown() {
|
||||
synchronized (lock) {
|
||||
final ExecutorService current = executor;
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_HudCacheBlendOverride_GLTest {
|
||||
|
||||
private static void assertDriverFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
assertEquals(srcRgb, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB));
|
||||
assertEquals(dstRgb, GL11.glGetInteger(GL14.GL_BLEND_DST_RGB));
|
||||
assertEquals(srcAlpha, GL11.glGetInteger(GL14.GL_BLEND_SRC_ALPHA));
|
||||
assertEquals(dstAlpha, GL11.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
|
||||
}
|
||||
|
||||
private static void assertCachedFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
|
||||
assertEquals(srcRgb, GLStateManager.glGetInteger(GL14.GL_BLEND_SRC_RGB));
|
||||
assertEquals(dstRgb, GLStateManager.glGetInteger(GL14.GL_BLEND_DST_RGB));
|
||||
assertEquals(srcAlpha, GLStateManager.glGetInteger(GL14.GL_BLEND_SRC_ALPHA));
|
||||
assertEquals(dstAlpha, GLStateManager.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
|
||||
}
|
||||
|
||||
@Test
|
||||
void overrideChangesTheDriverFuncButNotTheCache() {
|
||||
try {
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
GLStateManager.setHudCacheOverride(true);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_SRC_ALPHA, GL11.GL_ONE);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertCachedFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertDriverFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLStateManager.setHudCacheOverride(false);
|
||||
assertCachedFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertDriverFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
} finally {
|
||||
GLStateManager.setHudCacheOverride(false);
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void popUnderOverrideKeepsTheOverriddenAlphaOnTheDriver() {
|
||||
try {
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
final int d = GLStateManager.pushState(StateSet.BLEND);
|
||||
GLStateManager.setHudCacheOverride(true);
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
|
||||
GLStateManager.popStateTo(d);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLStateManager.setHudCacheOverride(false);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
} finally {
|
||||
GLStateManager.setHudCacheOverride(false);
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void separateAlphaIsForcedEvenWhenDstAlphaAlreadyMatches() {
|
||||
try {
|
||||
GLStateManager.setHudCacheOverride(true);
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
} finally {
|
||||
GLStateManager.setHudCacheOverride(false);
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
}
|
||||
}
|
||||
}
|
||||
+737
@@ -0,0 +1,737 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VertexKey;
|
||||
import com.gtnewhorizons.angelica.glsm.states.PixelStoreState;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_LegacyStackQueries_GLTest {
|
||||
|
||||
private static final int[] SINGLE_VALUE_QUERIES = {
|
||||
GL11.GL_ATTRIB_STACK_DEPTH,
|
||||
GL11.GL_CLIENT_ATTRIB_STACK_DEPTH,
|
||||
GL11.GL_TEXTURE_STACK_DEPTH,
|
||||
GL11.GL_MAX_ATTRIB_STACK_DEPTH,
|
||||
GL11.GL_MAX_CLIENT_ATTRIB_STACK_DEPTH,
|
||||
GL11.GL_MAX_MODELVIEW_STACK_DEPTH,
|
||||
GL11.GL_MAX_PROJECTION_STACK_DEPTH,
|
||||
GL11.GL_MAX_TEXTURE_STACK_DEPTH,
|
||||
GL13.GL_MAX_TEXTURE_UNITS,
|
||||
GL11.GL_MAX_LIGHTS,
|
||||
GL11.GL_STENCIL_BITS,
|
||||
GL11.GL_DEPTH_BITS,
|
||||
};
|
||||
|
||||
private static boolean positionArrayEnabled() {
|
||||
final VAOManager.Attrib position = GLStateManager.ctx().vaos.attrib(Usage.POSITION.getAttributeLocation());
|
||||
return position != null && position.enabled;
|
||||
}
|
||||
|
||||
private static boolean colorArrayEnabled() {
|
||||
return (GLStateManager.ctx().vaos.getVertexFlags() & VertexFlags.COLOR_BIT) != 0;
|
||||
}
|
||||
|
||||
private static void drainErrors() {
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
private static void drainClientAttribStack() {
|
||||
while (GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH) > 0) {
|
||||
GLStateManager.glPopClientAttrib();
|
||||
}
|
||||
}
|
||||
|
||||
private static void drainAttribStack() {
|
||||
while (GLStateManager.getAttribDepth() > 0) {
|
||||
GLStateManager.glPopAttrib();
|
||||
}
|
||||
}
|
||||
|
||||
private static int pushToLimit(int depthPname, int maxPname) {
|
||||
final int max = GLStateManager.glGetInteger(maxPname);
|
||||
for (int i = 0; i < max + 4; i++) {
|
||||
GLStateManager.glPushMatrix();
|
||||
}
|
||||
return GLStateManager.glGetInteger(depthPname);
|
||||
}
|
||||
|
||||
private static void popToBase(int depthPname) {
|
||||
for (int i = 0; i < 128 && GLStateManager.glGetInteger(depthPname) > 1; i++) {
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void attribDepthTracksPushAndPop() {
|
||||
final int base = GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH);
|
||||
try {
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
assertEquals(base + 1, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH));
|
||||
GLStateManager.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT);
|
||||
assertEquals(base + 2, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH));
|
||||
GLStateManager.glPopAttrib();
|
||||
assertEquals(base + 1, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH));
|
||||
GLStateManager.glPopAttrib();
|
||||
assertEquals(base, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH));
|
||||
assertEquals(GLStateManager.MAX_ATTRIB_STACK_DEPTH, GLStateManager.glGetInteger(GL11.GL_MAX_ATTRIB_STACK_DEPTH));
|
||||
} finally {
|
||||
while (GLStateManager.getAttribDepth() > base) {
|
||||
GLStateManager.glPopAttrib();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void clientAttribDepthTracksPushAndPop() {
|
||||
try {
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH));
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
|
||||
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH));
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
assertEquals(2, GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH));
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH));
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH));
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH));
|
||||
|
||||
final int max = GLStateManager.glGetInteger(GL11.GL_MAX_CLIENT_ATTRIB_STACK_DEPTH);
|
||||
for (int i = 0; i < max + 2; i++) {
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
|
||||
}
|
||||
assertEquals(max, GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH));
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void textureStackDepthIsPerActiveUnit() {
|
||||
try {
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_TEXTURE_STACK_DEPTH));
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glPushMatrix();
|
||||
assertEquals(2, GLStateManager.glGetInteger(GL11.GL_TEXTURE_STACK_DEPTH));
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_TEXTURE_STACK_DEPTH));
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
assertEquals(2, GLStateManager.glGetInteger(GL11.GL_TEXTURE_STACK_DEPTH));
|
||||
GLStateManager.glPopMatrix();
|
||||
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_TEXTURE_STACK_DEPTH));
|
||||
} finally {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
popToBase(GL11.GL_TEXTURE_STACK_DEPTH);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void maxModelviewStackDepthIsExactlyReachable() {
|
||||
try {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_MODELVIEW_STACK_DEPTH));
|
||||
assertEquals(GLStateManager.glGetInteger(GL11.GL_MAX_MODELVIEW_STACK_DEPTH), pushToLimit(GL11.GL_MODELVIEW_STACK_DEPTH, GL11.GL_MAX_MODELVIEW_STACK_DEPTH));
|
||||
} finally {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
popToBase(GL11.GL_MODELVIEW_STACK_DEPTH);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void maxProjectionStackDepthIsExactlyReachable() {
|
||||
try {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_PROJECTION_STACK_DEPTH));
|
||||
assertEquals(GLStateManager.glGetInteger(GL11.GL_MAX_PROJECTION_STACK_DEPTH), pushToLimit(GL11.GL_PROJECTION_STACK_DEPTH, GL11.GL_MAX_PROJECTION_STACK_DEPTH));
|
||||
} finally {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
popToBase(GL11.GL_PROJECTION_STACK_DEPTH);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void maxTextureStackDepthIsExactlyReachable() {
|
||||
try {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_TEXTURE_STACK_DEPTH));
|
||||
assertEquals(GLStateManager.glGetInteger(GL11.GL_MAX_TEXTURE_STACK_DEPTH), pushToLimit(GL11.GL_TEXTURE_STACK_DEPTH, GL11.GL_MAX_TEXTURE_STACK_DEPTH));
|
||||
} finally {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
popToBase(GL11.GL_TEXTURE_STACK_DEPTH);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void constantQueriesReportEmulatedLimits() {
|
||||
assertEquals(Math.min(VertexKey.MAX_UNITS, GLStateManager.MAX_TEXTURE_UNITS), GLStateManager.glGetInteger(GL13.GL_MAX_TEXTURE_UNITS));
|
||||
assertEquals(8, GLStateManager.glGetInteger(GL11.GL_MAX_LIGHTS));
|
||||
}
|
||||
|
||||
@Test
|
||||
void stencilAndDepthBitsMatchDefaultFramebuffer() {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final int savedDraw = glCtx.drawFramebuffer;
|
||||
final int savedRead = glCtx.readFramebuffer;
|
||||
try {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
drainErrors();
|
||||
assertEquals(
|
||||
GL30.glGetFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL11.GL_STENCIL, GL30.GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE),
|
||||
GLStateManager.glGetInteger(GL11.GL_STENCIL_BITS));
|
||||
assertEquals(
|
||||
GL30.glGetFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL11.GL_DEPTH, GL30.GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE),
|
||||
GLStateManager.glGetInteger(GL11.GL_DEPTH_BITS));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, savedDraw);
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, savedRead);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void stencilAndDepthBitsFollowBoundDrawFramebuffer() {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final int savedDraw = glCtx.drawFramebuffer;
|
||||
final int savedRead = glCtx.readFramebuffer;
|
||||
final int fbo = GLStateManager.glGenFramebuffers();
|
||||
final int depthOnly = GLStateManager.glGenRenderbuffers();
|
||||
final int depthStencil = GLStateManager.glGenRenderbuffers();
|
||||
try {
|
||||
drainErrors();
|
||||
GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depthOnly);
|
||||
GLStateManager.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL14.GL_DEPTH_COMPONENT24, 16, 16);
|
||||
GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depthStencil);
|
||||
GLStateManager.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_DEPTH24_STENCIL8, 16, 16);
|
||||
GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, 0);
|
||||
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, fbo);
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL11.GL_STENCIL_BITS));
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL11.GL_DEPTH_BITS));
|
||||
|
||||
GLStateManager.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, GL30.GL_RENDERBUFFER, depthOnly);
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL11.GL_STENCIL_BITS));
|
||||
final int depthOnlyBits = GL30.glGetFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, GL30.GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE);
|
||||
assertTrue(depthOnlyBits >= 24, "driver depth size " + depthOnlyBits);
|
||||
assertEquals(depthOnlyBits, GLStateManager.glGetInteger(GL11.GL_DEPTH_BITS));
|
||||
|
||||
GLStateManager.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, GL30.GL_RENDERBUFFER, depthStencil);
|
||||
GLStateManager.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, depthStencil);
|
||||
assertEquals(8, GLStateManager.glGetInteger(GL11.GL_STENCIL_BITS));
|
||||
assertEquals(24, GLStateManager.glGetInteger(GL11.GL_DEPTH_BITS));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, savedDraw);
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, savedRead);
|
||||
GLStateManager.glDeleteFramebuffers(fbo);
|
||||
GLStateManager.glDeleteRenderbuffers(depthOnly);
|
||||
GLStateManager.glDeleteRenderbuffers(depthStencil);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void intBufferOverloadMatchesAndDriverIsNotAsked() {
|
||||
final int base = GLStateManager.getAttribDepth();
|
||||
final IntBuffer buf = BufferUtils.createIntBuffer(16);
|
||||
try {
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
drainErrors();
|
||||
GL11.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH);
|
||||
assumeTrue(GL11.glGetError() != GL11.GL_NO_ERROR, "driver accepts GL_ATTRIB_STACK_DEPTH, so a clean error state proves nothing");
|
||||
for (final int pname : SINGLE_VALUE_QUERIES) {
|
||||
final int direct = GLStateManager.glGetInteger(pname);
|
||||
buf.put(0, -12345);
|
||||
GLStateManager.glGetInteger(pname, buf);
|
||||
assertEquals(direct, buf.get(0), "IntBuffer overload for 0x" + Integer.toHexString(pname));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "driver error after querying 0x" + Integer.toHexString(pname));
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
drainClientAttribStack();
|
||||
while (GLStateManager.getAttribDepth() > base) {
|
||||
GLStateManager.glPopAttrib();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void attribDepthUnchangedWhileCompilingDisplayList() {
|
||||
final int base = GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH);
|
||||
final int list = GLStateManager.glGenLists(1);
|
||||
try {
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
assertEquals(base, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH));
|
||||
GLStateManager.glPopAttrib();
|
||||
assertEquals(base, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH));
|
||||
GLStateManager.glEndList();
|
||||
assertEquals(base, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH));
|
||||
} finally {
|
||||
if (DisplayListManager.isRecording()) {
|
||||
GLStateManager.glEndList();
|
||||
}
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void popAttribOnEmptyStackIsIgnored() {
|
||||
drainAttribStack();
|
||||
drainErrors();
|
||||
assertEquals(0, GLStateManager.getAttribDepth());
|
||||
assertDoesNotThrow(GLStateManager::glPopAttrib);
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH));
|
||||
assertDoesNotThrow(GLStateManager::glPopAttrib);
|
||||
assertEquals(0, GLStateManager.getAttribDepth());
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void pixelStoreBitRestoresPixelStoreAndPboBindings() {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final int unpackBuffer = GLStateManager.glGenBuffers();
|
||||
final int packBuffer = GLStateManager.glGenBuffers();
|
||||
try {
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 0);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
|
||||
final PixelStoreState unpackBefore = glCtx.pixelUnpackState;
|
||||
final PixelStoreState packBefore = glCtx.pixelPackState;
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 2);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 7);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, unpackBuffer);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, packBuffer);
|
||||
assertEquals(1, glCtx.pixelUnpackState.alignment());
|
||||
assertEquals(2, glCtx.pixelPackState.alignment());
|
||||
assertEquals(7, glCtx.pixelPackState.rowLength());
|
||||
assertEquals(2, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT));
|
||||
GLStateManager.glPopClientAttrib();
|
||||
|
||||
assertSame(unpackBefore, glCtx.pixelUnpackState);
|
||||
assertSame(packBefore, glCtx.pixelPackState);
|
||||
assertEquals(4, GLStateManager.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING));
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL21.GL_PIXEL_PACK_BUFFER_BINDING));
|
||||
assertEquals(4, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
|
||||
assertEquals(4, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT));
|
||||
assertEquals(0, GL11.glGetInteger(GL11.GL_PACK_ROW_LENGTH));
|
||||
assertEquals(0, GL11.glGetInteger(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING));
|
||||
assertEquals(0, GL11.glGetInteger(GL21.GL_PIXEL_PACK_BUFFER_BINDING));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 0);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
|
||||
GLStateManager.glDeleteBuffers(unpackBuffer);
|
||||
GLStateManager.glDeleteBuffers(packBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void pixelStoreIsNotRestoredWithoutPixelStoreBit() {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final int unpackBuffer = GLStateManager.glGenBuffers();
|
||||
final int packBuffer = GLStateManager.glGenBuffers();
|
||||
try {
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 2);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, unpackBuffer);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, packBuffer);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
|
||||
assertEquals(1, glCtx.pixelUnpackState.alignment());
|
||||
assertEquals(2, glCtx.pixelPackState.alignment());
|
||||
assertEquals(unpackBuffer, GLStateManager.glGetInteger(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING));
|
||||
assertEquals(packBuffer, GLStateManager.glGetInteger(GL21.GL_PIXEL_PACK_BUFFER_BINDING));
|
||||
assertEquals(1, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
|
||||
assertEquals(2, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT));
|
||||
assertEquals(unpackBuffer, GL11.glGetInteger(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING));
|
||||
assertEquals(packBuffer, GL11.glGetInteger(GL21.GL_PIXEL_PACK_BUFFER_BINDING));
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
|
||||
GLStateManager.glDeleteBuffers(unpackBuffer);
|
||||
GLStateManager.glDeleteBuffers(packBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void pixelStoreWithReturnsSameInstanceForUnchangedValue() {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
try {
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 9);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 11);
|
||||
final PixelStoreState unpack = glCtx.pixelUnpackState;
|
||||
final PixelStoreState pack = glCtx.pixelPackState;
|
||||
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 9);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, unpack.alignment());
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SWAP_BYTES, 0);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 11);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, pack.alignment());
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_LSB_FIRST, 0);
|
||||
|
||||
assertSame(unpack, glCtx.pixelUnpackState);
|
||||
assertSame(pack, glCtx.pixelPackState);
|
||||
assertSame(unpack, unpack.with(GL11.GL_UNPACK_ROW_LENGTH, 9));
|
||||
assertSame(pack, pack.with(GL11.GL_PACK_ROW_LENGTH, 11));
|
||||
} finally {
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void packAndUnpackRestoreToTheirOwnPriorValues() {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
try {
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 8);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 3);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 5);
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 6);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 7);
|
||||
assertEquals(1, glCtx.pixelUnpackState.alignment());
|
||||
assertEquals(4, glCtx.pixelPackState.alignment());
|
||||
assertEquals(6, glCtx.pixelUnpackState.skipRows());
|
||||
assertEquals(7, glCtx.pixelPackState.skipRows());
|
||||
assertEquals(6, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS));
|
||||
assertEquals(7, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS));
|
||||
GLStateManager.glPopClientAttrib();
|
||||
|
||||
assertEquals(2, glCtx.pixelUnpackState.alignment());
|
||||
assertEquals(8, glCtx.pixelPackState.alignment());
|
||||
assertEquals(3, glCtx.pixelUnpackState.skipRows());
|
||||
assertEquals(5, glCtx.pixelPackState.skipRows());
|
||||
assertEquals(2, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
|
||||
assertEquals(8, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT));
|
||||
assertEquals(3, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS));
|
||||
assertEquals(5, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0);
|
||||
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void vertexArrayEnableRestoredOnlyWithVertexArrayBit() {
|
||||
final int savedVao = GLStateManager.getBoundVAO();
|
||||
final int vao = GLStateManager.glGenVertexArrays();
|
||||
final IntBuffer enabled = BufferUtils.createIntBuffer(16);
|
||||
final int position = Usage.POSITION.getAttributeLocation();
|
||||
try {
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
drainErrors();
|
||||
assertFalse(positionArrayEnabled());
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
assertTrue(positionArrayEnabled());
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertFalse(positionArrayEnabled());
|
||||
GL20.glGetVertexAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED, enabled);
|
||||
assertEquals(GL11.GL_FALSE, enabled.get(0));
|
||||
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertTrue(positionArrayEnabled());
|
||||
GL20.glGetVertexAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED, enabled);
|
||||
assertEquals(GL11.GL_TRUE, enabled.get(0));
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertTrue(positionArrayEnabled());
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glBindVertexArray(savedVao);
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void vertexArrayStateRestoredOnlyWithVertexArrayBit() {
|
||||
final int savedVao = GLStateManager.getBoundVAO();
|
||||
final int vao = GLStateManager.glGenVertexArrays();
|
||||
try {
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
final int flagsBefore = GLStateManager.ctx().vaos.getVertexFlags();
|
||||
assertFalse(colorArrayEnabled());
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
|
||||
GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
assertTrue(colorArrayEnabled());
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertFalse(colorArrayEnabled());
|
||||
assertEquals(flagsBefore, GLStateManager.ctx().vaos.getVertexFlags());
|
||||
assertEquals(0, GLStateManager.getClientActiveTextureUnit());
|
||||
|
||||
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
|
||||
GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.glPopClientAttrib();
|
||||
assertTrue(colorArrayEnabled());
|
||||
assertEquals(1, GLStateManager.getClientActiveTextureUnit());
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY);
|
||||
GLStateManager.glBindVertexArray(savedVao);
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void intLightGettersConvertLikeMesa() {
|
||||
final FloatBuffer savedDiffuse = BufferUtils.createFloatBuffer(16);
|
||||
final FloatBuffer savedPosition = BufferUtils.createFloatBuffer(16);
|
||||
final FloatBuffer savedDirection = BufferUtils.createFloatBuffer(16);
|
||||
final FloatBuffer savedCutoff = BufferUtils.createFloatBuffer(16);
|
||||
final FloatBuffer savedAttenuation = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_DIFFUSE, savedDiffuse);
|
||||
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_POSITION, savedPosition);
|
||||
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_SPOT_DIRECTION, savedDirection);
|
||||
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_SPOT_CUTOFF, savedCutoff);
|
||||
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_LINEAR_ATTENUATION, savedAttenuation);
|
||||
final IntBuffer ints = BufferUtils.createIntBuffer(16);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glPushMatrix();
|
||||
try {
|
||||
GLStateManager.glLoadIdentity();
|
||||
final FloatBuffer values = BufferUtils.createFloatBuffer(4);
|
||||
values.put(0, 0.25f).put(1, 0.5f).put(2, 1.0f).put(3, -0.5f);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT1, GL11.GL_DIFFUSE, values);
|
||||
values.put(0, 3.9f).put(1, -2.7f).put(2, 0.5f).put(3, 1.0f);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT1, GL11.GL_POSITION, values);
|
||||
values.put(0, 2.5f).put(1, -1.5f).put(2, -3.9f).put(3, 0.0f);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT1, GL11.GL_SPOT_DIRECTION, values);
|
||||
GLStateManager.glLightf(GL11.GL_LIGHT1, GL11.GL_SPOT_CUTOFF, 45.75f);
|
||||
GLStateManager.glLightf(GL11.GL_LIGHT1, GL11.GL_LINEAR_ATTENUATION, 2.5f);
|
||||
drainErrors();
|
||||
|
||||
ints.position(2);
|
||||
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_DIFFUSE, ints);
|
||||
assertEquals(2, ints.position());
|
||||
assertEquals(536870911, ints.get(2));
|
||||
assertEquals(1073741823, ints.get(3));
|
||||
assertEquals(2147483647, ints.get(4));
|
||||
assertEquals(-1073741823, ints.get(5));
|
||||
ints.position(0);
|
||||
|
||||
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_POSITION, ints);
|
||||
assertEquals(3, ints.get(0));
|
||||
assertEquals(-2, ints.get(1));
|
||||
assertEquals(0, ints.get(2));
|
||||
assertEquals(1, ints.get(3));
|
||||
|
||||
ints.put(3, -777);
|
||||
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_SPOT_DIRECTION, ints);
|
||||
assertEquals(2, ints.get(0));
|
||||
assertEquals(-1, ints.get(1));
|
||||
assertEquals(-3, ints.get(2));
|
||||
assertEquals(-777, ints.get(3));
|
||||
|
||||
ints.put(1, -777);
|
||||
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_SPOT_CUTOFF, ints);
|
||||
assertEquals(45, ints.get(0));
|
||||
assertEquals(-777, ints.get(1));
|
||||
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_LINEAR_ATTENUATION, ints);
|
||||
assertEquals(2, ints.get(0));
|
||||
|
||||
ints.put(0, -777);
|
||||
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_EMISSION, ints);
|
||||
assertEquals(-777, ints.get(0));
|
||||
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_SHININESS, ints);
|
||||
assertEquals(-777, ints.get(0));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
GLStateManager.glLoadIdentity();
|
||||
savedDiffuse.limit(4);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT1, GL11.GL_DIFFUSE, savedDiffuse);
|
||||
savedPosition.limit(4);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT1, GL11.GL_POSITION, savedPosition);
|
||||
savedDirection.limit(4);
|
||||
GLStateManager.glLight(GL11.GL_LIGHT1, GL11.GL_SPOT_DIRECTION, savedDirection);
|
||||
GLStateManager.glLightf(GL11.GL_LIGHT1, GL11.GL_SPOT_CUTOFF, savedCutoff.get(0));
|
||||
GLStateManager.glLightf(GL11.GL_LIGHT1, GL11.GL_LINEAR_ATTENUATION, savedAttenuation.get(0));
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void intMaterialGettersConvertLikeMesa() {
|
||||
final FloatBuffer savedSpecular = BufferUtils.createFloatBuffer(16);
|
||||
final FloatBuffer savedShininess = BufferUtils.createFloatBuffer(16);
|
||||
final FloatBuffer savedIndexes = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, savedSpecular);
|
||||
GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SHININESS, savedShininess);
|
||||
GLStateManager.glGetMaterial(GL11.GL_BACK, GL11.GL_COLOR_INDEXES, savedIndexes);
|
||||
final IntBuffer ints = BufferUtils.createIntBuffer(16);
|
||||
try {
|
||||
final FloatBuffer values = BufferUtils.createFloatBuffer(4);
|
||||
values.put(0, 0.5f).put(1, 0.75f).put(2, 1.0f).put(3, 0.0f);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, values);
|
||||
GLStateManager.glMaterialf(GL11.GL_FRONT, GL11.GL_SHININESS, 12.6f);
|
||||
values.put(0, 2.5f).put(1, -2.5f).put(2, 7.4f).put(3, 0.0f);
|
||||
GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_COLOR_INDEXES, values);
|
||||
drainErrors();
|
||||
|
||||
ints.position(1);
|
||||
GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, ints);
|
||||
assertEquals(1, ints.position());
|
||||
assertEquals(1073741823, ints.get(1));
|
||||
assertEquals(1610612735, ints.get(2));
|
||||
assertEquals(2147483647, ints.get(3));
|
||||
assertEquals(0, ints.get(4));
|
||||
ints.position(0);
|
||||
|
||||
ints.put(1, -777);
|
||||
GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SHININESS, ints);
|
||||
assertEquals(13, ints.get(0));
|
||||
assertEquals(-777, ints.get(1));
|
||||
|
||||
ints.put(3, -777);
|
||||
GLStateManager.glGetMaterial(GL11.GL_BACK, GL11.GL_COLOR_INDEXES, ints);
|
||||
assertEquals(3, ints.get(0));
|
||||
assertEquals(-3, ints.get(1));
|
||||
assertEquals(7, ints.get(2));
|
||||
assertEquals(-777, ints.get(3));
|
||||
|
||||
ints.put(0, -777);
|
||||
GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_POSITION, ints);
|
||||
assertEquals(-777, ints.get(0));
|
||||
GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SPOT_CUTOFF, ints);
|
||||
assertEquals(-777, ints.get(0));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
savedSpecular.limit(4);
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, savedSpecular);
|
||||
GLStateManager.glMaterialf(GL11.GL_FRONT, GL11.GL_SHININESS, savedShininess.get(0));
|
||||
savedIndexes.limit(4);
|
||||
GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_COLOR_INDEXES, savedIndexes);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void currentTextureCoordsFollowActiveUnit() {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(16);
|
||||
final float[][] saved = new float[4][4];
|
||||
try {
|
||||
for (int unit = 0; unit < 4; unit++) {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
GLStateManager.glGetFloat(GL11.GL_CURRENT_TEXTURE_COORDS, buf);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
saved[unit][i] = buf.get(i);
|
||||
}
|
||||
}
|
||||
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0, 0.25f, 0.5f);
|
||||
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE1, 240.0f, 16.0f);
|
||||
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE2, 0.75f, 0.125f);
|
||||
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE3, 0.375f, 0.625f);
|
||||
drainErrors();
|
||||
|
||||
final float[][] expected = { { 0.25f, 0.5f }, { 240.0f, 16.0f }, { 0.75f, 0.125f }, { 0.375f, 0.625f } };
|
||||
for (int unit = 0; unit < 4; unit++) {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
GLStateManager.glGetFloat(GL11.GL_CURRENT_TEXTURE_COORDS, buf);
|
||||
assertEquals(expected[unit][0], buf.get(0), "s of unit " + unit);
|
||||
assertEquals(expected[unit][1], buf.get(1), "t of unit " + unit);
|
||||
assertEquals(0.0f, buf.get(2), "r of unit " + unit);
|
||||
assertEquals(1.0f, buf.get(3), "q of unit " + unit);
|
||||
}
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
for (int unit = 0; unit < 4; unit++) {
|
||||
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0 + unit, saved[unit][0], saved[unit][1]);
|
||||
}
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void currentTextureCoordsOfUntrackedUnitAreInitialValue() {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(16);
|
||||
final int unit = GLStateManager.ctx().ffp.currentTexCoords.length + 1;
|
||||
try {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0, 0.25f, 0.5f);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
drainErrors();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
buf.put(i, -7.0f);
|
||||
}
|
||||
GLStateManager.glGetFloat(GL11.GL_CURRENT_TEXTURE_COORDS, buf);
|
||||
assertEquals(0.0f, buf.get(0));
|
||||
assertEquals(0.0f, buf.get(1));
|
||||
assertEquals(0.0f, buf.get(2));
|
||||
assertEquals(1.0f, buf.get(3));
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.TestThreads;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.function.Executable;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.SharedDrawable;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_OffContextCreation_GLTest {
|
||||
|
||||
private SharedDrawable sharedDrawable;
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
if (sharedDrawable != null) {
|
||||
DrawableContexts.destroy(sharedDrawable);
|
||||
sharedDrawable = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Executable> creationCalls() {
|
||||
final Map<String, Executable> calls = new LinkedHashMap<>();
|
||||
calls.put("glGenTextures()", GLStateManager::glGenTextures);
|
||||
calls.put("glGenTextures(IntBuffer)", () -> GLStateManager.glGenTextures(BufferUtils.createIntBuffer(1)));
|
||||
calls.put("glGenSamplers()", GLStateManager::glGenSamplers);
|
||||
calls.put("glGenSamplers(IntBuffer)", () -> GLStateManager.glGenSamplers(BufferUtils.createIntBuffer(1)));
|
||||
calls.put("glGenSamplers(int[])", () -> GLStateManager.glGenSamplers(new int[1]));
|
||||
calls.put("glGenBuffers()", GLStateManager::glGenBuffers);
|
||||
calls.put("glGenBuffers(IntBuffer)", () -> GLStateManager.glGenBuffers(BufferUtils.createIntBuffer(1)));
|
||||
calls.put("glGenBuffers(int[])", () -> GLStateManager.glGenBuffers(new int[1]));
|
||||
calls.put("glCreateBuffers()", GLStateManager::glCreateBuffers);
|
||||
calls.put("glCreateBuffers(IntBuffer)", () -> GLStateManager.glCreateBuffers(BufferUtils.createIntBuffer(1)));
|
||||
calls.put("glCreateBuffers(int[])", () -> GLStateManager.glCreateBuffers(new int[1]));
|
||||
calls.put("glGenVertexArrays()", GLStateManager::glGenVertexArrays);
|
||||
calls.put("glGenFramebuffers()", GLStateManager::glGenFramebuffers);
|
||||
calls.put("glGenRenderbuffers()", GLStateManager::glGenRenderbuffers);
|
||||
calls.put("glGenQueries()", GLStateManager::glGenQueries);
|
||||
calls.put("glGenQueries(IntBuffer)", () -> GLStateManager.glGenQueries(BufferUtils.createIntBuffer(1)));
|
||||
calls.put("glCreateShader", () -> GLStateManager.glCreateShader(GL20.GL_VERTEX_SHADER));
|
||||
calls.put("glCreateProgram", GLStateManager::glCreateProgram);
|
||||
calls.put("RenderSystem.createTexture", () -> RenderSystem.createTexture(GL11.GL_TEXTURE_2D));
|
||||
calls.put("RenderSystem.createFramebuffer", RenderSystem::createFramebuffer);
|
||||
return calls;
|
||||
}
|
||||
|
||||
@Test
|
||||
void creationWithoutContextThrows() throws InterruptedException {
|
||||
TestThreads.run("glsm-no-context", () -> {
|
||||
for (Map.Entry<String, Executable> call : creationCalls().entrySet()) {
|
||||
assertThrows(IllegalStateException.class, call.getValue(), call.getKey() + " did not throw off-context");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void creationOnSharedContextThreadPasses() throws Exception {
|
||||
sharedDrawable = new SharedDrawable(Display.getDrawable());
|
||||
DrawableContexts.run(sharedDrawable, "glsm-shared-context", () -> {
|
||||
final int texture = GLStateManager.glGenTextures();
|
||||
assertNotEquals(0, texture, "no texture name on a thread that owns a shared context");
|
||||
GLStateManager.glDeleteTextures(texture);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.RetainedState;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@GLCoreTest
|
||||
public class GLSM_RetainedState_GLTest {
|
||||
|
||||
private static final StateSet ALL = StateSet.forMask(GL11.GL_ALL_ATTRIB_BITS);
|
||||
|
||||
private static int driverTextureBinding(int unit) {
|
||||
final int saved = GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE);
|
||||
GL13.glActiveTexture(GL13.GL_TEXTURE0 + unit);
|
||||
final int binding = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
GL13.glActiveTexture(saved);
|
||||
return binding;
|
||||
}
|
||||
|
||||
private static void baseline() {
|
||||
GLStateManager.enableCull();
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.glDepthMask(true);
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1F);
|
||||
GLStateManager.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
private static void mutate(int texture) {
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ONE);
|
||||
GLStateManager.glDepthMask(false);
|
||||
GLStateManager.glDepthFunc(GL11.GL_ALWAYS);
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GEQUAL, 0.5F);
|
||||
GLStateManager.glColor4f(0.25F, 0.5F, 0.75F, 0.5F);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
}
|
||||
|
||||
private static void assertBaseline() {
|
||||
assertTrue(GLStateManager.glIsEnabled(GL11.GL_CULL_FACE));
|
||||
assertTrue(GL11.glIsEnabled(GL11.GL_CULL_FACE));
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_BLEND));
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_BLEND));
|
||||
assertEquals(GL11.GL_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB));
|
||||
assertEquals(GL11.GL_ZERO, GL11.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
|
||||
assertTrue(GL11.glGetBoolean(GL11.GL_DEPTH_WRITEMASK));
|
||||
assertEquals(GL11.GL_LEQUAL, GL11.glGetInteger(GL11.GL_DEPTH_FUNC));
|
||||
assertEquals(GL11.GL_GREATER, GLStateManager.glGetInteger(GL11.GL_ALPHA_TEST_FUNC));
|
||||
assertEquals(1.0F, GLStateManager.getColor().getRed(), 0.0F);
|
||||
assertEquals(0, driverTextureBinding(0));
|
||||
}
|
||||
|
||||
private static void assertMutated(int texture) {
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_CULL_FACE));
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_CULL_FACE));
|
||||
assertTrue(GLStateManager.glIsEnabled(GL11.GL_BLEND));
|
||||
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND));
|
||||
assertEquals(GL11.GL_DST_COLOR, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB));
|
||||
assertEquals(GL11.GL_ZERO, GL11.glGetInteger(GL14.GL_BLEND_DST_RGB));
|
||||
assertEquals(GL11.GL_ONE, GL11.glGetInteger(GL14.GL_BLEND_SRC_ALPHA));
|
||||
assertEquals(GL11.GL_ONE, GL11.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
|
||||
assertEquals(GL11.GL_DST_COLOR, GLStateManager.glGetInteger(GL14.GL_BLEND_SRC_RGB));
|
||||
assertFalse(GL11.glGetBoolean(GL11.GL_DEPTH_WRITEMASK));
|
||||
assertEquals(GL11.GL_ALWAYS, GL11.glGetInteger(GL11.GL_DEPTH_FUNC));
|
||||
assertEquals(GL11.GL_GEQUAL, GLStateManager.glGetInteger(GL11.GL_ALPHA_TEST_FUNC));
|
||||
assertEquals(0.25F, GLStateManager.getColor().getRed(), 0.0F);
|
||||
assertEquals(0.5F, GLStateManager.getColor().getAlpha(), 0.0F);
|
||||
assertEquals(texture, driverTextureBinding(0));
|
||||
assertEquals(texture, GLStateManager.getBoundTextureForServerState(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void retainedDeltaReappliesAfterPop() {
|
||||
final int texture = GL11.glGenTextures();
|
||||
final RetainedState retained = new RetainedState();
|
||||
try {
|
||||
baseline();
|
||||
final int d = GLStateManager.pushState(ALL);
|
||||
mutate(texture);
|
||||
GLStateManager.retainModifiedState(d, retained);
|
||||
GLStateManager.popStateTo(d);
|
||||
assertBaseline();
|
||||
|
||||
GLStateManager.applyRetainedState(retained);
|
||||
assertMutated(texture);
|
||||
|
||||
baseline();
|
||||
assertBaseline();
|
||||
GLStateManager.applyRetainedState(retained);
|
||||
assertMutated(texture);
|
||||
} finally {
|
||||
baseline();
|
||||
GL11.glDeleteTextures(texture);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void appliedDeltaIsRestoredByAnEnclosingPush() {
|
||||
final int texture = GL11.glGenTextures();
|
||||
final RetainedState retained = new RetainedState();
|
||||
try {
|
||||
baseline();
|
||||
final int d = GLStateManager.pushState(ALL);
|
||||
mutate(texture);
|
||||
GLStateManager.retainModifiedState(d, retained);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
final int outer = GLStateManager.pushState(ALL);
|
||||
GLStateManager.applyRetainedState(retained);
|
||||
assertMutated(texture);
|
||||
GLStateManager.popStateTo(outer);
|
||||
assertBaseline();
|
||||
} finally {
|
||||
baseline();
|
||||
GL11.glDeleteTextures(texture);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void statesRestoredByANestedPushAreNotRetained() {
|
||||
final RetainedState retained = new RetainedState();
|
||||
try {
|
||||
baseline();
|
||||
final int d = GLStateManager.pushState(ALL);
|
||||
GLStateManager.enableBlend();
|
||||
final int nested = GLStateManager.pushState(ALL);
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.popStateTo(nested);
|
||||
GLStateManager.retainModifiedState(d, retained);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
GLStateManager.applyRetainedState(retained);
|
||||
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND));
|
||||
assertTrue(GL11.glIsEnabled(GL11.GL_CULL_FACE));
|
||||
assertTrue(GLStateManager.glIsEnabled(GL11.GL_CULL_FACE));
|
||||
} finally {
|
||||
baseline();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void onlyDifferingStateIsReissued() {
|
||||
final int texture = GL11.glGenTextures();
|
||||
final RetainedState retained = new RetainedState();
|
||||
try {
|
||||
baseline();
|
||||
final int d = GLStateManager.pushState(ALL);
|
||||
mutate(texture);
|
||||
GLStateManager.retainModifiedState(d, retained);
|
||||
GLStateManager.popStateTo(d);
|
||||
GLStateManager.applyRetainedState(retained);
|
||||
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final long backendCalls = GLStateManager.attribBackendCalls;
|
||||
final int colorGen = glCtx.colorGeneration;
|
||||
final int fragmentGen = glCtx.fragmentGeneration;
|
||||
final int batchGen = glCtx.batchStateGeneration;
|
||||
GLStateManager.applyRetainedState(retained);
|
||||
assertEquals(backendCalls, GLStateManager.attribBackendCalls);
|
||||
assertEquals(colorGen, glCtx.colorGeneration);
|
||||
assertEquals(fragmentGen, glCtx.fragmentGeneration);
|
||||
assertEquals(batchGen, glCtx.batchStateGeneration);
|
||||
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
GLStateManager.applyRetainedState(retained);
|
||||
assertEquals(backendCalls + 3, GLStateManager.attribBackendCalls);
|
||||
assertMutated(texture);
|
||||
} finally {
|
||||
baseline();
|
||||
GL11.glDeleteTextures(texture);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void aSecondCaptureInvalidatesTheFirst() {
|
||||
final RetainedState first = new RetainedState();
|
||||
final RetainedState second = new RetainedState();
|
||||
try {
|
||||
baseline();
|
||||
int d = GLStateManager.pushState(ALL);
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.retainModifiedState(d, first);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
d = GLStateManager.pushState(ALL);
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.retainModifiedState(d, second);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
GLStateManager.applyRetainedState(first);
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_BLEND));
|
||||
assertTrue(GL11.glIsEnabled(GL11.GL_CULL_FACE));
|
||||
GLStateManager.applyRetainedState(second);
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_BLEND));
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_CULL_FACE));
|
||||
} finally {
|
||||
baseline();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void writesInAnOuterPushAreNotRetained() {
|
||||
final RetainedState retained = new RetainedState();
|
||||
try {
|
||||
baseline();
|
||||
GLStateManager.glClearColor(0.25F, 0.5F, 0.75F, 1.0F);
|
||||
final int outer = GLStateManager.pushState(ALL);
|
||||
GLStateManager.glClearColor(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
final int inner = GLStateManager.pushState(ALL);
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.retainModifiedState(inner, retained);
|
||||
GLStateManager.popStateTo(outer);
|
||||
|
||||
GLStateManager.applyRetainedState(retained);
|
||||
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND));
|
||||
assertEquals(0.25F, GLStateManager.getClearColor().getRed(), 0.0F);
|
||||
assertEquals(1.0F, GLStateManager.getClearColor().getAlpha(), 0.0F);
|
||||
} finally {
|
||||
baseline();
|
||||
GLStateManager.glClearColor(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void aDeletedProgramIsNotRebound() {
|
||||
final RetainedState retained = new RetainedState();
|
||||
final int program = GLSM_StateSet_GLTest.linkTrivialProgram();
|
||||
try {
|
||||
baseline();
|
||||
final int d = GLStateManager.pushState(ALL);
|
||||
GLStateManager.glUseProgram(program);
|
||||
GLStateManager.retainModifiedState(d, retained);
|
||||
GLStateManager.popStateTo(d);
|
||||
GLStateManager.glUseProgram(0);
|
||||
GLStateManager.glDeleteProgram(program);
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
|
||||
GLStateManager.applyRetainedState(retained);
|
||||
assertEquals(0, GLStateManager.getActiveProgram());
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
GLStateManager.glUseProgram(0);
|
||||
baseline();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ public class GLSM_StateSet_GLTest {
|
||||
private static final String TRIVIAL_VS = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }\n";
|
||||
private static final String TRIVIAL_FS = "#version 330 core\nout vec4 o;\nvoid main(){ o = vec4(1.0); }\n";
|
||||
|
||||
private static int linkTrivialProgram() {
|
||||
static int linkTrivialProgram() {
|
||||
final int v = compileShader(GL20.GL_VERTEX_SHADER, TRIVIAL_VS);
|
||||
final int f = compileShader(GL20.GL_FRAGMENT_SHADER, TRIVIAL_FS);
|
||||
final int p = GL20.glCreateProgram();
|
||||
@@ -89,33 +89,41 @@ public class GLSM_StateSet_GLTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void fontRestoresDepthAxisAndShadeModelButNotCull() {
|
||||
void fontRestoresDepthAxisShadeModelAndPolygonOffsetButNotStencil() {
|
||||
try {
|
||||
GLStateManager.enableDepthTest();
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
GLStateManager.glShadeModel(GL11.GL_SMOOTH);
|
||||
GLStateManager.enableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
GLStateManager.glPolygonOffset(0.0f, 0.0f);
|
||||
GLStateManager.glEnable(GL11.GL_STENCIL_TEST);
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.FONT);
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.glDepthFunc(GL11.GL_ALWAYS);
|
||||
GLStateManager.glShadeModel(GL11.GL_FLAT);
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
GLStateManager.glEnable(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
GLStateManager.glPolygonOffset(-10.0f, -10.0f);
|
||||
GLStateManager.glDisable(GL11.GL_STENCIL_TEST);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
verifyIsEnabled(GL11.GL_DEPTH_TEST, true, "Depth test enable - restored by FONT pop");
|
||||
assertEquals(GL11.GL_LEQUAL, GLStateManager.getDepthState().getFunc(), "cache depth func");
|
||||
assertEquals(GL11.GL_LEQUAL, GL11.glGetInteger(GL11.GL_DEPTH_FUNC), "driver depth func");
|
||||
assertEquals(GL11.GL_SMOOTH, GLStateManager.getShadeModelState().getValue(), "cache shade model");
|
||||
assertEquals(GL11.GL_FRONT, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode - not a FONT member, must stay toggled");
|
||||
assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode - not a FONT member, must stay toggled");
|
||||
verifyIsEnabled(GL11.GL_POLYGON_OFFSET_FILL, false, "Polygon offset fill - restored by FONT pop");
|
||||
assertEquals(0.0f, GLStateManager.getPolygonState().getOffsetFactor(), "cache polygon offset factor");
|
||||
assertEquals(0.0f, GL11.glGetFloat(GL11.GL_POLYGON_OFFSET_FACTOR), "driver polygon offset factor");
|
||||
assertEquals(0.0f, GLStateManager.getPolygonState().getOffsetUnits(), "cache polygon offset units");
|
||||
assertEquals(0.0f, GL11.glGetFloat(GL11.GL_POLYGON_OFFSET_UNITS), "driver polygon offset units");
|
||||
verifyIsEnabled(GL11.GL_STENCIL_TEST, false, "Stencil test - not a FONT member, must stay toggled");
|
||||
} finally {
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.glDepthFunc(GL11.GL_LESS);
|
||||
GLStateManager.glShadeModel(GL11.GL_SMOOTH);
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
GLStateManager.glPolygonOffset(0.0f, 0.0f);
|
||||
GLStateManager.glDisable(GL11.GL_STENCIL_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class BackendStartGuardTest {
|
||||
|
||||
private static File marker(Path dir) {
|
||||
return dir.resolve("angelica").resolve("sdlgpu-starting").toFile();
|
||||
}
|
||||
|
||||
@Test
|
||||
void firstStartCreatesTheMarkerAndFinishRemovesIt(@TempDir Path dir) {
|
||||
final File marker = marker(dir);
|
||||
assertFalse(BackendStartGuard.begin(marker, true, false));
|
||||
assertFalse(BackendStartGuard.tripped());
|
||||
assertTrue(marker.isFile());
|
||||
BackendStartGuard.finish();
|
||||
assertFalse(marker.exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
void leftoverMarkerTripsOnceAndIsRemoved(@TempDir Path dir) {
|
||||
final File marker = marker(dir);
|
||||
assertFalse(BackendStartGuard.begin(marker, true, false));
|
||||
assertTrue(BackendStartGuard.begin(marker, true, false));
|
||||
assertTrue(BackendStartGuard.tripped());
|
||||
assertFalse(marker.exists());
|
||||
BackendStartGuard.finish();
|
||||
assertFalse(marker.exists());
|
||||
assertFalse(BackendStartGuard.begin(marker, true, false));
|
||||
assertFalse(BackendStartGuard.tripped());
|
||||
BackendStartGuard.finish();
|
||||
}
|
||||
|
||||
@Test
|
||||
void openGlConfigWritesNoMarkerAndClearsAStaleOne(@TempDir Path dir) throws IOException {
|
||||
final File marker = marker(dir);
|
||||
assertFalse(BackendStartGuard.begin(marker, false, false));
|
||||
assertFalse(marker.exists());
|
||||
assertTrue(marker.getParentFile().mkdirs());
|
||||
assertTrue(marker.createNewFile());
|
||||
assertFalse(BackendStartGuard.begin(marker, false, false));
|
||||
assertFalse(BackendStartGuard.tripped());
|
||||
assertFalse(marker.exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
void forcedFlagNeverTripsOrWrites(@TempDir Path dir) throws IOException {
|
||||
final File marker = marker(dir);
|
||||
assertFalse(BackendStartGuard.begin(marker, true, true));
|
||||
assertFalse(marker.exists());
|
||||
assertTrue(marker.getParentFile().mkdirs());
|
||||
assertTrue(marker.createNewFile());
|
||||
assertFalse(BackendStartGuard.begin(marker, true, true));
|
||||
assertFalse(BackendStartGuard.tripped());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class MoltenVKTest {
|
||||
|
||||
private static final String PRESENT_RESOURCE = "/com/gtnewhorizons/angelica/glsm/backend/MoltenVK.class";
|
||||
private static final String ABSENT_RESOURCE = "/macos/arm64/org/lwjgl/vulkan/absent.dylib";
|
||||
|
||||
@Test
|
||||
void detectOrderIsEnvThenJarThenSystem(@TempDir Path dir) throws IOException {
|
||||
final String[] system = { Files.createFile(dir.resolve("system.dylib")).toString() };
|
||||
final String[] none = { dir.resolve("missing.dylib").toString() };
|
||||
assertEquals(MoltenVK.Source.NONE, MoltenVK.detect(false, "/env/lib.dylib", true, system));
|
||||
assertEquals(MoltenVK.Source.ENV, MoltenVK.detect(true, "/env/lib.dylib", true, system));
|
||||
assertEquals(MoltenVK.Source.JAR, MoltenVK.detect(true, "", true, system));
|
||||
assertEquals(MoltenVK.Source.SYSTEM, MoltenVK.detect(true, null, false, system));
|
||||
assertEquals(MoltenVK.Source.NONE, MoltenVK.detect(true, null, false, none));
|
||||
assertEquals(1, dir.toFile().list().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
void jarIsExtractedOnceAndNotRewritten(@TempDir Path dir) throws IOException {
|
||||
final File out = dir.resolve("natives").toFile();
|
||||
final String first = MoltenVK.resolve(MoltenVK.Source.JAR, null, PRESENT_RESOURCE, out, "arm64", new String[0]);
|
||||
final File extracted = new File(out, "libMoltenVK-arm64.dylib");
|
||||
assertEquals(extracted.getAbsolutePath(), first);
|
||||
assertTrue(extracted.length() > 0);
|
||||
assertTrue(extracted.setLastModified(1_000_000L));
|
||||
assertEquals(first, MoltenVK.resolve(MoltenVK.Source.JAR, null, PRESENT_RESOURCE, out, "arm64", new String[0]));
|
||||
assertEquals(1_000_000L, extracted.lastModified());
|
||||
assertEquals(1, out.list().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
void changedFileIsRewritten(@TempDir Path dir) throws IOException {
|
||||
final File extracted = new File(dir.toFile(), "libMoltenVK-arm64.dylib");
|
||||
Files.write(extracted.toPath(), new byte[] { 1, 2, 3 });
|
||||
MoltenVK.resolve(MoltenVK.Source.JAR, null, PRESENT_RESOURCE, dir.toFile(), "arm64", new String[0]);
|
||||
assertTrue(extracted.length() > 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void failedExtractionFallsBackToTheSystemInstall(@TempDir Path dir) throws IOException {
|
||||
final Path system = Files.createFile(dir.resolve("system.dylib"));
|
||||
final File notADirectory = Files.createFile(dir.resolve("blocker")).toFile();
|
||||
assertEquals(system.toString(), MoltenVK.resolve(MoltenVK.Source.JAR, null, PRESENT_RESOURCE, notADirectory, "arm64", new String[] { system.toString() }));
|
||||
assertNull(MoltenVK.resolve(MoltenVK.Source.JAR, null, ABSENT_RESOURCE, dir.toFile(), "arm64", new String[0]));
|
||||
}
|
||||
|
||||
@Test
|
||||
void envAndSystemAndNoneResolveWithoutExtracting(@TempDir Path dir) throws IOException {
|
||||
final Path system = Files.createFile(dir.resolve("system.dylib"));
|
||||
final File out = dir.resolve("natives").toFile();
|
||||
assertEquals("/env/lib.dylib", MoltenVK.resolve(MoltenVK.Source.ENV, "/env/lib.dylib", PRESENT_RESOURCE, out, "arm64", new String[0]));
|
||||
assertEquals(system.toString(), MoltenVK.resolve(MoltenVK.Source.SYSTEM, null, PRESENT_RESOURCE, out, "arm64", new String[] { system.toString() }));
|
||||
assertNull(MoltenVK.resolve(MoltenVK.Source.NONE, null, PRESENT_RESOURCE, out, "arm64", new String[] { system.toString() }));
|
||||
assertFalse(out.exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
void archNames() {
|
||||
assertEquals("arm64", MoltenVK.archName("aarch64"));
|
||||
assertEquals("x64", MoltenVK.archName("x86_64"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
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.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
@GLCoreTest
|
||||
class WideLineClipGLTest {
|
||||
|
||||
private static final int SIZE = 64;
|
||||
private static final double NEAR = 0.05;
|
||||
private static final float LINE_WIDTH = 3.0f;
|
||||
private static final int BACKGROUND = 0xFF000000;
|
||||
|
||||
private static final float[] IN_FRONT = { 0f, -1f, -2f };
|
||||
private static final float[] BEHIND = { 0f, 0.8f, 2f };
|
||||
|
||||
private boolean savedEmulation;
|
||||
private int vao;
|
||||
private int vbo;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
assumeTrue(GLStateManager.supportsGeometryShaders());
|
||||
savedEmulation = GLStateManager.wideLineEmulationEnabled;
|
||||
GLStateManager.wideLineEmulationEnabled = true;
|
||||
|
||||
FfpFixture.resetFfpState();
|
||||
ShaderManager.enable();
|
||||
ShaderManager.getInstance().activate();
|
||||
GLStateManager.glViewport(0, 0, SIZE, SIZE);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glFrustum(-NEAR, NEAR, -NEAR, NEAR, NEAR, 100.0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GLStateManager.glLineWidth(LINE_WIDTH);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
GLStateManager.wideLineEmulationEnabled = savedEmulation;
|
||||
GLStateManager.glLineWidth(1.0f);
|
||||
if (vbo != 0) { GLStateManager.glDeleteBuffers(vbo); vbo = 0; }
|
||||
if (vao != 0) { GLStateManager.glDeleteVertexArrays(vao); vao = 0; }
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
FfpFixture.resetFfpState();
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
void lineEndingBehindTheCameraKeepsItsWidth() {
|
||||
assertConstantWidth(IN_FRONT, BEHIND);
|
||||
}
|
||||
|
||||
@Test
|
||||
void lineStartingBehindTheCameraKeepsItsWidth() {
|
||||
assertConstantWidth(BEHIND, IN_FRONT);
|
||||
}
|
||||
|
||||
@Test
|
||||
void lineFullyBehindTheCameraDrawsNothing() {
|
||||
final int[] pixels = drawLine(new float[] { 0f, -1f, 2f }, BEHIND);
|
||||
for (int px : pixels) {
|
||||
assertEquals(BACKGROUND, px);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertConstantWidth(float[] start, float[] end) {
|
||||
final int[] pixels = drawLine(start, end);
|
||||
for (int row = 0; row < SIZE / 4; row++) {
|
||||
int lit = 0;
|
||||
for (int x = 0; x < SIZE; x++) {
|
||||
if (pixels[row * SIZE + x] != BACKGROUND) lit++;
|
||||
}
|
||||
final int y = row, count = lit;
|
||||
assertTrue(count > 0 && count <= LINE_WIDTH + 1, () -> "row " + y + " is " + count + " pixels wide");
|
||||
}
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
private int[] drawLine(float[] start, float[] end) {
|
||||
vao = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
vbo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
final FloatBuffer positions = BufferUtils.createFloatBuffer(6);
|
||||
positions.put(start).put(end).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);
|
||||
VAOManager.setCurrentVertexFlags(0);
|
||||
|
||||
FfpFixture.clear();
|
||||
GLStateManager.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
GLStateManager.glDrawArrays(GL11.GL_LINES, 0, 2);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
return FfpFixture.readRegion(SIZE);
|
||||
}
|
||||
}
|
||||
@@ -252,6 +252,32 @@ class GLSMRedirectorTest {
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void arbGenerateMipmapAliasIsRedirected() {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/ARBFramebufferObject", "glGenerateMipmap", "(I)V");
|
||||
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
assertTrue(changed);
|
||||
assertEquals(GLSM, firstCall(cn).owner);
|
||||
assertEquals("glGenerateMipmap", firstCall(cn).name);
|
||||
assertEquals("(I)V", firstCall(cn).desc);
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void extGenerateMipmapAliasIsRedirected() {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/EXTFramebufferObject", "glGenerateMipmapEXT", "(I)V");
|
||||
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
assertTrue(changed);
|
||||
assertEquals(GLSM, firstCall(cn).owner);
|
||||
assertEquals("glGenerateMipmap", firstCall(cn).name);
|
||||
assertEquals("(I)V", firstCall(cn).desc);
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void newlyMappedReadPixelsIsRedirected() {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/GL11", "glReadPixels", "(IIIIIILjava/nio/IntBuffer;)V");
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
@@ -122,4 +123,27 @@ class ShaderDiskCacheTest {
|
||||
ShaderDiskCache.put(ShaderDiskCache.key("t").i(9), new byte[] {5});
|
||||
assertArrayEquals(new byte[] {5}, ShaderDiskCache.get(ShaderDiskCache.key("t").i(9)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void retainLayerKeepsListedEntriesOtherLayersAndInFlightWrites() throws IOException {
|
||||
ShaderDiskCache.configure(dir, "a");
|
||||
final ShaderDiskCache.Key kept = ShaderDiskCache.key("p").str("kept");
|
||||
final ShaderDiskCache.Key dropped = ShaderDiskCache.key("p").str("dropped");
|
||||
final ShaderDiskCache.Key otherLayer = ShaderDiskCache.key("q").str("dropped");
|
||||
ShaderDiskCache.put(kept, new byte[] {1});
|
||||
ShaderDiskCache.put(dropped, new byte[] {2});
|
||||
ShaderDiskCache.put(otherLayer, new byte[] {3});
|
||||
final Path inFlight = dir.resolve("p").resolve(dropped.hex() + ".7.tmp");
|
||||
Files.write(inFlight, new byte[] {4});
|
||||
|
||||
ShaderDiskCache.retainLayer("p", Set.of(kept.hex()));
|
||||
assertArrayEquals(new byte[] {1}, ShaderDiskCache.get(kept));
|
||||
assertNull(ShaderDiskCache.get(dropped));
|
||||
assertArrayEquals(new byte[] {3}, ShaderDiskCache.get(otherLayer));
|
||||
assertTrue(Files.exists(inFlight));
|
||||
|
||||
ShaderDiskCache.retainLayer("p", null);
|
||||
assertNull(ShaderDiskCache.get(kept));
|
||||
assertArrayEquals(new byte[] {3}, ShaderDiskCache.get(otherLayer));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.gtnewhorizons.angelica.glsm.states;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class PixelStoreStateTest {
|
||||
|
||||
@Test
|
||||
void defaults() {
|
||||
assertTrue(PixelStoreState.DEFAULT.isDefault());
|
||||
assertEquals(4, PixelStoreState.DEFAULT.alignment());
|
||||
assertEquals(0, PixelStoreState.DEFAULT.rowLength());
|
||||
assertFalse(PixelStoreState.DEFAULT.swapBytes());
|
||||
assertFalse(PixelStoreState.DEFAULT.lsbFirst());
|
||||
}
|
||||
|
||||
@Test
|
||||
void withUpdatesUnpackParams() {
|
||||
PixelStoreState s = PixelStoreState.DEFAULT
|
||||
.with(GL11.GL_UNPACK_ALIGNMENT, 1)
|
||||
.with(GL11.GL_UNPACK_ROW_LENGTH, 64)
|
||||
.with(GL11.GL_UNPACK_SKIP_ROWS, 2)
|
||||
.with(GL11.GL_UNPACK_SKIP_PIXELS, 3)
|
||||
.with(GL12.GL_UNPACK_IMAGE_HEIGHT, 32)
|
||||
.with(GL12.GL_UNPACK_SKIP_IMAGES, 1)
|
||||
.with(GL11.GL_UNPACK_SWAP_BYTES, 1)
|
||||
.with(GL11.GL_UNPACK_LSB_FIRST, 1);
|
||||
assertEquals(new PixelStoreState(1, 64, 2, 3, 32, 1, true, true), s);
|
||||
assertFalse(s.isDefault());
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidValuesIgnored() {
|
||||
assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(GL11.GL_UNPACK_ALIGNMENT, 3));
|
||||
assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(GL11.GL_UNPACK_ALIGNMENT, 0));
|
||||
assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(GL11.GL_UNPACK_ROW_LENGTH, -1));
|
||||
assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(GL11.GL_UNPACK_SKIP_ROWS, -5));
|
||||
assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(GL12.GL_UNPACK_IMAGE_HEIGHT, -1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void booleansAcceptAnyNonzero() {
|
||||
assertTrue(PixelStoreState.DEFAULT.with(GL11.GL_UNPACK_SWAP_BYTES, 2).swapBytes());
|
||||
assertTrue(PixelStoreState.DEFAULT.with(GL11.GL_UNPACK_LSB_FIRST, -1).lsbFirst());
|
||||
PixelStoreState on = PixelStoreState.DEFAULT.with(GL11.GL_UNPACK_SWAP_BYTES, 1);
|
||||
assertFalse(on.with(GL11.GL_UNPACK_SWAP_BYTES, 0).swapBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
void packPnamesSetTheSameFields() {
|
||||
PixelStoreState s = PixelStoreState.DEFAULT
|
||||
.with(GL11.GL_PACK_ALIGNMENT, 1)
|
||||
.with(GL11.GL_PACK_ROW_LENGTH, 64)
|
||||
.with(GL11.GL_PACK_SKIP_ROWS, 2)
|
||||
.with(GL11.GL_PACK_SKIP_PIXELS, 3)
|
||||
.with(GL12.GL_PACK_IMAGE_HEIGHT, 32)
|
||||
.with(GL12.GL_PACK_SKIP_IMAGES, 1)
|
||||
.with(GL11.GL_PACK_SWAP_BYTES, 1)
|
||||
.with(GL11.GL_PACK_LSB_FIRST, 1);
|
||||
assertEquals(new PixelStoreState(1, 64, 2, 3, 32, 1, true, true), s);
|
||||
assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(GL11.GL_PACK_ALIGNMENT, 3));
|
||||
assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(GL11.GL_PACK_ROW_LENGTH, -1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unknownPnamesIgnored() {
|
||||
assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(0x1234, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unchangedValueReturnsSameInstance() {
|
||||
final PixelStoreState s = new PixelStoreState(1, 64, 2, 3, 32, 1, true, true);
|
||||
assertSame(s, s.with(GL11.GL_UNPACK_ALIGNMENT, 1));
|
||||
assertSame(s, s.with(GL11.GL_PACK_ALIGNMENT, 1));
|
||||
assertSame(s, s.with(GL11.GL_UNPACK_ROW_LENGTH, 64));
|
||||
assertSame(s, s.with(GL11.GL_PACK_ROW_LENGTH, 64));
|
||||
assertSame(s, s.with(GL11.GL_UNPACK_SKIP_ROWS, 2));
|
||||
assertSame(s, s.with(GL11.GL_PACK_SKIP_ROWS, 2));
|
||||
assertSame(s, s.with(GL11.GL_UNPACK_SKIP_PIXELS, 3));
|
||||
assertSame(s, s.with(GL11.GL_PACK_SKIP_PIXELS, 3));
|
||||
assertSame(s, s.with(GL12.GL_UNPACK_IMAGE_HEIGHT, 32));
|
||||
assertSame(s, s.with(GL12.GL_PACK_IMAGE_HEIGHT, 32));
|
||||
assertSame(s, s.with(GL12.GL_UNPACK_SKIP_IMAGES, 1));
|
||||
assertSame(s, s.with(GL12.GL_PACK_SKIP_IMAGES, 1));
|
||||
assertSame(s, s.with(GL11.GL_UNPACK_SWAP_BYTES, 5));
|
||||
assertSame(s, s.with(GL11.GL_PACK_SWAP_BYTES, 5));
|
||||
assertSame(s, s.with(GL11.GL_UNPACK_LSB_FIRST, 1));
|
||||
assertSame(s, s.with(GL11.GL_PACK_LSB_FIRST, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isPackSeparatesTheFamilies() {
|
||||
final int[] pack = {
|
||||
GL11.GL_PACK_ALIGNMENT, GL11.GL_PACK_ROW_LENGTH, GL11.GL_PACK_SKIP_ROWS, GL11.GL_PACK_SKIP_PIXELS,
|
||||
GL12.GL_PACK_IMAGE_HEIGHT, GL12.GL_PACK_SKIP_IMAGES, GL11.GL_PACK_SWAP_BYTES, GL11.GL_PACK_LSB_FIRST,
|
||||
};
|
||||
final int[] unpack = {
|
||||
GL11.GL_UNPACK_ALIGNMENT, GL11.GL_UNPACK_ROW_LENGTH, GL11.GL_UNPACK_SKIP_ROWS, GL11.GL_UNPACK_SKIP_PIXELS,
|
||||
GL12.GL_UNPACK_IMAGE_HEIGHT, GL12.GL_UNPACK_SKIP_IMAGES, GL11.GL_UNPACK_SWAP_BYTES, GL11.GL_UNPACK_LSB_FIRST,
|
||||
};
|
||||
for (final int pname : pack) {
|
||||
assertTrue(PixelStoreState.isPack(pname));
|
||||
}
|
||||
for (final int pname : unpack) {
|
||||
assertFalse(PixelStoreState.isPack(pname));
|
||||
}
|
||||
assertFalse(PixelStoreState.isPack(0x1234));
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package com.gtnewhorizons.angelica.glsm.states;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class PixelUnpackStateTest {
|
||||
|
||||
@Test
|
||||
void defaults() {
|
||||
assertTrue(PixelUnpackState.DEFAULT.isDefault());
|
||||
assertEquals(4, PixelUnpackState.DEFAULT.alignment());
|
||||
assertEquals(0, PixelUnpackState.DEFAULT.rowLength());
|
||||
assertFalse(PixelUnpackState.DEFAULT.swapBytes());
|
||||
assertFalse(PixelUnpackState.DEFAULT.lsbFirst());
|
||||
}
|
||||
|
||||
@Test
|
||||
void withUpdatesUnpackParams() {
|
||||
PixelUnpackState s = PixelUnpackState.DEFAULT
|
||||
.with(GL11.GL_UNPACK_ALIGNMENT, 1)
|
||||
.with(GL11.GL_UNPACK_ROW_LENGTH, 64)
|
||||
.with(GL11.GL_UNPACK_SKIP_ROWS, 2)
|
||||
.with(GL11.GL_UNPACK_SKIP_PIXELS, 3)
|
||||
.with(GL12.GL_UNPACK_IMAGE_HEIGHT, 32)
|
||||
.with(GL12.GL_UNPACK_SKIP_IMAGES, 1)
|
||||
.with(GL11.GL_UNPACK_SWAP_BYTES, 1)
|
||||
.with(GL11.GL_UNPACK_LSB_FIRST, 1);
|
||||
assertEquals(new PixelUnpackState(1, 64, 2, 3, 32, 1, true, true), s);
|
||||
assertFalse(s.isDefault());
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidValuesIgnored() {
|
||||
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_ALIGNMENT, 3));
|
||||
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_ALIGNMENT, 0));
|
||||
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_ROW_LENGTH, -1));
|
||||
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_SKIP_ROWS, -5));
|
||||
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL12.GL_UNPACK_IMAGE_HEIGHT, -1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void booleansAcceptAnyNonzero() {
|
||||
assertTrue(PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_SWAP_BYTES, 2).swapBytes());
|
||||
assertTrue(PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_LSB_FIRST, -1).lsbFirst());
|
||||
PixelUnpackState on = PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_SWAP_BYTES, 1);
|
||||
assertFalse(on.with(GL11.GL_UNPACK_SWAP_BYTES, 0).swapBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
void packAndUnknownPnamesIgnored() {
|
||||
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_PACK_ALIGNMENT, 1));
|
||||
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_PACK_ROW_LENGTH, 16));
|
||||
assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(0x1234, 1));
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,9 @@
|
||||
# LOCAL to test local config updates.
|
||||
gtnh.settings.blowdryerTag = 0.2.2
|
||||
|
||||
# See mod-version-gradle.kts
|
||||
gtnh.modules.gitVersion = false
|
||||
|
||||
# Human-readable mod name, available for mcmod.info population.
|
||||
modName = Angelica
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ launchwrapper = "1.12"
|
||||
# End Frozen
|
||||
|
||||
lwjgl3 = "3.4.2-SNAPSHOT"
|
||||
# Must equal the LWJGL release shipped by lwjgl3ify. Do not bump without bumping lwjgl3ify.
|
||||
lwjgl3Release = "3.4.2"
|
||||
lwjgl3ify = "3.0.31"
|
||||
|
||||
celeritas = "2.5.12-GTNH"
|
||||
@@ -75,6 +77,7 @@ lwjgl3-opengl = { module = "org.lwjgl:lwjgl-opengl", version.ref = "lwjgl3" }
|
||||
lwjgl3-sdl = { module = "org.lwjgl:lwjgl-sdl", version.ref = "lwjgl3" }
|
||||
lwjgl3-spvc = { module = "org.lwjgl:lwjgl-spvc", version.ref = "lwjgl3" }
|
||||
lwjgl3-shaderc = { module = "org.lwjgl:lwjgl-shaderc", version.ref = "lwjgl3" }
|
||||
lwjgl3-vulkan = { module = "org.lwjgl:lwjgl-vulkan", version.ref = "lwjgl3Release" }
|
||||
|
||||
# Consumed with an explicit :dev / :universal / :downgraded-8 classifier at the call site.
|
||||
gtnhlib = { module = "com.github.GTNewHorizons:GTNHLib", version.ref = "gtnhlib" }
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// Replaces GTNHGradle's GitVersionModule (gtnh.modules.gitVersion = false): same version format, but only
|
||||
// x.y.z[-suffix] tags count, so additional tags (moltenvk-v*, tracy-v*) on master never become the mod version.
|
||||
|
||||
fun git(vararg args: String): String? = try {
|
||||
val exec = providers.exec {
|
||||
commandLine("git", *args)
|
||||
workingDir = rootDir
|
||||
isIgnoreExitValue = true
|
||||
}
|
||||
if (exec.result.get().exitValue == 0) exec.standardOutput.asText.get().trim() else null
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
fun gitVersion(): String {
|
||||
val described = git("describe", "--tags", "--long", "--first-parent", "--abbrev=7", "--match=[0-9]*.[0-9]*.[0-9]*", "HEAD")
|
||||
val match = described?.let { Regex("(.*)-([0-9]+)-g[0-9a-f]+").matchEntire(it) }
|
||||
val tag = match?.groupValues?.get(1)
|
||||
val distance = match?.groupValues?.get(2)?.toInt() ?: 0
|
||||
val hash = git("rev-parse", "HEAD")?.take(10)?.ifEmpty { null }
|
||||
val base = tag ?: hash
|
||||
if (base == null) {
|
||||
logger.error("No git repository found; set the VERSION environment variable to version this build.")
|
||||
return "NO-GIT-TAG-SET"
|
||||
}
|
||||
val dirty = !git("status", "--porcelain").isNullOrEmpty()
|
||||
val branch = (git("branch", "--show-current")?.ifEmpty { null } ?: System.getenv("GIT_BRANCH") ?: "git")
|
||||
.removePrefix("origin/")
|
||||
.replace(Regex("[^a-zA-Z0-9-]+"), "-")
|
||||
return when {
|
||||
distance > 0 -> "$base-$branch.$distance+$hash${if (dirty) "-dirty" else ""}"
|
||||
dirty -> "$base-$branch+$hash-dirty"
|
||||
else -> base
|
||||
}
|
||||
}
|
||||
|
||||
extra["modVersion"] = providers.environmentVariable("VERSION").orNull ?: gitVersion()
|
||||
+22
@@ -28,6 +28,7 @@ import org.lwjgl.opengl.GL31C;
|
||||
import org.lwjgl.opengl.GL32C;
|
||||
import org.lwjgl.opengl.GL33C;
|
||||
import org.lwjgl.opengl.GL40C;
|
||||
import org.lwjgl.opengl.GL41C;
|
||||
import org.lwjgl.opengl.GL42C;
|
||||
import org.lwjgl.opengl.GL43C;
|
||||
import org.lwjgl.opengl.GL44C;
|
||||
@@ -965,6 +966,27 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
||||
return GL20C.glIsProgram(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsProgramBinary() {
|
||||
return !RenderSystem.isGLES() && (caps.OpenGL41 || caps.GL_ARB_get_program_binary)
|
||||
&& GL11C.glGetInteger(GL41C.GL_NUM_PROGRAM_BINARY_FORMATS) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void programParameteri(int program, int pname, int value) {
|
||||
GL41C.glProgramParameteri(program, pname, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getProgramBinary(int program, IntBuffer length, IntBuffer binaryFormat, ByteBuffer binary) {
|
||||
GL41C.glGetProgramBinary(program, length, binaryFormat, binary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void programBinary(int program, int binaryFormat, ByteBuffer binary) {
|
||||
GL41C.glProgramBinary(program, binaryFormat, binary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateProgram(int program) {
|
||||
GL20C.glValidateProgram(program);
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
version = rootProject.version
|
||||
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
options.release.set(8)
|
||||
}
|
||||
|
||||
val rfgObfAttr = Attribute.of("com.gtnewhorizons.retrofuturagradle.obfuscation", String::class.java)
|
||||
val rfgTransformedAttr = Attribute.of("rfgDeobfuscatorTransformed", Boolean::class.javaObjectType)
|
||||
|
||||
configurations {
|
||||
listOf("apiElements", "runtimeElements").forEach { name ->
|
||||
named(name) {
|
||||
attributes {
|
||||
attribute(rfgObfAttr, "mcp")
|
||||
attribute(rfgTransformedAttr, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val moltenvkVersion = (findProperty("moltenvkVersion") as String?) ?: libs.versions.lwjgl3Release.get()
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "Forge"
|
||||
url = uri("https://maven.minecraftforge.net/")
|
||||
}
|
||||
maven {
|
||||
name = "Forge artifact-only"
|
||||
url = uri("https://maven.minecraftforge.net/")
|
||||
metadataSources { artifact() }
|
||||
content { includeModule("net.minecraftforge", "forge") }
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
val moltenvkNatives by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("net.minecraftforge:forge:1.7.10-10.13.4.1614-1.7.10:universal") { isTransitive = false }
|
||||
|
||||
listOf("natives-macos", "natives-macos-arm64").forEach { nativesClassifier ->
|
||||
moltenvkNatives(libs.lwjgl3.vulkan) {
|
||||
artifact { classifier = nativesClassifier }
|
||||
isTransitive = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val extractMoltenVK = tasks.register<Sync>("extractMoltenVK") {
|
||||
val outDir = layout.buildDirectory.dir("generated/resources/moltenvk")
|
||||
from(provider { moltenvkNatives.files.map { zipTree(it) } }) {
|
||||
include("macos/**/libMoltenVK.dylib")
|
||||
}
|
||||
includeEmptyDirs = false
|
||||
into(outDir)
|
||||
doLast {
|
||||
listOf("macos/x64", "macos/arm64").forEach { dir ->
|
||||
val rel = "$dir/org/lwjgl/vulkan/libMoltenVK.dylib"
|
||||
if (!outDir.get().file(rel).asFile.isFile) {
|
||||
throw GradleException("MoltenVK dylib missing after extraction: $rel")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceSets["main"].resources.srcDir(extractMoltenVK)
|
||||
|
||||
val distJar = tasks.register<Jar>("distJar") {
|
||||
archiveBaseName.set("angelica-moltenvk")
|
||||
archiveVersion.set(moltenvkVersion)
|
||||
from(sourceSets["main"].output)
|
||||
manifest {
|
||||
attributes(
|
||||
"FMLCorePlugin" to "com.gtnewhorizons.angelica.moltenvk.loading.MoltenVKCorePlugin"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("mavenJava") {
|
||||
groupId = "com.gtnewhorizons.angelica"
|
||||
artifactId = "angelica-moltenvk"
|
||||
version = moltenvkVersion
|
||||
artifact(distJar)
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
if (hasProperty("publishMoltenVK") && System.getenv("MAVEN_USER") != null) {
|
||||
maven {
|
||||
name = "GTNHMaven"
|
||||
url = uri(rootProject.findProperty("mavenPublishUrl")?.toString() ?: "https://nexus.gtnewhorizons.com/repository/releases/")
|
||||
credentials {
|
||||
username = System.getenv("MAVEN_USER")
|
||||
password = System.getenv("MAVEN_PASSWORD")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.gtnewhorizons.angelica.moltenvk.loading;
|
||||
|
||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@IFMLLoadingPlugin.MCVersion("1.7.10")
|
||||
public class MoltenVKCorePlugin implements IFMLLoadingPlugin {
|
||||
|
||||
@Override
|
||||
public String[] getASMTransformerClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModContainerClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSetupClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectData(Map<String, Object> data) {}
|
||||
|
||||
@Override
|
||||
public String getAccessTransformerClass() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -9,6 +9,9 @@
|
||||
# route - straight | pan | circuit | static
|
||||
# length - Units: blocks (straight), blocks per leg (circuit), degrees (pan), ticks (static).
|
||||
# extra - passed through to gradle; -Dangelica.* reaches the client (e.g. -Dangelica.flyby.timeOfDay=1500).
|
||||
# -Dangelica.flyby.camera=FIRST_PERSON|THIRD_PERSON_BACK|THIRD_PERSON_FRONT (default FIRST_PERSON).
|
||||
# -Dangelica.flyby.dimension=<id> moves the player there first (1 = End), after world saving is off.
|
||||
# -PclientJvmVendor=adoptium (macOS) picks the JVM vendor for runClient17/21/25.
|
||||
#
|
||||
# FLYBY_SPEED - blocks per tick. (0.5 ~= creative flight).
|
||||
# FLYBY_PACING - UNCAPPED | CONFIGURED. Passed through when set.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# End crystals next to the arrival platform. Use with -Dangelica.flyby.dimension=1, route pan.
|
||||
summon EnderCrystal ~ ~2 ~12 {ForgeData:{flyby:1b}}
|
||||
summon EnderCrystal ~8 ~2 ~8 {ForgeData:{flyby:1b}}
|
||||
summon EnderCrystal ~-8 ~4 ~10 {ForgeData:{flyby:1b}}
|
||||
@@ -2,7 +2,7 @@ package com.gtnewhorizons.angelica.sdlgpu;
|
||||
|
||||
import static org.lwjgl.sdl.SDLVideo.SDL_PROP_WINDOW_CREATE_METAL_BOOLEAN;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendOptions;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||
import com.gtnewhorizons.angelica.glsm.loading.DependencyVerifier;
|
||||
@@ -83,7 +83,7 @@ public final class SDLGPUGate {
|
||||
|
||||
private static synchronized boolean probe() {
|
||||
if (deviceReady != null) return deviceReady;
|
||||
if (!SystemProperties.USE_SDL_GPU || !isSDLGPUAvailable()) {
|
||||
if (!BackendOptions.sdlGpuRequested() || !isSDLGPUAvailable()) {
|
||||
deviceReady = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
+260
-135
@@ -1,14 +1,17 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.compute.VertexWriteReplayer;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.compute.VoxelizationDispatcher;
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.CaptureGate;
|
||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendOptions;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.GLDebugMessageListener;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.MainThreadPump;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.VertexWriteReplaySetup;
|
||||
import me.eigenraven.lwjgl3ify.client.MainThreadExec;
|
||||
import org.lwjglx.Lwjgl3ifyEventLoop;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
|
||||
@@ -40,6 +43,7 @@ import com.gtnewhorizons.angelica.sdlgpu.resource.FBOClearTracker;
|
||||
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.MappedRange;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PersistentBufferSync;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PackState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PersistentMapping;
|
||||
@@ -63,6 +67,7 @@ import com.gtnewhorizons.angelica.sdlgpu.util.ThreadRegistry;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -161,12 +166,14 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
@Override public long nextSeq() { return TransferThread.nextSeq(); }
|
||||
});
|
||||
private final PipelineApplier pipelineApplier = new PipelineApplier(frameManager, resourceManager, shaderManager, pipelineStore, fboClearTracker, persistentSync, samplerBinder, storageTextureBinder, storageBufferBinder);
|
||||
private final VertexWriteReplayer vertexWriteReplayer = new VertexWriteReplayer(shaderManager, resourceManager, pipelineApplier, computeBinder, SDLGPURenderBackend::s);
|
||||
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;
|
||||
|
||||
private final IntOpenHashSet missingProgramWarned = new IntOpenHashSet();
|
||||
private final LongOpenHashSet warnedBufferOps = new LongOpenHashSet();
|
||||
|
||||
private boolean droppedCopyWarned;
|
||||
private boolean droppedSsboWriteWarned;
|
||||
@@ -247,7 +254,9 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
|
||||
|
||||
@Override public void onPersistentBufferWrite(int glId, long offset, long size) {
|
||||
persistentSync.onPersistentBufferWrite(glId, offset, size);
|
||||
if (!persistentSync.onPersistentBufferWrite(glId, offset, size)) {
|
||||
warnInvalidBufferOp("persistent write out of range", glId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onPostWindowCreate(long window) {
|
||||
@@ -295,6 +304,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
});
|
||||
pipelineApplier.setDeferredUploadSink(this::enqueuePreCopied);
|
||||
frameManager.setRenderPassEndHook(vertexWriteReplayer::flush);
|
||||
resourceManager.setTextureReplaceHook(this::flushVertexWritesBeforeTextureChange);
|
||||
frameManager.setPreRenderPassHook(() -> {
|
||||
final ContextState st = s();
|
||||
pipelineApplier.flushUniformBlocks(st);
|
||||
@@ -514,14 +525,13 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
st.appliedLogicOpKey = 0;
|
||||
st.fragSamplerBindings.free();
|
||||
st.vertSamplerBindings.free();
|
||||
if (st.mappedStagingBuffer != null) {
|
||||
MemoryUtil.memFree(st.mappedStagingBuffer);
|
||||
}
|
||||
clearMappedState(st);
|
||||
}
|
||||
registeredStates.clear();
|
||||
|
||||
drawDispatch.clearWarnedState();
|
||||
synchronized (warnedBufferOps) {
|
||||
warnedBufferOps.clear();
|
||||
}
|
||||
attachmentClear.shutdown();
|
||||
depthStencilReadback.shutdown();
|
||||
textureOps.shutdown();
|
||||
@@ -532,7 +542,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override public boolean isAvailable() {
|
||||
return SystemProperties.USE_SDL_GPU && SDLGPUGate.isSDLGPUAvailable() && SDLGPUGate.isEngaged();
|
||||
return BackendOptions.sdlGpuRequested() && SDLGPUGate.isSDLGPUAvailable() && SDLGPUGate.isEngaged();
|
||||
}
|
||||
|
||||
@Override public String getName() {
|
||||
@@ -543,6 +553,10 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
return device.getDevice() != 0;
|
||||
}
|
||||
|
||||
@Override public boolean hasContextOnThread() {
|
||||
return hasContext() && (Thread.currentThread() == GLStateManager.getMainThread() || tlState.get() != null);
|
||||
}
|
||||
|
||||
@Override public boolean isCurrent() {
|
||||
return Thread.currentThread() == GLStateManager.getMainThread();
|
||||
}
|
||||
@@ -1198,9 +1212,11 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
if (st.rasterizerDiscard) return;
|
||||
final FrameState f = frameManager.frame();
|
||||
if (!f.frameActive) { f.droppedDrawsThisFrame++; return; }
|
||||
prepareVertexWrites(st);
|
||||
if (mode == GL11.GL_TRIANGLE_FAN && count >= 3) {
|
||||
if (!drawDispatch.drawTriangleFanAsTriangleList(st, first, count)) { f.droppedDrawsThisFrame++; return; }
|
||||
if (SystemProperties.FFP_TRACE) ffpTrace.trace(st, "arraysFan", mode, count, 0, first, 0);
|
||||
captureVertexWrites(st, first, count, 1, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
drawDispatch.setPrimitiveTypeForDraw(st, FormatMap.mapPrimitiveType(mode));
|
||||
@@ -1209,6 +1225,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
if (!pipelineApplier.applyPipelineAndState(st, f)) { f.droppedDrawsThisFrame++; return; }
|
||||
if (SystemProperties.FFP_TRACE) ffpTrace.trace(st, "arrays", mode, count, 0, first, 0);
|
||||
SDL_DrawGPUPrimitives(f.renderPass, count, 1, first, 0);
|
||||
captureVertexWrites(st, first, count, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override public void drawElements(int mode, int count, int type, long indices) {
|
||||
@@ -1218,6 +1235,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
final int firstIndex = (int) (indices / FormatMap.indexElementSize(type));
|
||||
if (SystemProperties.FFP_TRACE) ffpTrace.trace(st, "elements", mode, count, type, firstIndex, 0);
|
||||
drawDispatch.issueIndexedDraw(st, rp, st.currentVao.elementBuffer, type, count, 1, firstIndex, 0);
|
||||
captureVertexWrites(st, 0, count, 1, type, indices, 0);
|
||||
}
|
||||
|
||||
@Override public void drawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
|
||||
@@ -1227,6 +1245,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
final int firstIndex = (int) (indices / FormatMap.indexElementSize(type));
|
||||
if (SystemProperties.FFP_TRACE) ffpTrace.trace(st, "elementsInstanced", mode, count, type, firstIndex, 0);
|
||||
drawDispatch.issueIndexedDraw(st, rp, st.currentVao.elementBuffer, type, count, primcount, firstIndex, 0);
|
||||
captureVertexWrites(st, 0, count, primcount, type, indices, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1237,10 +1256,12 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
final int firstIndex = (int) (indices / FormatMap.indexElementSize(type));
|
||||
if (SystemProperties.FFP_TRACE) ffpTrace.trace(st, "elementsBaseVertex", mode, count, type, firstIndex, baseVertex);
|
||||
drawDispatch.issueIndexedDraw(st, rp, st.currentVao.elementBuffer, type, count, 1, firstIndex, baseVertex);
|
||||
captureVertexWrites(st, 0, count, 1, type, indices, baseVertex);
|
||||
}
|
||||
|
||||
private long prepareIndexedDrawBind(ContextState st, int mode, int type, String opName) {
|
||||
if (st.rasterizerDiscard) return 0;
|
||||
prepareVertexWrites(st);
|
||||
if (!drawDispatch.prepareIndexedDraw(st, mode, type)) return 0;
|
||||
final int ebo = st.currentVao.elementBuffer;
|
||||
final long eboHandle = resourceManager.getBufferHandle(ebo);
|
||||
@@ -1269,6 +1290,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
final int baseVertex = MemoryUtil.memGetInt(pBaseVertex + offset);
|
||||
if (SystemProperties.FFP_TRACE) ffpTrace.trace(st, "multiElementsBaseVertex", mode, count, type, firstIndex, baseVertex);
|
||||
drawDispatch.issueIndexedDraw(st, rp, ebo, type, count, 1, firstIndex, baseVertex);
|
||||
captureVertexWrites(st, 0, count, 1, type, indices, baseVertex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1295,6 +1317,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
if (SystemProperties.FFP_TRACE) ffpTrace.trace(st, "elementsIndirect", mode, drawcount, type, (int) indirect, 0);
|
||||
SDL_DrawGPUIndexedPrimitivesIndirect(rp, indirectHandle, (int) indirect, drawcount);
|
||||
if (st.boundProgramObj != null && st.boundProgramObj.vertexWriteReplay != null) vertexWriteReplayer.noteIndirectDraw();
|
||||
} finally {
|
||||
Tracy.endZone();
|
||||
}
|
||||
@@ -1328,6 +1351,12 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
final int srcGlId = getBoundBuffer(readTarget);
|
||||
final int dstGlId = getBoundBuffer(writeTarget);
|
||||
if (srcGlId == 0 || dstGlId == 0 || size <= 0) return;
|
||||
final long srcSize = resourceManager.getBufferSize(srcGlId);
|
||||
final long dstSize = resourceManager.getBufferSize(dstGlId);
|
||||
if (readOffset < 0 || writeOffset < 0 || (srcSize > 0 && (readOffset > srcSize || size > srcSize - readOffset)) || (dstSize > 0 && (writeOffset > dstSize || size > dstSize - writeOffset))) {
|
||||
warnInvalidBufferOp("glCopyBufferSubData out of range", dstGlId);
|
||||
return;
|
||||
}
|
||||
resourceManager.dropArrayShadow(dstGlId);
|
||||
resourceManager.dropEboShadow(dstGlId);
|
||||
resourceManager.markBufferContentsDefined(dstGlId);
|
||||
@@ -1342,7 +1371,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
deferredCopyOp.dstHandle = dstHandle;
|
||||
deferredCopyOp.writeOffset = writeOffset;
|
||||
deferredCopyOp.seq = seq;
|
||||
final boolean handled = resourceManager.enqueuePersistentCopyDeferred(srcGlId, dstGlId, seq, deferredCopyOp);
|
||||
final boolean handled = resourceManager.enqueuePersistentCopyDeferred(srcGlId, dstGlId, seq, readOffset, size, deferredCopyOp);
|
||||
if (handled) {
|
||||
wakeTransferThread();
|
||||
return;
|
||||
@@ -1403,6 +1432,11 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
resourceManager.deleteTexture(texture);
|
||||
}
|
||||
|
||||
/** Captured vertex-write replays name textures by GL id, which a delete or reallocation would make mean something else. */
|
||||
private void flushVertexWritesBeforeTextureChange() {
|
||||
if (vertexWriteReplayer.hasPending()) frameManager.endRenderPassIfActive(FrameManager.PASS_END_COMPUTE);
|
||||
}
|
||||
|
||||
|
||||
@Override public void bindTexture(int target, int texture) {
|
||||
if (isProxyTarget(target)) return;
|
||||
@@ -2756,8 +2790,10 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
private void deleteBuffer(int buffer) {
|
||||
dropMappingFor(buffer);
|
||||
if (buffer != 0) unbindDeletedBuffer(s(), buffer);
|
||||
if (buffer != 0) {
|
||||
freePlainMappingFor(buffer, false);
|
||||
unbindDeletedBuffer(s(), buffer);
|
||||
}
|
||||
readbackShadows.release(buffer);
|
||||
resourceManager.deleteBuffer(buffer);
|
||||
}
|
||||
@@ -2804,12 +2840,6 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
if (ssboChanged) st.ssboBindGen++;
|
||||
}
|
||||
|
||||
private static void dropMappingFor(int buffer) {
|
||||
final ContextState st = s();
|
||||
if (buffer == 0 || st.mappedBufferGlId != buffer || st.mappedStagingBuffer == null) return;
|
||||
MemoryUtil.memFree(st.mappedStagingBuffer);
|
||||
clearMappedState(st);
|
||||
}
|
||||
@Override public void bindBuffer(int target, int buffer) {
|
||||
final ContextState st = s();
|
||||
switch (target) {
|
||||
@@ -2909,8 +2939,15 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
@Override public void bufferData(int target, long size, int usage) {
|
||||
final int glId = getBoundBuffer(target);
|
||||
final ContextState cs = s();
|
||||
final int glId = cs.getBoundBuffer(target);
|
||||
if (glId == 0) return;
|
||||
if (refuseImmutable(glId, "glBufferData on immutable storage")) return;
|
||||
respecifyStore(cs, target, glId, size, usage);
|
||||
}
|
||||
|
||||
private void respecifyStore(ContextState cs, int target, int glId, long size, int usage) {
|
||||
freePlainMappingFor(glId, true);
|
||||
resourceManager.recordBufferGlParams(glId, usage, BufferParams.MUTABLE_STORE);
|
||||
if (target == GL31.GL_UNIFORM_BUFFER) {
|
||||
if (resourceManager.getBufferSize(glId) == size) return;
|
||||
@@ -2926,14 +2963,45 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
readbackShadows.release(glId);
|
||||
resourceManager.deleteBuffer(glId);
|
||||
resourceManager.createBuffer(glId, sdlUsage, size);
|
||||
if (target == GL15.GL_ARRAY_BUFFER) s().bumpAttribStateGen();
|
||||
if (target == GL15.GL_ARRAY_BUFFER) cs.bumpAttribStateGen();
|
||||
}
|
||||
|
||||
private void freePlainMappingFor(int glId, boolean warn) {
|
||||
final ByteBuffer staging = resourceManager.takePlainStaging(glId);
|
||||
if (staging == null) return;
|
||||
MemoryUtil.memFree(staging);
|
||||
if (warn) warnInvalidBufferOp("glBufferData on mapped buffer", glId);
|
||||
}
|
||||
|
||||
private boolean refuseImmutable(int glId, String op) {
|
||||
if (resourceManager.getBufferStorageFlags(glId) == BufferParams.MUTABLE_STORE) return false;
|
||||
warnInvalidBufferOp(op, glId);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public boolean isBufferImmutable(int glId) {
|
||||
return resourceManager.getBufferStorageFlags(glId) != BufferParams.MUTABLE_STORE;
|
||||
}
|
||||
|
||||
private void warnInvalidBufferOp(String op, int glId) {
|
||||
synchronized (warnedBufferOps) {
|
||||
if (warnedBufferOps.add(((long) op.hashCode() << 32) | (glId & 0xFFFFFFFFL))) {
|
||||
LOG.warn("{} (buffer {})", op, glId, new Throwable());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void bufferData(int target, ByteBuffer data, int usage) {
|
||||
final ContextState cs = s();
|
||||
final int glId = getBoundBuffer(target);
|
||||
final int glId = cs.getBoundBuffer(target);
|
||||
if (glId == 0 || data == null) return;
|
||||
if (refuseImmutable(glId, "glBufferData on immutable storage")) return;
|
||||
respecifyStore(cs, target, glId, data, usage);
|
||||
}
|
||||
|
||||
private void respecifyStore(ContextState cs, int target, int glId, ByteBuffer data, int usage) {
|
||||
final long dataSize = data.remaining();
|
||||
freePlainMappingFor(glId, true);
|
||||
resourceManager.recordBufferGlParams(glId, usage, BufferParams.MUTABLE_STORE);
|
||||
if (target == GL31.GL_UNIFORM_BUFFER) {
|
||||
if (dataSize > 0) {
|
||||
@@ -3023,11 +3091,15 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
|
||||
@Override public void bufferSubData(int target, long offset, ByteBuffer data) {
|
||||
final ContextState cs = s();
|
||||
final int glId = getBoundBuffer(target);
|
||||
final int glId = cs.getBoundBuffer(target);
|
||||
if (glId == 0 || data == null) return;
|
||||
if (target == GL31.GL_UNIFORM_BUFFER) {
|
||||
final ByteBuffer shadow = resourceManager.getUboShadow(glId);
|
||||
if (shadow != null) {
|
||||
if (offset < 0 || offset + data.remaining() > shadow.capacity()) {
|
||||
warnInvalidBufferOp("glBufferSubData out of range on uniform buffer", glId);
|
||||
return;
|
||||
}
|
||||
shadow.position(0);
|
||||
ByteRegionCopy.copyByteRegion(data, data.position(), shadow, (int) offset, data.remaining());
|
||||
}
|
||||
@@ -3096,133 +3168,132 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
|
||||
@Override public boolean unmapBuffer(int target) {
|
||||
final ContextState cs = s();
|
||||
final int glId = getBoundBuffer(target);
|
||||
final int glId = cs.getBoundBuffer(target);
|
||||
|
||||
final PersistentMapping pm = resourceManager.removePersistentMapping(glId);
|
||||
if (pm != null) {
|
||||
resourceManager.releasePersistentStaging(pm);
|
||||
if (resourceManager.getPersistentMapping(glId) != null) {
|
||||
final PersistentMapping pm = resourceManager.removePersistentMapping(glId);
|
||||
if (pm != null) resourceManager.releasePersistentStaging(pm, glId, "unmap");
|
||||
return true;
|
||||
}
|
||||
|
||||
final ContextState st = cs;
|
||||
if (st.mappedStagingBuffer == null) return false;
|
||||
final MappedRange m = cs.mappingScratch;
|
||||
if (!resourceManager.takePlainMapping(glId, m)) {
|
||||
warnInvalidBufferOp("glUnmapBuffer on unmapped buffer", glId);
|
||||
return false;
|
||||
}
|
||||
|
||||
final boolean writeMap = (st.mappedAccessFlags & GL30.GL_MAP_WRITE_BIT) != 0;
|
||||
final ByteBuffer staging = m.staging;
|
||||
m.staging = null;
|
||||
final boolean writeMap = (m.accessFlags & GL30.GL_MAP_WRITE_BIT) != 0;
|
||||
|
||||
if (target == GL30.GL_PIXEL_UNPACK_BUFFER) {
|
||||
st.mappedStagingBuffer.position(0).limit((int) st.mappedLength);
|
||||
final long gpuHandle = resourceManager.getBufferHandle(st.mappedBufferGlId);
|
||||
staging.position(0).limit((int) m.length);
|
||||
final long gpuHandle = resourceManager.getBufferHandle(glId);
|
||||
if (writeMap && gpuHandle != 0) {
|
||||
if (st.deferUploads) {
|
||||
enqueuePreCopied(st.mappedStagingBuffer, gpuHandle, st.mappedOffset, false);
|
||||
if (cs.deferUploads) {
|
||||
enqueuePreCopied(staging, gpuHandle, m.offset, false);
|
||||
} else if (frameManager.getCommandBuffer() != 0) {
|
||||
resourceManager.uploadToBuffer(frameManager.ensureCopyPass(), st.mappedStagingBuffer, gpuHandle, st.mappedOffset, false);
|
||||
resourceManager.uploadToBuffer(frameManager.ensureCopyPass(), staging, gpuHandle, m.offset, false);
|
||||
}
|
||||
resourceManager.markBufferContentsDefined(st.mappedBufferGlId);
|
||||
resourceManager.markBufferContentsDefined(glId);
|
||||
}
|
||||
final ByteBuffer old = resourceManager.putPboStaging(glId, st.mappedStagingBuffer);
|
||||
final ByteBuffer old = resourceManager.putPboStaging(glId, staging);
|
||||
if (old != null) MemoryUtil.memFree(old);
|
||||
clearMappedState(st);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target == GL31.GL_UNIFORM_BUFFER) {
|
||||
final ByteBuffer shadow = resourceManager.getUboShadow(st.mappedBufferGlId);
|
||||
if (writeMap && shadow != null && st.mappedOffset + st.mappedLength <= shadow.capacity()) {
|
||||
st.mappedStagingBuffer.position(0).limit((int) st.mappedLength);
|
||||
shadow.position((int) st.mappedOffset).limit((int) (st.mappedOffset + st.mappedLength));
|
||||
shadow.put(st.mappedStagingBuffer);
|
||||
final ByteBuffer shadow = resourceManager.getUboShadow(glId);
|
||||
if (writeMap && shadow != null && m.offset + m.length <= shadow.capacity()) {
|
||||
staging.position(0).limit((int) m.length);
|
||||
shadow.position((int) m.offset).limit((int) (m.offset + m.length));
|
||||
shadow.put(staging);
|
||||
shadow.position(0).limit(shadow.capacity());
|
||||
resourceManager.markBufferContentsDefined(st.mappedBufferGlId);
|
||||
resourceManager.markBufferContentsDefined(glId);
|
||||
}
|
||||
MemoryUtil.memFree(st.mappedStagingBuffer);
|
||||
clearMappedState(st);
|
||||
MemoryUtil.memFree(staging);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!writeMap) {
|
||||
MemoryUtil.memFree(st.mappedStagingBuffer);
|
||||
clearMappedState(st);
|
||||
MemoryUtil.memFree(staging);
|
||||
return true;
|
||||
}
|
||||
|
||||
st.mappedStagingBuffer.position(0).limit((int) st.mappedLength);
|
||||
staging.position(0).limit((int) m.length);
|
||||
final long gpuHandle;
|
||||
if (st.mappedInvalidate) {
|
||||
if (m.invalidate) {
|
||||
final int sdlUsage = FormatMap.mapBufferUsage(0);
|
||||
final long glSize = Math.max(resourceManager.getBufferSize(st.mappedBufferGlId), st.mappedLength);
|
||||
final int glUsage = resourceManager.getBufferGlUsage(st.mappedBufferGlId);
|
||||
final int storageFlags = resourceManager.getBufferStorageFlags(st.mappedBufferGlId);
|
||||
resourceManager.deleteBuffer(st.mappedBufferGlId);
|
||||
gpuHandle = resourceManager.createBuffer(st.mappedBufferGlId, sdlUsage, glSize);
|
||||
resourceManager.recordBufferGlParams(st.mappedBufferGlId, glUsage, storageFlags);
|
||||
st.bumpAttribStateGen();
|
||||
final long glSize = Math.max(resourceManager.getBufferSize(glId), m.length);
|
||||
final int glUsage = resourceManager.getBufferGlUsage(glId);
|
||||
final int storageFlags = resourceManager.getBufferStorageFlags(glId);
|
||||
resourceManager.deleteBuffer(glId);
|
||||
gpuHandle = resourceManager.createBuffer(glId, sdlUsage, glSize);
|
||||
resourceManager.recordBufferGlParams(glId, glUsage, storageFlags);
|
||||
cs.bumpAttribStateGen();
|
||||
} else {
|
||||
gpuHandle = resourceManager.getBufferHandle(st.mappedBufferGlId);
|
||||
gpuHandle = resourceManager.getBufferHandle(glId);
|
||||
}
|
||||
if (target == GL15.GL_ELEMENT_ARRAY_BUFFER
|
||||
&& (cs.primitiveRestartEnabled || resourceManager.hasEboShadow(st.mappedBufferGlId))) {
|
||||
final int len = (int) st.mappedLength;
|
||||
st.mappedStagingBuffer.position(0).limit(len);
|
||||
persistentSync.mirrorEboShadow(st.mappedBufferGlId, st.mappedStagingBuffer, (int) st.mappedOffset, len);
|
||||
st.mappedStagingBuffer.position(0).limit(len);
|
||||
&& (cs.primitiveRestartEnabled || resourceManager.hasEboShadow(glId))) {
|
||||
final int len = (int) m.length;
|
||||
staging.position(0).limit(len);
|
||||
persistentSync.mirrorEboShadow(glId, staging, (int) m.offset, len);
|
||||
staging.position(0).limit(len);
|
||||
}
|
||||
if (target == GL15.GL_ARRAY_BUFFER && resourceManager.hasArrayShadow(st.mappedBufferGlId)) {
|
||||
final int len = (int) st.mappedLength;
|
||||
st.mappedStagingBuffer.position(0).limit(len);
|
||||
resourceManager.mirrorArrayShadowRegion(st.mappedBufferGlId, st.mappedStagingBuffer, (int) st.mappedOffset);
|
||||
st.mappedStagingBuffer.position(0).limit(len);
|
||||
if (target == GL15.GL_ARRAY_BUFFER && resourceManager.hasArrayShadow(glId)) {
|
||||
final int len = (int) m.length;
|
||||
staging.position(0).limit(len);
|
||||
resourceManager.mirrorArrayShadowRegion(glId, staging, (int) m.offset);
|
||||
staging.position(0).limit(len);
|
||||
}
|
||||
if (gpuHandle != 0) {
|
||||
if (st.deferUploads) {
|
||||
enqueuePreCopied(st.mappedStagingBuffer, gpuHandle, st.mappedOffset, false);
|
||||
if (cs.deferUploads) {
|
||||
enqueuePreCopied(staging, gpuHandle, m.offset, false);
|
||||
} else if (frameManager.getCommandBuffer() != 0) {
|
||||
resourceManager.uploadToBuffer(frameManager.ensureCopyPass(), st.mappedStagingBuffer, gpuHandle, st.mappedOffset, false);
|
||||
resourceManager.uploadToBuffer(frameManager.ensureCopyPass(), staging, gpuHandle, m.offset, false);
|
||||
}
|
||||
resourceManager.markBufferContentsDefined(st.mappedBufferGlId);
|
||||
resourceManager.markBufferContentsDefined(glId);
|
||||
}
|
||||
|
||||
MemoryUtil.memFree(st.mappedStagingBuffer);
|
||||
clearMappedState(st);
|
||||
MemoryUtil.memFree(staging);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void clearMappedState(ContextState st) {
|
||||
st.mappedStagingBuffer = null;
|
||||
st.mappedBufferGlId = 0;
|
||||
st.mappedOffset = 0;
|
||||
st.mappedLength = 0;
|
||||
st.mappedInvalidate = false;
|
||||
st.mappedAccessFlags = 0;
|
||||
}
|
||||
|
||||
@Override public ByteBuffer mapBufferRange(int target, long offset, long length, int access) {
|
||||
final int glId = getBoundBuffer(target);
|
||||
final ContextState cs = s();
|
||||
final int glId = cs.getBoundBuffer(target);
|
||||
if (glId == 0 || length <= 0) return null;
|
||||
|
||||
final boolean persistent = (access & GL44.GL_MAP_PERSISTENT_BIT) != 0;
|
||||
|
||||
if (persistent) {
|
||||
if (target == GL15.GL_ARRAY_BUFFER) resourceManager.dropArrayShadow(glId);
|
||||
final int storageFlags = resourceManager.getBufferStorageFlags(glId);
|
||||
if (storageFlags < 0 || (storageFlags & GL44.GL_MAP_PERSISTENT_BIT) == 0) {
|
||||
warnInvalidBufferOp("glMapBufferRange persistent without persistent storage", glId);
|
||||
return null;
|
||||
}
|
||||
final ByteBuffer staging = MemoryUtil.memCalloc((int) length);
|
||||
final PersistentMapping fresh = new PersistentMapping(staging, offset, length, access);
|
||||
final PersistentMapping prior = resourceManager.swapPersistentMapping(glId, fresh);
|
||||
if (prior != null) resourceManager.releasePersistentStaging(prior);
|
||||
if (!resourceManager.putPersistentMappingIfAbsent(glId, fresh)) {
|
||||
MemoryUtil.memFree(staging);
|
||||
warnInvalidBufferOp("glMapBufferRange on mapped buffer", glId);
|
||||
return null;
|
||||
}
|
||||
if (target == GL15.GL_ARRAY_BUFFER) resourceManager.dropArrayShadow(glId);
|
||||
return staging;
|
||||
}
|
||||
|
||||
final ContextState st = s();
|
||||
if (st.mappedStagingBuffer != null) MemoryUtil.memFree(st.mappedStagingBuffer);
|
||||
st.mappedStagingBuffer = MemoryUtil.memAlloc((int) length);
|
||||
st.mappedBufferGlId = glId;
|
||||
st.mappedOffset = offset;
|
||||
st.mappedLength = length;
|
||||
st.mappedInvalidate = (access & GL30.GL_MAP_INVALIDATE_BUFFER_BIT) != 0;
|
||||
st.mappedAccessFlags = access;
|
||||
final ByteBuffer staging = MemoryUtil.memAlloc((int) length);
|
||||
if ((access & (GL30.GL_MAP_INVALIDATE_BUFFER_BIT | GL30.GL_MAP_INVALIDATE_RANGE_BIT)) == 0) {
|
||||
seedMappedStaging(target, glId, offset, length, st.mappedStagingBuffer);
|
||||
seedMappedStaging(target, glId, offset, length, staging);
|
||||
}
|
||||
return st.mappedStagingBuffer;
|
||||
if (!resourceManager.putPlainMappingIfAbsent(glId, staging, offset, length, (access & GL30.GL_MAP_INVALIDATE_BUFFER_BIT) != 0, access)) {
|
||||
MemoryUtil.memFree(staging);
|
||||
warnInvalidBufferOp("glMapBufferRange on mapped buffer", glId);
|
||||
return null;
|
||||
}
|
||||
return staging;
|
||||
}
|
||||
|
||||
private void seedMappedStaging(int target, int glId, long offset, long length, ByteBuffer staging) {
|
||||
@@ -3310,22 +3381,31 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
@Override public void flushMappedBufferRange(int target, long offset, long length) {
|
||||
final int glId = getBoundBuffer(target);
|
||||
if (glId == 0 || length <= 0) return;
|
||||
persistentSync.onPersistentBufferWrite(glId, offset, length);
|
||||
if (!persistentSync.onPersistentBufferWrite(glId, offset, length)) {
|
||||
warnInvalidBufferOp("glFlushMappedBufferRange out of range", glId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void bufferStorage(int target, ByteBuffer data, int flags) {
|
||||
bufferData(target, data, GL15.GL_DYNAMIC_DRAW);
|
||||
recordImmutableStorage(target, flags);
|
||||
final ContextState cs = s();
|
||||
final int glId = cs.getBoundBuffer(target);
|
||||
if (glId == 0) return;
|
||||
if (refuseImmutable(glId, "glBufferStorage on immutable storage")) return;
|
||||
if (data != null) respecifyStore(cs, target, glId, data, GL15.GL_DYNAMIC_DRAW);
|
||||
recordImmutableStorage(glId, flags);
|
||||
}
|
||||
|
||||
@Override public void bufferStorage(int target, long size, int flags) {
|
||||
bufferData(target, size, GL15.GL_DYNAMIC_DRAW);
|
||||
recordImmutableStorage(target, flags);
|
||||
final ContextState cs = s();
|
||||
final int glId = cs.getBoundBuffer(target);
|
||||
if (glId == 0) return;
|
||||
if (refuseImmutable(glId, "glBufferStorage on immutable storage")) return;
|
||||
respecifyStore(cs, target, glId, size, GL15.GL_DYNAMIC_DRAW);
|
||||
recordImmutableStorage(glId, flags);
|
||||
}
|
||||
|
||||
private void recordImmutableStorage(int target, int flags) {
|
||||
final int glId = getBoundBuffer(target);
|
||||
if (glId != 0) resourceManager.recordBufferGlParams(glId, GL15.GL_DYNAMIC_DRAW, flags);
|
||||
private void recordImmutableStorage(int glId, int flags) {
|
||||
resourceManager.recordBufferGlParams(glId, GL15.GL_DYNAMIC_DRAW, flags);
|
||||
}
|
||||
@Override public void getBufferSubData(int target, long offset, ByteBuffer data) {
|
||||
final int glId = getBoundBuffer(target);
|
||||
@@ -3341,23 +3421,12 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
private void downloadBufferBlocking(long handle, int offset, int size, ByteBuffer out) {
|
||||
flushDeferredUploadsForRead();
|
||||
frameManager.endCopyPassIfActive();
|
||||
frameManager.endRenderPassIfActive(FrameManager.PASS_END_COPY);
|
||||
flushAndSubmitMidFrame();
|
||||
final long cb = SDL_AcquireGPUCommandBuffer(device.getDevice());
|
||||
if (cb == 0) return;
|
||||
resourceManager.downloadFromBuffer(cb, handle, offset, size, out);
|
||||
}
|
||||
|
||||
private void flushDeferredUploadsForRead() {
|
||||
final ContextState st = s();
|
||||
drainDeferredPersistentRegions(st);
|
||||
resourceManager.flushUploadArena();
|
||||
if (transferThread != null && st.frameHighestEnqueuedSeq > transferThread.getSubmittedSeq()) {
|
||||
transferThread.awaitSubmittedUpTo(st.frameHighestEnqueuedSeq);
|
||||
}
|
||||
}
|
||||
|
||||
private static void serveFromShadow(ByteBuffer shadow, int srcOffset, ByteBuffer data, int rem) {
|
||||
final int dstPos = data.position();
|
||||
if (shadow.isDirect() && data.isDirect()) {
|
||||
@@ -3394,8 +3463,9 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
final PersistentMapping pm = resourceManager.getPersistentMapping(glId);
|
||||
final ContextState st = s();
|
||||
final boolean plainMapped = st.mappedStagingBuffer != null && st.mappedBufferGlId == glId;
|
||||
final MappedRange plain = s().mappingScratch;
|
||||
final boolean plainMapped = resourceManager.peekPlainMapping(glId, plain);
|
||||
plain.staging = null;
|
||||
|
||||
final boolean mapped;
|
||||
final int accessFlags;
|
||||
@@ -3408,9 +3478,9 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
mapLength = pm.length;
|
||||
} else if (plainMapped) {
|
||||
mapped = true;
|
||||
accessFlags = st.mappedAccessFlags;
|
||||
mapOffset = st.mappedOffset;
|
||||
mapLength = st.mappedLength;
|
||||
accessFlags = plain.accessFlags;
|
||||
mapOffset = plain.offset;
|
||||
mapLength = plain.length;
|
||||
} else {
|
||||
mapped = false;
|
||||
accessFlags = 0;
|
||||
@@ -3848,14 +3918,18 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
@Override public int createBuffers() { return resourceManager.genBuffer(); }
|
||||
@Override public void namedBufferData(int buffer, long size, int usage) {
|
||||
if (buffer == 0 || size <= 0) return;
|
||||
if (refuseImmutable(buffer, "glNamedBufferData on immutable storage")) return;
|
||||
final int sdlUsage = FormatMap.mapBufferUsage(GL15.GL_ARRAY_BUFFER);
|
||||
freePlainMappingFor(buffer, true);
|
||||
resourceManager.deleteBuffer(buffer);
|
||||
resourceManager.createBuffer(buffer, sdlUsage, size);
|
||||
resourceManager.recordBufferGlParams(buffer, usage, BufferParams.MUTABLE_STORE);
|
||||
}
|
||||
@Override public void namedBufferData(int buffer, ByteBuffer data, int usage) {
|
||||
if (buffer == 0 || data == null) return;
|
||||
if (refuseImmutable(buffer, "glNamedBufferData on immutable storage")) return;
|
||||
final int sdlUsage = FormatMap.mapBufferUsage(GL15.GL_ARRAY_BUFFER);
|
||||
freePlainMappingFor(buffer, true);
|
||||
resourceManager.deleteBuffer(buffer);
|
||||
final long handle = resourceManager.createBuffer(buffer, sdlUsage, data.remaining());
|
||||
resourceManager.recordBufferGlParams(buffer, usage, BufferParams.MUTABLE_STORE);
|
||||
@@ -4104,36 +4178,53 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
private static final int CLEAR_CHUNK_BYTES = 64 * 1024;
|
||||
private static final int FILL_CHUNK_BYTES = 4 * 1024 * 1024;
|
||||
|
||||
private static boolean isAllZero(ByteBuffer data, int elemBytes) {
|
||||
final int n = Math.min(elemBytes, data.remaining());
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (data.get(data.position() + i) != 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@Override public void clearBufferSubData(int target, int internalFormat, long offset, long size, int format, int type, ByteBuffer data) {
|
||||
final int glId = getBoundBuffer(target);
|
||||
if (glId == 0 || size <= 0) return;
|
||||
final long bufSize = resourceManager.getBufferSize(glId);
|
||||
if (offset < 0 || (bufSize > 0 && (offset > bufSize || size > bufSize - offset))) {
|
||||
warnInvalidBufferOp("glClearBufferSubData out of range", glId);
|
||||
return;
|
||||
}
|
||||
final long handle = resourceManager.getBufferHandle(glId);
|
||||
if (handle == 0 || frameManager.getCommandBuffer() == 0) return;
|
||||
|
||||
final int elemBytes = Math.max(1, PixelOps.glPixelSize(format, type));
|
||||
final int chunkBytes = (int) Math.min(size, CLEAR_CHUNK_BYTES);
|
||||
final boolean defer = s().deferUploads && target != GL43.GL_SHADER_STORAGE_BUFFER;
|
||||
// Undeferred clears stage one large pattern and upload it repeatedly
|
||||
final int chunkBytes = (int) Math.min(size, defer ? CLEAR_CHUNK_BYTES : FILL_CHUNK_BYTES);
|
||||
final int alignedChunk = (chunkBytes / elemBytes) * elemBytes;
|
||||
if (alignedChunk == 0) return;
|
||||
resourceManager.markBufferContentsDefined(glId);
|
||||
final ByteBuffer chunk = MemoryUtil.memAlloc(alignedChunk);
|
||||
final boolean zero = data == null || isAllZero(data, elemBytes);
|
||||
final ByteBuffer chunk = zero ? MemoryUtil.memCalloc(alignedChunk) : MemoryUtil.memAlloc(alignedChunk);
|
||||
try {
|
||||
for (int i = 0; i < alignedChunk; i++) {
|
||||
final byte v = (data != null && (i % elemBytes) < data.remaining())
|
||||
? data.get(data.position() + (i % elemBytes)) : 0;
|
||||
chunk.put(i, v);
|
||||
if (!zero) {
|
||||
for (int i = 0; i < alignedChunk; i++) {
|
||||
final byte v = (i % elemBytes) < data.remaining() ? data.get(data.position() + (i % elemBytes)) : 0;
|
||||
chunk.put(i, v);
|
||||
}
|
||||
}
|
||||
chunk.position(0).limit(alignedChunk);
|
||||
if (!defer) {
|
||||
resourceManager.fillBuffer(frameManager.ensureCopyPass(), chunk, handle, offset, size);
|
||||
return;
|
||||
}
|
||||
long remaining = size;
|
||||
long writeOffset = offset;
|
||||
final boolean defer = s().deferUploads && target != GL43.GL_SHADER_STORAGE_BUFFER;
|
||||
while (remaining > 0) {
|
||||
final int n = (int) Math.min(remaining, alignedChunk);
|
||||
chunk.position(0).limit(n);
|
||||
if (defer) {
|
||||
enqueuePreCopied(chunk, handle, writeOffset, false);
|
||||
} else {
|
||||
resourceManager.uploadToBuffer(frameManager.ensureCopyPass(), chunk, handle, writeOffset, false);
|
||||
}
|
||||
enqueuePreCopied(chunk, handle, writeOffset, false);
|
||||
remaining -= n;
|
||||
writeOffset += n;
|
||||
}
|
||||
@@ -4158,11 +4249,14 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
return;
|
||||
}
|
||||
|
||||
// Volume-sized staging, so it is only reached for 3D formats the compute clear cannot address.
|
||||
final long totalBytes = (long) mipW * mipH * mipD * bpp;
|
||||
if (totalBytes <= 0 || totalBytes > Integer.MAX_VALUE) return;
|
||||
final long cp = frameManager.ensureCopyPass();
|
||||
if (cp == 0) return;
|
||||
// A volume-sized staging buffer would allocate and upload each byte (1 GB for Euphoria Patches' floodfill!!)
|
||||
if (mipD > 1 && resourceManager.zeroTexture3D(cp, texHandle, mipW, mipH, mipD, level, bpp)) {
|
||||
return;
|
||||
}
|
||||
final long totalBytes = (long) mipW * mipH * mipD * bpp;
|
||||
if (totalBytes <= 0 || totalBytes > Integer.MAX_VALUE) return;
|
||||
final ByteBuffer zeros = MemoryUtil.memCalloc((int) totalBytes);
|
||||
try {
|
||||
if (mipD > 1) {
|
||||
@@ -4196,6 +4290,10 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
@Override public void memoryBarrier(int barriers) {
|
||||
// Graphics storage bindings are read-only on SDL_GPU; RW is compute-pass only
|
||||
frameManager.endCopyPassIfActive();
|
||||
// Replayed vertex image writes land when the render pass ends, and a barrier promises them to the draws after it
|
||||
if ((barriers & (GL42.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL42.GL_TEXTURE_FETCH_BARRIER_BIT)) != 0 && vertexWriteReplayer.hasPending()) {
|
||||
frameManager.endRenderPassIfActive(FrameManager.PASS_END_COMPUTE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void copyImageSubData(int srcName, int srcTarget, int srcLevel, int srcX, int srcY, int srcZ,
|
||||
@@ -4342,18 +4440,45 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
voxelizationDispatcher.endBatch(pass);
|
||||
}
|
||||
|
||||
@Override public void setVertexWriteReplay(int graphicsProgram, VertexWriteReplaySetup setup) {
|
||||
vertexWriteReplayer.setReplay(graphicsProgram, setup);
|
||||
}
|
||||
|
||||
@Override public boolean supportsComputeImageAtomics() {
|
||||
return device.supportsSpirv() || !device.supportsMsl();
|
||||
}
|
||||
|
||||
@Override public boolean supportsComputeImageAtomics(int width, int height, int depth) {
|
||||
return supportsComputeImageAtomics() || device.supportsImageAtomics(width, height, depth);
|
||||
}
|
||||
|
||||
private void prepareVertexWrites(ContextState st) {
|
||||
final ShaderManager.ProgramObject prog = st.boundProgramObj;
|
||||
if (prog != null && prog.vertexWriteReplay != null) vertexWriteReplayer.ensureImageUsage(prog.vertexWriteReplay);
|
||||
}
|
||||
|
||||
private void captureVertexWrites(ContextState st, int first, int count, int instances, int indexType, long indexOffset, int baseVertex) {
|
||||
final ShaderManager.ProgramObject prog = st.boundProgramObj;
|
||||
if (prog != null && prog.vertexWriteReplay != null) {
|
||||
vertexWriteReplayer.capture(st, first, count, instances, indexType, indexOffset, baseVertex);
|
||||
if (!frameManager.isRenderPassActive()) vertexWriteReplayer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawArraysInstanced(int mode, int first, int count, int primcount) {
|
||||
if (count <= 0 || primcount <= 0) return;
|
||||
final ContextState st = s();
|
||||
if (st.rasterizerDiscard) return;
|
||||
if (!frameManager.isFrameActive()) return;
|
||||
prepareVertexWrites(st);
|
||||
drawDispatch.setPrimitiveTypeForDraw(st, FormatMap.mapPrimitiveType(mode));
|
||||
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);
|
||||
SDL_DrawGPUPrimitives(frameManager.getRenderPass(), count, primcount, first, 0);
|
||||
captureVertexWrites(st, first, count, primcount, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -4464,8 +4589,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
long readOffset, size, dstHandle, writeOffset, seq;
|
||||
@Override
|
||||
public void accept(PersistentMapping src, PersistentMapping dst) {
|
||||
enqueueUpload(TransferThread.StagingReadUpload.acquire(src.staging, readOffset, size, dstHandle, writeOffset, seq, false));
|
||||
if (dst != null) PersistentBufferSync.mirrorPersistentCopy(src.staging, readOffset, dst.staging, writeOffset, size);
|
||||
enqueueUpload(TransferThread.StagingReadUpload.acquire(src.staging, src.stagingIndex(readOffset), size, dstHandle, writeOffset, seq, false));
|
||||
if (dst != null) PersistentBufferSync.mirrorPersistentCopy(src, readOffset, dst, writeOffset, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
+94
-19
@@ -45,6 +45,27 @@ public final class ComputeBinder implements ComputeDispatchSink {
|
||||
private final ShaderManager shaderManager;
|
||||
private final SamplerBinder samplerBinder;
|
||||
private boolean droppedDispatchWarned;
|
||||
private ShaderManager.ProgramObject lastUniformProgram;
|
||||
private long lastUniformCb;
|
||||
private long[] samplerOverrides;
|
||||
private long[] rwBufferOverrides;
|
||||
private ByteBuffer[] uniformBlockOverrides;
|
||||
|
||||
public void setSamplerOverrides(long[] byUnit) {
|
||||
samplerOverrides = byUnit;
|
||||
}
|
||||
|
||||
public void setReadWriteStorageBufferOverrides(long[] byGlSlot) {
|
||||
rwBufferOverrides = byGlSlot;
|
||||
}
|
||||
|
||||
public void setUniformBlockOverrides(ByteBuffer[] byIndex) {
|
||||
uniformBlockOverrides = byIndex;
|
||||
}
|
||||
|
||||
public long samplerHandle(int samplerObject, int glTexId) {
|
||||
return samplerBinder.getSampler(samplerObject, glTexId);
|
||||
}
|
||||
|
||||
public ComputeBinder(FrameManager frameManager, ResourceManager resourceManager, ShaderManager shaderManager, SamplerBinder samplerBinder) {
|
||||
this.frameManager = frameManager;
|
||||
@@ -153,6 +174,43 @@ public final class ComputeBinder implements ComputeDispatchSink {
|
||||
|
||||
@Override
|
||||
public long beginBatchedComputeDispatch(ContextState st, boolean cycleRwBuffers) {
|
||||
return beginComputePass(st, cycleRwBuffers, true);
|
||||
}
|
||||
|
||||
public long beginComputePassWithoutStorageBuffers(ContextState st) {
|
||||
return beginComputePass(st, false, false);
|
||||
}
|
||||
|
||||
public boolean bindRoStorageBufferHandles(long pass, ContextState st, long[] handlesByGlSlot) {
|
||||
if (pass == 0) return false;
|
||||
final ShaderManager.ProgramObject prog = shaderManager.getProgram(st.boundProgram);
|
||||
if (prog == null || !prog.linked) return false;
|
||||
final int[] slots = prog.computeBindingMap.roSsboGlSlots();
|
||||
if (slots.length == 0) return true;
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
final PointerBuffer ptrs = stack.mallocPointer(slots.length);
|
||||
for (int i = 0; i < slots.length; i++) {
|
||||
long handle = slots[i] < handlesByGlSlot.length ? handlesByGlSlot[slots[i]] : 0;
|
||||
if (handle == 0) handle = resourceManager.getOrCreateFallbackStorageBuffer();
|
||||
if (handle == 0) return false;
|
||||
ptrs.put(i, handle);
|
||||
}
|
||||
SDL_BindGPUComputeStorageBuffers(pass, 0, ptrs);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean rebindSamplers(long pass, ContextState st) {
|
||||
if (pass == 0) return false;
|
||||
final ShaderManager.ProgramObject prog = shaderManager.getProgram(st.boundProgram);
|
||||
if (prog == null || !prog.linked) return false;
|
||||
final ShaderManager.ComputeBindingMap map = prog.computeBindingMap;
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
return bindComputeSamplers(st, prog, pass, map.samplerGlSlots(), map.samplerNames(), stack);
|
||||
}
|
||||
}
|
||||
|
||||
private long beginComputePass(ContextState st, boolean cycleRwBuffers, boolean bindRoBuffers) {
|
||||
final int programId = st.boundProgram;
|
||||
if (programId == 0) return 0;
|
||||
final ShaderManager.ProgramObject prog = shaderManager.getProgram(programId);
|
||||
@@ -196,7 +254,7 @@ public final class ComputeBinder implements ComputeDispatchSink {
|
||||
|
||||
final boolean ok = bindComputeSamplers(st, prog, pass, samplerSlots, map.samplerNames(), stack)
|
||||
&& bindRoStorageTextures(st, prog, pass, map, stack)
|
||||
&& bindRoStorageBuffers(st, pass, roBufSlots, stack);
|
||||
&& (!bindRoBuffers || bindRoStorageBuffers(st, pass, roBufSlots, stack));
|
||||
if (!ok) {
|
||||
SDL_EndGPUComputePass(pass);
|
||||
frameManager.noteComputePassEnded();
|
||||
@@ -298,7 +356,7 @@ public final class ComputeBinder implements ComputeDispatchSink {
|
||||
private SDL_GPUStorageBufferReadWriteBinding.Buffer buildRwBufferBindings(ContextState st, int[] slots, MemoryStack stack, boolean cycle) {
|
||||
final SDL_GPUStorageBufferReadWriteBinding.Buffer buf = SDL_GPUStorageBufferReadWriteBinding.calloc(slots.length, stack);
|
||||
for (int i = 0; i < slots.length; i++) {
|
||||
final long handle = resolveStorageBufHandle(st, slots[i]);
|
||||
final long handle = rwBufferOverrides != null && slots[i] < rwBufferOverrides.length ? rwBufferOverrides[slots[i]] : resolveStorageBufHandle(st, slots[i]);
|
||||
|
||||
if (handle == 0) { warnMissingBinding(st, 1, "RW storage buffer slot", slots[i], true); return null; }
|
||||
buf.get(i).buffer(handle).cycle(cycle);
|
||||
@@ -319,7 +377,8 @@ public final class ComputeBinder implements ComputeDispatchSink {
|
||||
if (texHandle == 0) { warnMissingBinding(st, 2, "sampler texture unit", unit, true); return false; }
|
||||
warnMissingBinding(st, 2, "sampler texture unit", unit, false);
|
||||
}
|
||||
long sampHandle = samplerBinder.getSamplerForUnit(st, unit, texGlId);
|
||||
final long override = samplerOverrides != null && unit >= 0 && unit < samplerOverrides.length ? samplerOverrides[unit] : 0;
|
||||
long sampHandle = override != 0 ? override : samplerBinder.getSamplerForUnit(st, unit, texGlId);
|
||||
if (sampHandle == 0) {
|
||||
sampHandle = resourceManager.getOrCreateDefaultSampler();
|
||||
if (sampHandle == 0) { warnMissingBinding(st, 3, "sampler unit", unit, true); return false; }
|
||||
@@ -374,6 +433,28 @@ public final class ComputeBinder implements ComputeDispatchSink {
|
||||
return true;
|
||||
}
|
||||
|
||||
public ByteBuffer uniformBlockView(ContextState st, ShaderManager.ProgramObject prog, int i) {
|
||||
final ShaderManager.ComputeBindingMap map = prog.computeBindingMap;
|
||||
final int[] uboSlots = map.uboGlSlots();
|
||||
final int[] declaredSizes = map.uboSizes();
|
||||
if (i >= uboSlots.length) return null;
|
||||
final int slot = uboSlots[i];
|
||||
final int bufGlId = st.boundUboByIndex[slot];
|
||||
if (bufGlId == 0) return null;
|
||||
final ByteBuffer shadow = resourceManager.getUboShadow(bufGlId);
|
||||
if (shadow == null) return null;
|
||||
final int offset = st.uboRangeOffset[slot];
|
||||
if (offset < 0 || offset >= shadow.capacity()) return null;
|
||||
int size = (i < declaredSizes.length && declaredSizes[i] > 0) ? declaredSizes[i] : shadow.capacity() - offset;
|
||||
final int rangeSize = st.uboRangeSize[slot];
|
||||
if (rangeSize > 0) size = Math.min(size, rangeSize);
|
||||
size = Math.min(size, shadow.capacity() - offset);
|
||||
if (size <= 0) return null;
|
||||
final ByteBuffer view = frameManager.getUboPushView(bufGlId, shadow);
|
||||
view.position(offset).limit(offset + size);
|
||||
return view;
|
||||
}
|
||||
|
||||
private void pushComputeUboData(ContextState st, long cb, ShaderManager.ProgramObject prog, int[] uboSlots) {
|
||||
if (prog.lastComputeUboHash == null || prog.lastComputeUboHash.length != uboSlots.length) {
|
||||
prog.lastComputeUboHash = new long[uboSlots.length];
|
||||
@@ -381,16 +462,18 @@ public final class ComputeBinder implements ComputeDispatchSink {
|
||||
prog.lastComputeFrame = 0;
|
||||
}
|
||||
final long curFrame = frameManager.getFrameNumber();
|
||||
final boolean cacheValid = cb == prog.lastComputeCb && curFrame == prog.lastComputeFrame;
|
||||
final boolean cacheValid = cb == prog.lastComputeCb && curFrame == prog.lastComputeFrame
|
||||
&& lastUniformProgram == prog && lastUniformCb == cb;
|
||||
final long[] cache = prog.lastComputeUboHash;
|
||||
if (!cacheValid) {
|
||||
for (int i = 0; i < cache.length; i++) cache[i] = 0L;
|
||||
prog.lastComputeCb = cb;
|
||||
prog.lastComputeFrame = curFrame;
|
||||
}
|
||||
lastUniformProgram = prog;
|
||||
lastUniformCb = cb;
|
||||
final ShaderManager.ComputeBindingMap map = prog.computeBindingMap;
|
||||
final boolean[] isDefault = map.uboIsDefaultBlock();
|
||||
final int[] declaredSizes = map.uboSizes();
|
||||
final UniformStaging us = st.uniformStaging(prog);
|
||||
for (int i = 0; i < uboSlots.length; i++) {
|
||||
final ByteBuffer view;
|
||||
@@ -398,21 +481,13 @@ public final class ComputeBinder implements ComputeDispatchSink {
|
||||
if (us.fsUniformBuf == null || prog.fragmentUboSize <= 0) continue;
|
||||
view = us.fsUniformBuf;
|
||||
view.position(0).limit(prog.fragmentUboSize);
|
||||
} else if (uniformBlockOverrides != null && i < uniformBlockOverrides.length && uniformBlockOverrides[i] != null) {
|
||||
view = uniformBlockOverrides[i];
|
||||
view.position(0);
|
||||
if (!view.hasRemaining()) continue;
|
||||
} else {
|
||||
final int slot = uboSlots[i];
|
||||
final int bufGlId = st.boundUboByIndex[slot];
|
||||
if (bufGlId == 0) continue;
|
||||
final ByteBuffer shadow = resourceManager.getUboShadow(bufGlId);
|
||||
if (shadow == null) continue;
|
||||
final int offset = st.uboRangeOffset[slot];
|
||||
if (offset < 0 || offset >= shadow.capacity()) continue;
|
||||
int size = (i < declaredSizes.length && declaredSizes[i] > 0) ? declaredSizes[i] : shadow.capacity() - offset;
|
||||
final int rangeSize = st.uboRangeSize[slot];
|
||||
if (rangeSize > 0) size = Math.min(size, rangeSize);
|
||||
size = Math.min(size, shadow.capacity() - offset);
|
||||
if (size <= 0) continue;
|
||||
view = frameManager.getUboPushView(bufGlId, shadow);
|
||||
view.position(offset).limit(offset + size);
|
||||
view = uniformBlockView(st, prog, i);
|
||||
if (view == null) continue;
|
||||
}
|
||||
final int pos = view.position();
|
||||
final int len = view.remaining();
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.compute;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.cross.CrossCompileCache;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.msl.MslCrossCompile;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
|
||||
import org.lwjgl.opengl.GL43;
|
||||
import org.lwjgl.sdl.SDLError;
|
||||
import org.lwjgl.sdl.SDL_GPUComputePipelineCreateInfo;
|
||||
import org.lwjgl.sdl.SDL_GPUStorageTextureReadWriteBinding;
|
||||
import org.lwjgl.sdl.SDL_GPUTextureCreateInfo;
|
||||
import org.lwjgl.sdl.SDL_GPUTextureRegion;
|
||||
import org.lwjgl.sdl.SDL_GPUTextureTransferInfo;
|
||||
import org.lwjgl.sdl.SDL_GPUTransferBufferCreateInfo;
|
||||
import org.lwjgl.system.JNI;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
import org.lwjgl.system.macosx.ObjCRuntime;
|
||||
import org.lwjgl.util.shaderc.Shaderc;
|
||||
|
||||
import static org.lwjgl.sdl.SDLGPU.*;
|
||||
|
||||
public final class ImageAtomicsProbe {
|
||||
private static final Logger LOG = LogManager.getLogger("Angelica-SDLGPU");
|
||||
|
||||
private static final String SOURCE = "angelica:sdlgpu/atomic_probe.csh";
|
||||
public static final int MTL_LANGUAGE_VERSION_3_1 = (3 << 16) + 1;
|
||||
private static final int GROUPS = 64;
|
||||
|
||||
private final long device;
|
||||
private long pipeline;
|
||||
|
||||
public ImageAtomicsProbe(long device) {
|
||||
this.device = device;
|
||||
final SpirvCompiler.Result compiled = SpirvCompiler.compile(ShaderLoader.getShaderSource(SOURCE), Shaderc.shaderc_compute_shader, "atomic_probe", SpirvCompiler.Options.vulkanForced460Core(), false);
|
||||
if (compiled.spirv() == null) {
|
||||
LOG.warn("Metal image atomics: probe pipeline failed: {}", compiled.error());
|
||||
return;
|
||||
}
|
||||
CrossCompileCache.Output msl = null;
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
ShaderManager.remapSpirvForComputeSDLGPU(compiled.spirv());
|
||||
msl = MslCrossCompile.compileAtVersion(compiled.spirv(), GL43.GL_COMPUTE_SHADER, MslCrossCompile.SPVC_MSL_3_1);
|
||||
pipeline = SDL_CreateGPUComputePipeline(device, SDL_GPUComputePipelineCreateInfo.calloc(stack)
|
||||
.code(msl.code())
|
||||
.entrypoint(stack.ASCII(msl.entrypoint()))
|
||||
.format(SDL_GPU_SHADERFORMAT_MSL)
|
||||
.num_readwrite_storage_textures(1)
|
||||
.threadcount_x(InvocationDispatch.WORKGROUP_SIZE).threadcount_y(1).threadcount_z(1));
|
||||
if (pipeline == 0) LOG.warn("Metal image atomics: probe pipeline failed: {}", SDLError.SDL_GetError());
|
||||
} catch (RuntimeException e) {
|
||||
LOG.warn("Metal image atomics: probe pipeline failed", e);
|
||||
} finally {
|
||||
if (msl != null) MemoryUtil.memFree(msl.code());
|
||||
MemoryUtil.memFree(compiled.spirv());
|
||||
}
|
||||
}
|
||||
|
||||
public static int defaultMslVersion() {
|
||||
try {
|
||||
final long msgSend = ObjCRuntime.getLibrary().getFunctionAddress("objc_msgSend");
|
||||
final long cls = ObjCRuntime.objc_getClass("MTLCompileOptions");
|
||||
if (msgSend == 0 || cls == 0) return 0;
|
||||
final long options = JNI.invokePPP(cls, ObjCRuntime.sel_getUid("new"), msgSend);
|
||||
if (options == 0) return 0;
|
||||
final long version = JNI.invokePPP(options, ObjCRuntime.sel_getUid("languageVersion"), msgSend);
|
||||
JNI.invokePPV(options, ObjCRuntime.sel_getUid("release"), msgSend);
|
||||
return (int) version;
|
||||
} catch (RuntimeException | LinkageError e) {
|
||||
LOG.warn("Could not read the default Metal language version", e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean run(int width, int height) {
|
||||
if (pipeline == 0) return false;
|
||||
long texture = 0, upload = 0, download = 0, cb = 0;
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
texture = SDL_CreateGPUTexture(device, SDL_GPUTextureCreateInfo.calloc(stack)
|
||||
.type(SDL_GPU_TEXTURETYPE_2D)
|
||||
.format(SDL_GPU_TEXTUREFORMAT_R32_INT)
|
||||
.usage(SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE)
|
||||
.width(width).height(height).layer_count_or_depth(1).num_levels(1)
|
||||
.sample_count(SDL_GPU_SAMPLECOUNT_1));
|
||||
if (texture == 0) return failSdl(width, height, "texture");
|
||||
|
||||
upload = SDL_CreateGPUTransferBuffer(device, SDL_GPUTransferBufferCreateInfo.calloc(stack).usage(SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD).size(Integer.BYTES));
|
||||
download = SDL_CreateGPUTransferBuffer(device, SDL_GPUTransferBufferCreateInfo.calloc(stack).usage(SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD).size(Integer.BYTES));
|
||||
if (upload == 0 || download == 0) return failSdl(width, height, "transfer buffer");
|
||||
final long zero = nSDL_MapGPUTransferBuffer(device, upload, false);
|
||||
if (zero == 0) return failSdl(width, height, "map");
|
||||
MemoryUtil.memPutInt(zero, 0);
|
||||
SDL_UnmapGPUTransferBuffer(device, upload);
|
||||
|
||||
cb = SDL_AcquireGPUCommandBuffer(device);
|
||||
if (cb == 0) return failSdl(width, height, "command buffer");
|
||||
final SDL_GPUTextureRegion region = SDL_GPUTextureRegion.calloc(stack).texture(texture).w(1).h(1).d(1);
|
||||
long copy = SDL_BeginGPUCopyPass(cb);
|
||||
SDL_UploadToGPUTexture(copy, SDL_GPUTextureTransferInfo.calloc(stack).transfer_buffer(upload), region, false);
|
||||
SDL_EndGPUCopyPass(copy);
|
||||
|
||||
final SDL_GPUStorageTextureReadWriteBinding.Buffer binding = SDL_GPUStorageTextureReadWriteBinding.calloc(1, stack);
|
||||
binding.get(0).texture(texture);
|
||||
final long pass = SDL_BeginGPUComputePass(cb, binding, null);
|
||||
if (pass == 0) return failSdl(width, height, "compute pass");
|
||||
SDL_BindGPUComputePipeline(pass, pipeline);
|
||||
SDL_DispatchGPUCompute(pass, GROUPS, 1, 1);
|
||||
SDL_EndGPUComputePass(pass);
|
||||
|
||||
copy = SDL_BeginGPUCopyPass(cb);
|
||||
SDL_DownloadFromGPUTexture(copy, region, SDL_GPUTextureTransferInfo.calloc(stack).transfer_buffer(download));
|
||||
SDL_EndGPUCopyPass(copy);
|
||||
final boolean submitted = SDL_SubmitGPUCommandBuffer(cb);
|
||||
cb = 0;
|
||||
if (!submitted) return failSdl(width, height, "submit");
|
||||
SDL_WaitForGPUIdle(device);
|
||||
|
||||
final long result = nSDL_MapGPUTransferBuffer(device, download, false);
|
||||
if (result == 0) return failSdl(width, height, "map");
|
||||
final int adds = MemoryUtil.memGetInt(result);
|
||||
SDL_UnmapGPUTransferBuffer(device, download);
|
||||
|
||||
final int invocations = GROUPS * InvocationDispatch.WORKGROUP_SIZE;
|
||||
if (adds != invocations) LOG.info("Metal image atomics on {}x{}: {} invocations added {}", width, height, invocations, adds);
|
||||
return adds == invocations;
|
||||
} catch (RuntimeException e) {
|
||||
return fail(width, height, e.toString());
|
||||
} finally {
|
||||
if (cb != 0) SDL_CancelGPUCommandBuffer(cb);
|
||||
if (download != 0) SDL_ReleaseGPUTransferBuffer(device, download);
|
||||
if (upload != 0) SDL_ReleaseGPUTransferBuffer(device, upload);
|
||||
if (texture != 0) SDL_ReleaseGPUTexture(device, texture);
|
||||
}
|
||||
}
|
||||
|
||||
public void release() {
|
||||
if (pipeline != 0) SDL_ReleaseGPUComputePipeline(device, pipeline);
|
||||
pipeline = 0;
|
||||
}
|
||||
|
||||
private static boolean failSdl(int width, int height, String what) {
|
||||
return fail(width, height, what + " failed: " + SDLError.SDL_GetError());
|
||||
}
|
||||
|
||||
private static boolean fail(int width, int height, String reason) {
|
||||
LOG.warn("Metal image atomics on {}x{}: {}", width, height, reason);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.compute;
|
||||
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
|
||||
public final class InvocationDispatch {
|
||||
static final int MAX_GROUPS_PER_DISPATCH = 65535;
|
||||
static final int WORKGROUP_SIZE = 64;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface IntUniformWriter {
|
||||
void write(ContextState st, int location, int value);
|
||||
}
|
||||
|
||||
private InvocationDispatch() {}
|
||||
|
||||
static void dispatch(ComputeDispatchSink sink, ContextState st, long pass, long invocations, int invocationBaseLocation, IntUniformWriter writer) {
|
||||
final long groups = (invocations + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE;
|
||||
for (long g0 = 0; g0 < groups; g0 += MAX_GROUPS_PER_DISPATCH) {
|
||||
if (invocationBaseLocation >= 0) writer.write(st, invocationBaseLocation, (int) (g0 * WORKGROUP_SIZE));
|
||||
sink.pushPendingComputeUniforms(st);
|
||||
sink.dispatchInBatch(pass, (int) Math.min(MAX_GROUPS_PER_DISPATCH, groups - g0), 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
+635
@@ -0,0 +1,635 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.compute;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.backend.VertexWriteReplaySetup;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SDLGPURenderBackend;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineApplier;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.UniformStaging;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
import org.lwjgl.opengl.GL41;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Performs the image writes of a vertex shader that SDL-GPU strips (graphics stages may only read storage images).
|
||||
* Every draw made with a program that has a {@link Replay} is captured: its vertex and index buffers, attribute layout,
|
||||
* draw range, the uniform values the replay reads and the textures it samples. When the render pass ends, the replay
|
||||
* compute shader re-runs the vertex shader for each captured vertex and performs the writes.
|
||||
*/
|
||||
public final class VertexWriteReplayer {
|
||||
private static final Logger LOG = LogManager.getLogger("Angelica-SDLGPU");
|
||||
|
||||
private static final int MAX_CAPTURES = 1 << 16;
|
||||
private static final int[] COMPONENT_SIZE = { 4, 2, 1, 1, 2, 2, 4, 4, 4, 4, 4, 4 };
|
||||
private static final int FIRST_PACKED_TYPE = 9;
|
||||
private static final int PACKED_10F_11F_11F = 11;
|
||||
|
||||
private static final int ENABLED = 0x2000;
|
||||
private static final int BGRA = 0x4000;
|
||||
private static final int NO_ELEMENT = 0x8000;
|
||||
private static final int MAX_DIVISOR = 0xFFFF;
|
||||
private static final int UNCHANGED = -1;
|
||||
private static final int TEXTURE_UNITS = 32;
|
||||
private static final int SAMPLER_OBJECT_OF_DRAW = -1;
|
||||
|
||||
public static final class Replay {
|
||||
final int replayProgram;
|
||||
final VertexWriteReplaySetup setup;
|
||||
|
||||
private boolean resolved;
|
||||
private boolean broken;
|
||||
int[] inputLocations;
|
||||
int[] uniformSources;
|
||||
int[] uniformTargets;
|
||||
int[] samplerUnits;
|
||||
IntSupplier[] samplerSources;
|
||||
int[] samplerObjects;
|
||||
int[] packStorageSlots;
|
||||
int[] packRwStorageSlots;
|
||||
int[] uniformBlockIndices;
|
||||
int uniformBlockCount;
|
||||
int locAttr, locAttrDefault, locDraw, locIndex, locRestart, locInvocationBase;
|
||||
long[] lastHashes;
|
||||
boolean lastHashesApplied;
|
||||
|
||||
public Replay(VertexWriteReplaySetup setup) {
|
||||
this.replayProgram = setup.replayProgram();
|
||||
this.setup = setup;
|
||||
}
|
||||
|
||||
IntSupplier[] images() {
|
||||
return setup.images();
|
||||
}
|
||||
|
||||
boolean resolve(ShaderManager.ProgramObject graphics, ShaderManager.ProgramObject replay) {
|
||||
if (resolved) return !broken;
|
||||
resolved = true;
|
||||
|
||||
final String[] inputNames = setup.inputNames();
|
||||
inputLocations = new int[inputNames.length];
|
||||
for (int i = 0; i < inputNames.length; i++) {
|
||||
int loc = graphics.resolvedAttribLocations.getInt(inputNames[i]);
|
||||
if (loc < 0) loc = graphics.attribLocationBindings.getInt(inputNames[i]);
|
||||
if (loc < 0) loc = setup.inputLocations()[i];
|
||||
inputLocations[i] = loc;
|
||||
}
|
||||
|
||||
final IntArrayList sources = new IntArrayList();
|
||||
final IntArrayList targets = new IntArrayList();
|
||||
final List<String> samplerNames = new ArrayList<>();
|
||||
for (int target = 0; target < replay.uniformSlotCount; target++) {
|
||||
final String name = replay.locationName[target];
|
||||
if (name == null || name.startsWith("_vg_")) continue;
|
||||
if (replay.locationKind[target] == ShaderManager.ProgramObject.LOCATION_KIND_SAMPLER) {
|
||||
samplerNames.add(name);
|
||||
continue;
|
||||
}
|
||||
if (replay.locationKind[target] != ShaderManager.ProgramObject.LOCATION_KIND_PLAIN) continue;
|
||||
final int source = graphics.nameToLocation.getInt(name);
|
||||
if (source < 0) continue;
|
||||
sources.add(source);
|
||||
targets.add(target);
|
||||
}
|
||||
uniformSources = sources.toIntArray();
|
||||
uniformTargets = targets.toIntArray();
|
||||
lastHashes = new long[uniformSources.length];
|
||||
resolveSamplers(graphics, replay, samplerNames);
|
||||
|
||||
final IntArrayList storage = new IntArrayList();
|
||||
for (int slot : replay.computeBindingMap.roSsboGlSlots()) {
|
||||
final boolean vertexBuffer = slot >= setup.vertexBufferBinding() && slot < setup.vertexBufferBinding() + setup.vertexBufferCount();
|
||||
if (!vertexBuffer && slot != setup.indexBufferBinding()) storage.add(slot);
|
||||
}
|
||||
packStorageSlots = storage.toIntArray();
|
||||
packRwStorageSlots = replay.computeBindingMap.rwSsboGlSlots().clone();
|
||||
final IntArrayList blocks = new IntArrayList();
|
||||
final boolean[] isDefault = replay.computeBindingMap.uboIsDefaultBlock();
|
||||
for (int i = 0; i < replay.computeBindingMap.uboGlSlots().length; i++) {
|
||||
if (i >= isDefault.length || !isDefault[i]) blocks.add(i);
|
||||
}
|
||||
uniformBlockIndices = blocks.toIntArray();
|
||||
uniformBlockCount = replay.computeBindingMap.uboGlSlots().length;
|
||||
|
||||
locAttr = replay.nameToLocation.getInt("_vg_attr");
|
||||
locAttrDefault = replay.nameToLocation.getInt("_vg_attrDefault");
|
||||
locDraw = replay.nameToLocation.getInt("_vg_draw");
|
||||
locIndex = replay.nameToLocation.getInt("_vg_index");
|
||||
locRestart = replay.nameToLocation.getInt("_vg_restart");
|
||||
locInvocationBase = replay.nameToLocation.getInt("_vg_invocationBase");
|
||||
if (locDraw < 0 || locInvocationBase < 0) {
|
||||
broken = true;
|
||||
LOG.warn("Vertex write replay program {} is missing its dispatch uniforms; its writes are dropped", replayProgram);
|
||||
}
|
||||
return !broken;
|
||||
}
|
||||
|
||||
private void resolveSamplers(ShaderManager.ProgramObject graphics, ShaderManager.ProgramObject replay, List<String> names) {
|
||||
final IntArrayList units = new IntArrayList();
|
||||
final List<IntSupplier> sources = new ArrayList<>();
|
||||
final IntArrayList objects = new IntArrayList();
|
||||
final IntArrayList ownTextureIndices = new IntArrayList();
|
||||
final List<String> ownTextureNames = new ArrayList<>();
|
||||
final List<String> pipelineNames = Arrays.asList(setup.samplerNames());
|
||||
for (String name : names) {
|
||||
int unit = graphics.samplerTextureUnits.getInt(name);
|
||||
if (unit < 0) {
|
||||
final int i = pipelineNames.indexOf(name);
|
||||
if (i < 0) continue;
|
||||
if (setup.samplerTextures()[i] != null && setup.samplerUnits()[i] < 0) {
|
||||
ownTextureNames.add(name);
|
||||
ownTextureIndices.add(i);
|
||||
continue;
|
||||
}
|
||||
unit = setup.samplerUnits()[i];
|
||||
if (unit < 0) continue;
|
||||
}
|
||||
replay.samplerTextureUnits.put(name, unit);
|
||||
if (!units.contains(unit)) {
|
||||
units.add(unit);
|
||||
sources.add(null);
|
||||
objects.add(SAMPLER_OBJECT_OF_DRAW);
|
||||
}
|
||||
}
|
||||
int free = TEXTURE_UNITS - 1;
|
||||
for (int k = 0; k < ownTextureNames.size(); k++) {
|
||||
while (free >= 0 && units.contains(free)) free--;
|
||||
if (free < 0) break;
|
||||
final int i = ownTextureIndices.getInt(k);
|
||||
replay.samplerTextureUnits.put(ownTextureNames.get(k), free);
|
||||
units.add(free);
|
||||
sources.add(setup.samplerTextures()[i]);
|
||||
objects.add(setup.samplerObjects()[i]);
|
||||
}
|
||||
samplerUnits = units.toIntArray();
|
||||
samplerSources = sources.toArray(new IntSupplier[0]);
|
||||
samplerObjects = objects.toIntArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Capture {
|
||||
Replay replay;
|
||||
long[] buffers = new long[0];
|
||||
int bufferCount;
|
||||
long indexBuffer;
|
||||
float[] attr = new float[0];
|
||||
float[] attrDefault = new float[0];
|
||||
int[] samplerTextures = new int[0];
|
||||
long[] samplerHandles = new long[0];
|
||||
int[] imageTextures = new int[0];
|
||||
long[] packStorage = new long[0];
|
||||
long[] packRwStorage = new long[0];
|
||||
ByteBuffer[] uniformBlocks = new ByteBuffer[0];
|
||||
int first, count, instances, baseVertex, indexSize, indexOffset, indexCount;
|
||||
boolean restart;
|
||||
int restartIndex;
|
||||
}
|
||||
|
||||
private final ShaderManager shaderManager;
|
||||
private final ResourceManager resourceManager;
|
||||
private final PipelineApplier pipelineApplier;
|
||||
private final ComputeBinder computeBinder;
|
||||
private final Supplier<ContextState> state;
|
||||
|
||||
private final ArrayList<Capture> captures = new ArrayList<>();
|
||||
private int pending;
|
||||
private float[] uniformValues = new float[4096];
|
||||
private int[] uniformLengths = new int[256];
|
||||
private int uniformValueCount;
|
||||
private int uniformCount;
|
||||
private boolean flushing;
|
||||
private final long[] handlesByGlSlot = new long[ContextState.MAX_INDEXED_BUFFERS];
|
||||
private final long[] rwHandlesByGlSlot = new long[ContextState.MAX_INDEXED_BUFFERS];
|
||||
private final long[] samplerOverrides = new long[TEXTURE_UNITS];
|
||||
private final int[] savedImages = new int[ContextState.MAX_IMAGE_UNITS];
|
||||
private final int[] savedLevels = new int[ContextState.MAX_IMAGE_UNITS];
|
||||
private final int[] savedAccess = new int[ContextState.MAX_IMAGE_UNITS];
|
||||
private final int[] savedTextures = new int[TEXTURE_UNITS];
|
||||
private int uniformCursor;
|
||||
private int valueCursor;
|
||||
private final InvocationDispatch.IntUniformWriter invocationBaseWriter = (st, location, value) -> putIntVector(st, location, 1, value, 0, 0, 0);
|
||||
private final Set<String> warned = new HashSet<>();
|
||||
|
||||
public VertexWriteReplayer(ShaderManager shaderManager, ResourceManager resourceManager, PipelineApplier pipelineApplier, ComputeBinder computeBinder, Supplier<ContextState> state) {
|
||||
this.shaderManager = shaderManager;
|
||||
this.resourceManager = resourceManager;
|
||||
this.pipelineApplier = pipelineApplier;
|
||||
this.computeBinder = computeBinder;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public void setReplay(int graphicsProgram, VertexWriteReplaySetup setup) {
|
||||
final ShaderManager.ProgramObject prog = shaderManager.getProgram(graphicsProgram);
|
||||
if (prog == null) return;
|
||||
if (setup == null) {
|
||||
prog.vertexWriteReplay = null;
|
||||
return;
|
||||
}
|
||||
if (setup.images().length > ContextState.MAX_IMAGE_UNITS) {
|
||||
warnOnce("images:" + setup.replayProgram(), "Vertex write replay program " + setup.replayProgram() + " uses " + setup.images().length + " images, more than the " + ContextState.MAX_IMAGE_UNITS + " image units; its writes are dropped");
|
||||
prog.vertexWriteReplay = null;
|
||||
return;
|
||||
}
|
||||
prog.vertexWriteReplay = new Replay(setup);
|
||||
}
|
||||
|
||||
public void ensureImageUsage(Replay replay) {
|
||||
final IntSupplier[] images = replay.images();
|
||||
final int written = replay.setup.writtenImageCount();
|
||||
for (int i = 0; i < images.length; i++) {
|
||||
final int texture = images[i].getAsInt();
|
||||
if (texture != 0) resourceManager.ensureTextureUsage(texture, SDLGPURenderBackend.imageUsageForAccess(imageAccess(i, written)));
|
||||
}
|
||||
}
|
||||
|
||||
public void capture(ContextState st, int first, int count, int instances, int indexType, long indexOffset, int baseVertex) {
|
||||
final ShaderManager.ProgramObject graphics = st.boundProgramObj;
|
||||
final Replay replay = graphics.vertexWriteReplay;
|
||||
final ShaderManager.ProgramObject twin = shaderManager.getProgram(replay.replayProgram);
|
||||
if (twin == null || !twin.linked || twin.sdlComputePipeline == 0) return;
|
||||
if (!replay.resolve(graphics, twin)) return;
|
||||
if (count <= 0 || instances <= 0) return;
|
||||
if (pending == MAX_CAPTURES) {
|
||||
warnOnce("captures", "More than " + MAX_CAPTURES + " draws to replay vertex image writes for in one render pass; the rest are dropped");
|
||||
return;
|
||||
}
|
||||
|
||||
final IntSupplier[] images = replay.images();
|
||||
final Capture c = acquire();
|
||||
c.replay = replay;
|
||||
if (c.buffers.length != replay.setup.vertexBufferCount()) c.buffers = new long[replay.setup.vertexBufferCount()];
|
||||
c.bufferCount = 0;
|
||||
if (!captureInputs(st, replay, c)) return;
|
||||
|
||||
if (indexType != 0) {
|
||||
c.indexBuffer = resourceManager.getBufferHandle(st.currentVao.elementBuffer);
|
||||
c.indexSize = indexType == GL11.GL_UNSIGNED_BYTE ? 1 : indexType == GL11.GL_UNSIGNED_SHORT ? 2 : 4;
|
||||
c.indexOffset = (int) indexOffset;
|
||||
if (c.indexBuffer == 0) return;
|
||||
final long indexBytes = resourceManager.getBufferSize(st.currentVao.elementBuffer);
|
||||
c.indexCount = indexBytes <= 0 ? -1 : (int) Math.min(Math.max(0L, indexBytes - indexOffset) / c.indexSize, Integer.MAX_VALUE);
|
||||
c.restart = st.primitiveRestartEnabled && indexType != GL11.GL_UNSIGNED_BYTE;
|
||||
c.restartIndex = indexType == GL11.GL_UNSIGNED_SHORT ? st.primitiveRestartSentinel & 0xFFFF : st.primitiveRestartSentinel;
|
||||
} else {
|
||||
c.indexBuffer = 0;
|
||||
c.indexSize = 0;
|
||||
c.indexOffset = 0;
|
||||
c.indexCount = 0;
|
||||
c.restart = false;
|
||||
}
|
||||
c.first = first;
|
||||
c.count = count;
|
||||
c.instances = instances;
|
||||
c.baseVertex = baseVertex;
|
||||
|
||||
if (c.samplerTextures.length != replay.samplerUnits.length) {
|
||||
c.samplerTextures = new int[replay.samplerUnits.length];
|
||||
c.samplerHandles = new long[replay.samplerUnits.length];
|
||||
}
|
||||
for (int i = 0; i < replay.samplerUnits.length; i++) {
|
||||
final int unit = replay.samplerUnits[i];
|
||||
final IntSupplier source = replay.samplerSources[i];
|
||||
c.samplerTextures[i] = source != null ? source.getAsInt() : st.boundTextures[unit];
|
||||
final int samplerObject = replay.samplerObjects[i] != SAMPLER_OBJECT_OF_DRAW ? replay.samplerObjects[i] : st.boundSamplerObjects[unit];
|
||||
c.samplerHandles[i] = computeBinder.samplerHandle(samplerObject, c.samplerTextures[i]);
|
||||
}
|
||||
if (c.imageTextures.length != images.length) c.imageTextures = new int[images.length];
|
||||
for (int i = 0; i < images.length; i++) c.imageTextures[i] = images[i].getAsInt();
|
||||
if (c.packStorage.length != replay.packStorageSlots.length) c.packStorage = new long[replay.packStorageSlots.length];
|
||||
for (int i = 0; i < replay.packStorageSlots.length; i++) {
|
||||
c.packStorage[i] = resourceManager.getBufferHandle(st.boundSsboByIndex[replay.packStorageSlots[i]]);
|
||||
}
|
||||
if (c.packRwStorage.length != replay.packRwStorageSlots.length) c.packRwStorage = new long[replay.packRwStorageSlots.length];
|
||||
for (int i = 0; i < replay.packRwStorageSlots.length; i++) {
|
||||
c.packRwStorage[i] = resourceManager.getBufferHandle(st.boundSsboByIndex[replay.packRwStorageSlots[i]]);
|
||||
}
|
||||
captureUniformBlocks(st, twin, replay, c);
|
||||
|
||||
captureUniforms(st, graphics, replay);
|
||||
pending++;
|
||||
}
|
||||
|
||||
private boolean captureInputs(ContextState st, Replay replay, Capture c) {
|
||||
final int n = replay.inputLocations.length;
|
||||
if (c.attr.length < n * 4) {
|
||||
c.attr = new float[n * 4];
|
||||
c.attrDefault = new float[n * 4];
|
||||
}
|
||||
final ContextState.VAOState vao = st.currentVao;
|
||||
for (int k = 0; k < n; k++) {
|
||||
final int loc = replay.inputLocations[k];
|
||||
int x = 0, y = 0, z = 0, w = 0;
|
||||
if (loc >= 0 && loc < ContextState.MAX_VERTEX_ATTRIBS) {
|
||||
System.arraycopy(st.attribDefaults, loc * 4, c.attrDefault, k * 4, 4);
|
||||
if (vao.attribEnabled[loc]) {
|
||||
final int type = componentType(vao.attribType[loc]);
|
||||
final int binding = vao.attribBinding[loc];
|
||||
final int buffer = vao.bindingBuffer[binding];
|
||||
final long handle = type >= 0 ? resourceManager.getBufferHandle(buffer) : 0;
|
||||
if (type < 0) warnOnce("type:" + vao.attribType[loc], "Vertex write replay cannot read vertex attribute type 0x" + Integer.toHexString(vao.attribType[loc]) + "; that input reads its default value");
|
||||
if (handle != 0) {
|
||||
final int slot = bufferSlot(c, handle);
|
||||
if (slot < 0) {
|
||||
warnOnce("buffers", "A draw reads more than " + c.buffers.length + " vertex buffers; its vertex image writes are dropped");
|
||||
return false;
|
||||
}
|
||||
final boolean bgra = vao.attribSize[loc] == GL12.GL_BGRA;
|
||||
final int size = type == PACKED_10F_11F_11F ? 3 : bgra ? 4 : vao.attribSize[loc];
|
||||
final int elementBytes = type >= FIRST_PACKED_TYPE ? 4 : size * COMPONENT_SIZE[type];
|
||||
final int stride = vao.bindingStride[binding] != 0 ? vao.bindingStride[binding] : elementBytes;
|
||||
final long offset = vao.bindingOffset[binding] + vao.attribRelativeOffset[loc];
|
||||
final int divisor = Math.min(vao.bindingDivisor[binding], MAX_DIVISOR);
|
||||
final long bufferSize = resourceManager.getBufferSize(buffer);
|
||||
final boolean noElement = bufferSize > 0 && bufferSize < offset + elementBytes;
|
||||
x = slot | type << 4 | size << 8 | (vao.attribNormalized[loc] ? 0x800 : 0) | (vao.attribIsInteger[loc] ? 0x1000 : 0)
|
||||
| ENABLED | (bgra ? BGRA : 0) | (noElement ? NO_ELEMENT : 0) | divisor << 16;
|
||||
y = (int) offset;
|
||||
z = stride;
|
||||
w = lastElement(bufferSize, offset, elementBytes, stride);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.attrDefault[k * 4] = 0f;
|
||||
c.attrDefault[k * 4 + 1] = 0f;
|
||||
c.attrDefault[k * 4 + 2] = 0f;
|
||||
c.attrDefault[k * 4 + 3] = 1f;
|
||||
}
|
||||
c.attr[k * 4] = Float.intBitsToFloat(x);
|
||||
c.attr[k * 4 + 1] = Float.intBitsToFloat(y);
|
||||
c.attr[k * 4 + 2] = Float.intBitsToFloat(z);
|
||||
c.attr[k * 4 + 3] = Float.intBitsToFloat(w);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int lastElement(long bufferSize, long offset, int elementBytes, int stride) {
|
||||
if (bufferSize <= 0) return -1;
|
||||
final long room = bufferSize - offset - elementBytes;
|
||||
if (room < 0 || stride <= 0) return 0;
|
||||
return (int) Math.min(room / stride, 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
private static int bufferSlot(Capture c, long handle) {
|
||||
for (int i = 0; i < c.bufferCount; i++) {
|
||||
if (c.buffers[i] == handle) return i;
|
||||
}
|
||||
if (c.bufferCount == c.buffers.length) return -1;
|
||||
c.buffers[c.bufferCount] = handle;
|
||||
return c.bufferCount++;
|
||||
}
|
||||
|
||||
static int componentType(int glType) {
|
||||
return switch (glType) {
|
||||
case GL11.GL_FLOAT -> 0;
|
||||
case GL30.GL_HALF_FLOAT -> 1;
|
||||
case GL11.GL_BYTE -> 2;
|
||||
case GL11.GL_UNSIGNED_BYTE -> 3;
|
||||
case GL11.GL_SHORT -> 4;
|
||||
case GL11.GL_UNSIGNED_SHORT -> 5;
|
||||
case GL11.GL_INT -> 6;
|
||||
case GL11.GL_UNSIGNED_INT -> 7;
|
||||
case GL41.GL_FIXED -> 8;
|
||||
case GL33.GL_INT_2_10_10_10_REV -> 9;
|
||||
case GL12.GL_UNSIGNED_INT_2_10_10_10_REV -> 10;
|
||||
case GL30.GL_UNSIGNED_INT_10F_11F_11F_REV -> PACKED_10F_11F_11F;
|
||||
default -> -1;
|
||||
};
|
||||
}
|
||||
|
||||
private void captureUniformBlocks(ContextState st, ShaderManager.ProgramObject twin, Replay replay, Capture c) {
|
||||
if (c.uniformBlocks.length != replay.uniformBlockCount) c.uniformBlocks = new ByteBuffer[replay.uniformBlockCount];
|
||||
for (int index : replay.uniformBlockIndices) {
|
||||
final ByteBuffer view = computeBinder.uniformBlockView(st, twin, index);
|
||||
if (view == null) {
|
||||
if (c.uniformBlocks[index] != null) c.uniformBlocks[index].limit(0);
|
||||
continue;
|
||||
}
|
||||
ByteBuffer copy = c.uniformBlocks[index];
|
||||
if (copy == null || copy.capacity() < view.remaining()) copy = c.uniformBlocks[index] = ByteBuffer.allocateDirect(view.remaining()).order(ByteOrder.nativeOrder());
|
||||
copy.clear();
|
||||
copy.put(view);
|
||||
copy.flip();
|
||||
}
|
||||
}
|
||||
|
||||
private void captureUniforms(ContextState st, ShaderManager.ProgramObject graphics, Replay replay) {
|
||||
final UniformStaging us = st.uniformStaging(graphics);
|
||||
final long[] hashes = us.uniformValueHashBySlot;
|
||||
for (int u = 0; u < replay.uniformSources.length; u++) {
|
||||
final int source = replay.uniformSources[u];
|
||||
final float[] value = us.uniformDataBySlot != null && source < us.uniformDataBySlot.length ? us.uniformDataBySlot[source] : null;
|
||||
final long hash = hashes != null && source < hashes.length ? hashes[source] : 0L;
|
||||
if (uniformCount == uniformLengths.length) uniformLengths = Arrays.copyOf(uniformLengths, uniformLengths.length * 2);
|
||||
if (value != null && replay.lastHashesApplied && hash == replay.lastHashes[u]) {
|
||||
uniformLengths[uniformCount++] = UNCHANGED;
|
||||
continue;
|
||||
}
|
||||
replay.lastHashes[u] = hash;
|
||||
final int len = value == null ? 0 : value.length;
|
||||
uniformLengths[uniformCount++] = len;
|
||||
if (len == 0) continue;
|
||||
if (uniformValueCount + len > uniformValues.length) uniformValues = Arrays.copyOf(uniformValues, Math.max(uniformValues.length * 2, uniformValueCount + len));
|
||||
System.arraycopy(value, 0, uniformValues, uniformValueCount, len);
|
||||
uniformValueCount += len;
|
||||
}
|
||||
replay.lastHashesApplied = true;
|
||||
}
|
||||
|
||||
public boolean hasPending() {
|
||||
return pending > 0;
|
||||
}
|
||||
|
||||
public void noteIndirectDraw() {
|
||||
warnOnce("indirect", "An indirect draw used a program whose vertex shader writes images; those writes are dropped");
|
||||
}
|
||||
|
||||
private Capture acquire() {
|
||||
if (pending == captures.size()) captures.add(new Capture());
|
||||
return captures.get(pending);
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
if (pending == 0 || flushing) return;
|
||||
flushing = true;
|
||||
final ContextState st = state.get();
|
||||
final int savedProgram = st.boundProgram;
|
||||
final ShaderManager.ProgramObject savedProgramObj = st.boundProgramObj;
|
||||
try {
|
||||
uniformCursor = 0;
|
||||
valueCursor = 0;
|
||||
int start = 0;
|
||||
while (start < pending) {
|
||||
final Capture first = captures.get(start);
|
||||
final Replay replay = first.replay;
|
||||
int end = start;
|
||||
while (end < pending && captures.get(end).replay == replay && Arrays.equals(captures.get(end).imageTextures, first.imageTextures)
|
||||
&& Arrays.equals(captures.get(end).packRwStorage, first.packRwStorage)) end++;
|
||||
replayRun(st, replay, start, end);
|
||||
start = end;
|
||||
}
|
||||
} finally {
|
||||
st.boundProgram = savedProgram;
|
||||
st.boundProgramObj = savedProgramObj;
|
||||
for (int i = 0; i < pending; i++) captures.get(i).replay = null;
|
||||
pending = 0;
|
||||
uniformCount = 0;
|
||||
uniformValueCount = 0;
|
||||
flushing = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void replayRun(ContextState st, Replay replay, int start, int end) {
|
||||
final ShaderManager.ProgramObject twin = shaderManager.getProgram(replay.replayProgram);
|
||||
if (twin == null || !twin.linked || twin.sdlComputePipeline == 0) {
|
||||
skipUniforms(replay, start, end);
|
||||
return;
|
||||
}
|
||||
|
||||
final Capture first = captures.get(start);
|
||||
final int[] imageTextures = first.imageTextures;
|
||||
final int imageCount = imageTextures.length;
|
||||
final int written = replay.setup.writtenImageCount();
|
||||
System.arraycopy(st.boundStorageTextureByUnit, 0, savedImages, 0, imageCount);
|
||||
System.arraycopy(st.boundStorageTextureLevel, 0, savedLevels, 0, imageCount);
|
||||
System.arraycopy(st.boundStorageTextureAccess, 0, savedAccess, 0, imageCount);
|
||||
for (int i = 0; i < replay.samplerUnits.length; i++) savedTextures[i] = st.boundTextures[replay.samplerUnits[i]];
|
||||
computeBinder.setSamplerOverrides(samplerOverrides);
|
||||
Arrays.fill(rwHandlesByGlSlot, 0L);
|
||||
for (int i = 0; i < replay.packRwStorageSlots.length; i++) rwHandlesByGlSlot[replay.packRwStorageSlots[i]] = first.packRwStorage[i];
|
||||
computeBinder.setReadWriteStorageBufferOverrides(rwHandlesByGlSlot);
|
||||
|
||||
try {
|
||||
for (int i = 0; i < imageCount; i++) {
|
||||
st.boundStorageTextureByUnit[i] = imageTextures[i];
|
||||
st.boundStorageTextureLevel[i] = 0;
|
||||
st.boundStorageTextureAccess[i] = imageAccess(i, written);
|
||||
}
|
||||
applySamplerTextures(st, replay, captures.get(start));
|
||||
|
||||
st.boundProgram = replay.replayProgram;
|
||||
st.boundProgramObj = twin;
|
||||
final long pass = computeBinder.beginComputePassWithoutStorageBuffers(st);
|
||||
if (pass == 0) {
|
||||
for (int i = start; i < end; i++) applyUniforms(st, replay);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
for (int i = start; i < end; i++) {
|
||||
final Capture c = captures.get(i);
|
||||
if (i > start && applySamplerTextures(st, replay, c)) computeBinder.rebindSamplers(pass, st);
|
||||
dispatch(st, pass, replay, c);
|
||||
}
|
||||
} finally {
|
||||
computeBinder.endBatchedComputeDispatch(pass);
|
||||
}
|
||||
} finally {
|
||||
computeBinder.setReadWriteStorageBufferOverrides(null);
|
||||
computeBinder.setUniformBlockOverrides(null);
|
||||
System.arraycopy(savedImages, 0, st.boundStorageTextureByUnit, 0, imageCount);
|
||||
System.arraycopy(savedLevels, 0, st.boundStorageTextureLevel, 0, imageCount);
|
||||
System.arraycopy(savedAccess, 0, st.boundStorageTextureAccess, 0, imageCount);
|
||||
for (int i = 0; i < replay.samplerUnits.length; i++) {
|
||||
st.boundTextures[replay.samplerUnits[i]] = savedTextures[i];
|
||||
samplerOverrides[replay.samplerUnits[i]] = 0L;
|
||||
}
|
||||
computeBinder.setSamplerOverrides(null);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean applySamplerTextures(ContextState st, Replay replay, Capture c) {
|
||||
boolean changed = false;
|
||||
for (int i = 0; i < replay.samplerUnits.length; i++) {
|
||||
final int unit = replay.samplerUnits[i];
|
||||
if (st.boundTextures[unit] != c.samplerTextures[i] || samplerOverrides[unit] != c.samplerHandles[i]) {
|
||||
st.boundTextures[unit] = c.samplerTextures[i];
|
||||
samplerOverrides[unit] = c.samplerHandles[i];
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
private void skipUniforms(Replay replay, int start, int end) {
|
||||
replay.lastHashesApplied = false;
|
||||
for (int i = start; i < end; i++) {
|
||||
for (int u = 0; u < replay.uniformSources.length; u++) valueCursor += Math.max(0, uniformLengths[uniformCursor++]);
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatch(ContextState st, long pass, Replay replay, Capture c) {
|
||||
Arrays.fill(handlesByGlSlot, 0L);
|
||||
for (int i = 0; i < replay.packStorageSlots.length; i++) handlesByGlSlot[replay.packStorageSlots[i]] = c.packStorage[i];
|
||||
System.arraycopy(c.buffers, 0, handlesByGlSlot, replay.setup.vertexBufferBinding(), c.bufferCount);
|
||||
handlesByGlSlot[replay.setup.indexBufferBinding()] = c.indexBuffer;
|
||||
final boolean bound = computeBinder.bindRoStorageBufferHandles(pass, st, handlesByGlSlot);
|
||||
|
||||
applyUniforms(st, replay);
|
||||
if (!bound) return;
|
||||
|
||||
final int inputs = replay.inputLocations.length;
|
||||
putFloats(st, replay.locAttr, c.attr, inputs * 4);
|
||||
putFloats(st, replay.locAttrDefault, c.attrDefault, inputs * 4);
|
||||
putIntVector(st, replay.locDraw, 4, c.first, c.count, c.instances, c.baseVertex);
|
||||
putIntVector(st, replay.locIndex, 3, c.indexSize, c.indexOffset, c.indexCount, 0);
|
||||
putIntVector(st, replay.locRestart, 2, c.restart ? 1 : 0, c.restartIndex, 0, 0);
|
||||
computeBinder.setUniformBlockOverrides(c.uniformBlocks);
|
||||
|
||||
InvocationDispatch.dispatch(computeBinder, st, pass, (long) c.count * c.instances, replay.locInvocationBase, invocationBaseWriter);
|
||||
}
|
||||
|
||||
private void putFloats(ContextState st, int location, float[] source, int len) {
|
||||
if (location < 0 || len == 0) return;
|
||||
final float[] value = pipelineApplier.reuseOrAlloc(st, location, len);
|
||||
if (value == null) return;
|
||||
System.arraycopy(source, 0, value, 0, len);
|
||||
pipelineApplier.putUniform(st, location, value);
|
||||
}
|
||||
|
||||
private void applyUniforms(ContextState st, Replay replay) {
|
||||
for (int u = 0; u < replay.uniformSources.length; u++) {
|
||||
final int len = uniformLengths[uniformCursor++];
|
||||
if (len <= 0) continue;
|
||||
final float[] value = pipelineApplier.reuseOrAlloc(st, replay.uniformTargets[u], len);
|
||||
if (value != null) {
|
||||
System.arraycopy(uniformValues, valueCursor, value, 0, len);
|
||||
pipelineApplier.putUniform(st, replay.uniformTargets[u], value);
|
||||
}
|
||||
valueCursor += len;
|
||||
}
|
||||
}
|
||||
|
||||
private static int imageAccess(int index, int writtenImageCount) {
|
||||
return index < writtenImageCount ? GL15.GL_READ_WRITE : GL15.GL_READ_ONLY;
|
||||
}
|
||||
|
||||
private void putIntVector(ContextState st, int location, int count, int x, int y, int z, int w) {
|
||||
if (location < 0) return;
|
||||
final float[] value = pipelineApplier.reuseOrAlloc(st, location, count);
|
||||
if (value == null) return;
|
||||
value[0] = Float.intBitsToFloat(x);
|
||||
if (count > 1) value[1] = Float.intBitsToFloat(y);
|
||||
if (count > 2) value[2] = Float.intBitsToFloat(z);
|
||||
if (count > 3) value[3] = Float.intBitsToFloat(w);
|
||||
pipelineApplier.putUniform(st, location, value);
|
||||
}
|
||||
|
||||
private void warnOnce(String key, String message) {
|
||||
if (warned.add(key)) LOG.warn(message);
|
||||
}
|
||||
}
|
||||
+2
-8
@@ -4,8 +4,7 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
|
||||
public final class VoxelizationDispatcher {
|
||||
static final int MAX_GROUPS_PER_DISPATCH = 65535;
|
||||
private static final int WORKGROUP_SIZE = 64;
|
||||
private static final InvocationDispatch.IntUniformWriter UNIFORM_1I = (st, location, value) -> GLStateManager.glUniform1i(location, value);
|
||||
|
||||
private final ComputeDispatchSink computeBinder;
|
||||
|
||||
@@ -25,12 +24,7 @@ public final class VoxelizationDispatcher {
|
||||
if (locBase >= 0) GLStateManager.glUniform1i(locBase, rangeBase);
|
||||
if (locCount >= 0) GLStateManager.glUniform1i(locCount, rangeCount);
|
||||
if (locTotal >= 0) GLStateManager.glUniform1i(locTotal, vertexTotal);
|
||||
final int groups = (vertexTotal + WORKGROUP_SIZE - 1) / WORKGROUP_SIZE;
|
||||
for (int g0 = 0; g0 < groups; g0 += MAX_GROUPS_PER_DISPATCH) {
|
||||
if (locInvBase >= 0) GLStateManager.glUniform1i(locInvBase, g0 * WORKGROUP_SIZE);
|
||||
computeBinder.pushPendingComputeUniforms(st);
|
||||
computeBinder.dispatchInBatch(pass, Math.min(MAX_GROUPS_PER_DISPATCH, groups - g0), 1, 1);
|
||||
}
|
||||
InvocationDispatch.dispatch(computeBinder, st, pass, vertexTotal, locInvBase, UNIFORM_1I);
|
||||
}
|
||||
|
||||
public void endBatch(long pass) {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.device;
|
||||
|
||||
import com.gtnewhorizons.angelica.config.SystemProperties;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendOptions;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.MoltenVK;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.compute.ImageAtomicsProbe;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.util.DebugMessageRelay;
|
||||
import it.unimi.dsi.fastutil.longs.Long2BooleanOpenHashMap;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.sdl.SDLError;
|
||||
@@ -20,11 +24,12 @@ import org.lwjgl.system.Platform;
|
||||
import org.lwjglx.opengl.Display;
|
||||
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.lwjgl.sdl.SDLGPU.*;
|
||||
import static org.lwjgl.sdl.SDLHints.SDL_HINT_GPU_DRIVER;
|
||||
import static org.lwjgl.sdl.SDLHints.SDL_HINT_VULKAN_LIBRARY;
|
||||
import static org.lwjgl.sdl.SDLHints.SDL_SetHint;
|
||||
import static org.lwjgl.sdl.SDLVideo.*;
|
||||
import static org.lwjgl.system.MemoryUtil.memUTF8;
|
||||
@@ -46,6 +51,9 @@ public final class Device {
|
||||
private String driverName;
|
||||
private boolean fencePollEnabled;
|
||||
private boolean fenceQueryInverted;
|
||||
private int defaultMslVersion;
|
||||
private ImageAtomicsProbe imageAtomicsProbe;
|
||||
private final Long2BooleanOpenHashMap imageAtomicsBySize = new Long2BooleanOpenHashMap();
|
||||
private final FenceReleaser fenceReleaser = new FenceReleaser(fence -> device == 0 || FenceWait.isSignaled(this, fence), fence -> { if (device != 0) SDL_ReleaseGPUFence(device, fence); });
|
||||
private String deviceName;
|
||||
private String driverVersion;
|
||||
@@ -82,9 +90,23 @@ public final class Device {
|
||||
throw new GpuDeviceLostException(operation, err);
|
||||
}
|
||||
|
||||
// Default to vulkan for now until D3D is more tested
|
||||
static String resolveDriverName(String userHint, boolean windows) {
|
||||
return userHint.isEmpty() && windows ? "vulkan" : null;
|
||||
static String[] driverCandidates(String requested, Platform platform) {
|
||||
final String[] platformOrder = switch (platform) {
|
||||
case MACOSX -> new String[] { "metal", "vulkan" };
|
||||
case WINDOWS -> new String[] { "vulkan", "direct3d12" };
|
||||
default -> new String[] { "vulkan" };
|
||||
};
|
||||
if (requested.isEmpty()) return platformOrder;
|
||||
final List<String> candidates = new ArrayList<>(platformOrder.length + 1);
|
||||
candidates.add(requested);
|
||||
for (final String driver : platformOrder) {
|
||||
if (!driver.equalsIgnoreCase(requested)) candidates.add(driver);
|
||||
}
|
||||
return candidates.toArray(new String[0]);
|
||||
}
|
||||
|
||||
static boolean isVulkan(String driverName) {
|
||||
return "vulkan".equalsIgnoreCase(driverName);
|
||||
}
|
||||
|
||||
static boolean isMetal(String driverName) {
|
||||
@@ -103,17 +125,16 @@ public final class Device {
|
||||
final int ver = SDLVersion.SDL_GetVersion();
|
||||
LOG.info("SDL runtime version {}.{}.{} (raw {})", SDLVersion.SDL_VERSIONNUM_MAJOR(ver), SDLVersion.SDL_VERSIONNUM_MINOR(ver), SDLVersion.SDL_VERSIONNUM_MICRO(ver), ver);
|
||||
|
||||
final String driverHint = SystemProperties.SDL_GPU_DRIVER;
|
||||
if (!driverHint.isEmpty()) {
|
||||
SDL_SetHint(SDL_HINT_GPU_DRIVER, driverHint);
|
||||
LOG.info("SDL GPU driver hint set to '{}'", driverHint);
|
||||
final String requestedDriver = BackendOptions.sdlGpuDriver();
|
||||
final String[] candidates = driverCandidates(requestedDriver, Platform.get());
|
||||
if (!requestedDriver.isEmpty()) {
|
||||
LOG.info("SDL GPU driver requested: '{}'", requestedDriver);
|
||||
}
|
||||
|
||||
final boolean gpuDebug = SystemProperties.LWJGL_DEBUG || SystemProperties.SDL_GPU_DEBUG;
|
||||
final String forcedDriver = resolveDriverName(driverHint, Platform.get() == Platform.WINDOWS);
|
||||
device = createGPUDevice(gpuDebug, forcedDriver);
|
||||
device = createGPUDevice(gpuDebug, candidates);
|
||||
if (device == 0 && VideoDriverRecovery.shouldRetryOnX11(SDL_GetCurrentVideoDriver(), vulkanLoaderAvailable(), System.getenv("DISPLAY"))) {
|
||||
device = VideoDriverRecovery.retryUnderX11(() -> createGPUDevice(gpuDebug, forcedDriver));
|
||||
device = VideoDriverRecovery.retryUnderX11(() -> createGPUDevice(gpuDebug, candidates));
|
||||
}
|
||||
if (device == 0) {
|
||||
logDeviceCreationFailure(REQUESTED_SHADER_FORMATS);
|
||||
@@ -128,7 +149,7 @@ public final class Device {
|
||||
driverVersion = SDLProperties.SDL_GetStringProperty(props, SDL_PROP_GPU_DEVICE_DRIVER_VERSION_STRING, "");
|
||||
driverInfo = SDLProperties.SDL_GetStringProperty(props, SDL_PROP_GPU_DEVICE_DRIVER_INFO_STRING, "");
|
||||
|
||||
LOG.info("SDL GPU device created: gpu={}, driver={} {}, video={}, formats=0x{}, debug={}, forcedDriver={}", deviceName, driverName, driverVersion, SDL_GetCurrentVideoDriver(), Integer.toHexString(supportedShaderFormats), gpuDebug, forcedDriver);
|
||||
LOG.info("SDL GPU device created: gpu={}, driver={} {}, video={}, formats=0x{}, debug={}, requestedDriver={}", deviceName, driverName, driverVersion, SDL_GetCurrentVideoDriver(), Integer.toHexString(supportedShaderFormats), gpuDebug, requestedDriver);
|
||||
|
||||
if (metalNeedsNewerSdl(driverName, ver)) {
|
||||
LOG.error("SDL GPU on Metal requires SDL 3.4.6 or newer (have {}.{}.{}); falling back to OpenGL", SDLVersion.SDL_VERSIONNUM_MAJOR(ver), SDLVersion.SDL_VERSIONNUM_MINOR(ver), SDLVersion.SDL_VERSIONNUM_MICRO(ver));
|
||||
@@ -137,10 +158,36 @@ public final class Device {
|
||||
}
|
||||
|
||||
probeFences(ver);
|
||||
readMetalLanguageVersion();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void readMetalLanguageVersion() {
|
||||
if (!isMetal(driverName)) return;
|
||||
defaultMslVersion = ImageAtomicsProbe.defaultMslVersion();
|
||||
if (metalTextureAtomics()) {
|
||||
LOG.info("Metal image atomics: MSL 3.1 available, checked per image size");
|
||||
} else {
|
||||
LOG.info("Metal image atomics: unavailable, this Java runtime compiles Metal shaders as MSL {}.{} and they need 3.1. A runtime built with the macOS 14 SDK or newer enables them.", defaultMslVersion >>> 16, defaultMslVersion & 0xFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean metalTextureAtomics() {
|
||||
return defaultMslVersion >= ImageAtomicsProbe.MTL_LANGUAGE_VERSION_3_1;
|
||||
}
|
||||
|
||||
public boolean supportsImageAtomics(int width, int height, int depth) {
|
||||
if (!metalTextureAtomics() || width <= 0 || height <= 0 || depth > 1) return false;
|
||||
final long key = (long) width << 32 | height;
|
||||
if (imageAtomicsBySize.containsKey(key)) return imageAtomicsBySize.get(key);
|
||||
if (imageAtomicsProbe == null) imageAtomicsProbe = new ImageAtomicsProbe(device);
|
||||
final boolean ok = imageAtomicsProbe.run(width, height);
|
||||
if (ok) LOG.info("Metal image atomics on {}x{}: ok", width, height);
|
||||
imageAtomicsBySize.put(key, ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
private void probeFences(int ver) {
|
||||
fencePollEnabled = FenceWait.pollEnabled(driverName, ver);
|
||||
final boolean expectInverted = isMetal(driverName) && ver == SDLVersion.SDL_VERSIONNUM(3, 4, 14);
|
||||
@@ -151,13 +198,29 @@ public final class Device {
|
||||
}
|
||||
}
|
||||
|
||||
private long createGPUDevice(boolean gpuDebug, String forcedDriver) {
|
||||
long dev = SDL_CreateGPUDevice(REQUESTED_SHADER_FORMATS, gpuDebug, forcedDriver);
|
||||
if (dev == 0 && forcedDriver != null) {
|
||||
LOG.warn("Failed to create a '{}' SDL GPU device ({}); falling back to SDL's default backend. Set -D{} to pin one.", forcedDriver, SDLError.SDL_GetError(), SystemProperties.KEY_SDL_GPU_DRIVER);
|
||||
dev = SDL_CreateGPUDevice(REQUESTED_SHADER_FORMATS, gpuDebug, (CharSequence) null);
|
||||
private static void useMoltenVk() {
|
||||
final String path = MoltenVK.locate();
|
||||
if (path == null || MoltenVK.source() == MoltenVK.Source.ENV) return;
|
||||
if (SDL_SetHint(SDL_HINT_VULKAN_LIBRARY, path)) {
|
||||
LOG.info("MoltenVK: {}", path);
|
||||
} else {
|
||||
LOG.warn("Could not point SDL at MoltenVK {}: {}", path, SDLError.SDL_GetError());
|
||||
}
|
||||
return dev;
|
||||
}
|
||||
|
||||
private long createGPUDevice(boolean gpuDebug, String[] candidates) {
|
||||
final boolean macos = Platform.get() == Platform.MACOSX;
|
||||
for (final String driver : candidates) {
|
||||
final boolean macVulkan = macos && isVulkan(driver);
|
||||
if (macVulkan) useMoltenVk();
|
||||
final long dev = SDL_CreateGPUDevice(REQUESTED_SHADER_FORMATS, gpuDebug, driver);
|
||||
if (dev != 0) return dev;
|
||||
LOG.warn("Failed to create a '{}' SDL GPU device: {}", driver, SDLError.SDL_GetError());
|
||||
if (macVulkan && MoltenVK.locate() == null) {
|
||||
LOG.warn("Vulkan on macOS needs MoltenVK. Add {} to the mods folder or install MoltenVK.", MoltenVK.JAR_NAME);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void claimWindow(long window) {
|
||||
@@ -213,7 +276,7 @@ public final class Device {
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG.error("Continuing on OpenGL. Set -D{}=false to skip this probe entirely.", SystemProperties.KEY_USE_SDL_GPU);
|
||||
LOG.error("Continuing on OpenGL. Set Video Settings > Renderer > Backend to OpenGL, or -D{}=false, to skip this probe entirely.", SystemProperties.KEY_USE_SDL_GPU);
|
||||
}
|
||||
|
||||
public void destroyDevice() {
|
||||
@@ -224,11 +287,15 @@ public final class Device {
|
||||
claimed = false;
|
||||
}
|
||||
fenceReleaser.releaseAll();
|
||||
if (imageAtomicsProbe != null) imageAtomicsProbe.release();
|
||||
imageAtomicsProbe = null;
|
||||
SDL_DestroyGPUDevice(device);
|
||||
device = 0;
|
||||
driverName = null;
|
||||
fencePollEnabled = false;
|
||||
fenceQueryInverted = false;
|
||||
defaultMslVersion = 0;
|
||||
imageAtomicsBySize.clear();
|
||||
deviceName = null;
|
||||
driverVersion = null;
|
||||
driverInfo = null;
|
||||
|
||||
@@ -3,6 +3,7 @@ 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.MappedRange;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PackState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.LogicOpVariant;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
|
||||
@@ -314,12 +315,7 @@ public final class ContextState {
|
||||
public final long[] computeBatchRwTexs = new long[MAX_IMAGE_UNITS];
|
||||
public final int[] computeBatchRwTexLevels = new int[MAX_IMAGE_UNITS];
|
||||
|
||||
public int mappedBufferGlId;
|
||||
public ByteBuffer mappedStagingBuffer;
|
||||
public long mappedOffset;
|
||||
public long mappedLength;
|
||||
public boolean mappedInvalidate;
|
||||
public int mappedAccessFlags;
|
||||
public final MappedRange mappingScratch = new MappedRange();
|
||||
|
||||
public final SDL_GPUTextureSamplerBinding.Buffer fragSamplerBindings = SDL_GPUTextureSamplerBinding.calloc(MAX_SAMPLERS);
|
||||
public final SDL_GPUTextureSamplerBinding.Buffer vertSamplerBindings = SDL_GPUTextureSamplerBinding.calloc(MAX_SAMPLERS);
|
||||
|
||||
@@ -381,11 +381,14 @@ public final class FrameManager {
|
||||
|
||||
|
||||
private volatile Runnable preRenderPassHook;
|
||||
private volatile Runnable renderPassEndHook;
|
||||
private volatile Runnable beforeSubmit;
|
||||
private volatile Runnable afterPresent;
|
||||
|
||||
public void setPreRenderPassHook(Runnable hook) { this.preRenderPassHook = hook; }
|
||||
|
||||
public void setRenderPassEndHook(Runnable hook) { this.renderPassEndHook = hook; }
|
||||
|
||||
public void noteComputePassBegun() {
|
||||
final FrameState f = frame();
|
||||
f.computePassesThisFrame++;
|
||||
@@ -472,6 +475,8 @@ public final class FrameManager {
|
||||
f.currentColorTarget = 0;
|
||||
f.currentDepthTarget = 0;
|
||||
f.activeLayoutHash = 0;
|
||||
final Runnable hook = renderPassEndHook;
|
||||
if (hook != null) hook.run();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -173,8 +173,9 @@ public final class FFPTrace {
|
||||
sb.append(" | ffpUbo=buffer").append(glId).append(" noStaging");
|
||||
return;
|
||||
}
|
||||
final int base = st.uboRangeOffset[binding];
|
||||
sb.append(" | ffpUbo=buffer").append(glId).append(" base=").append(base);
|
||||
final int rangeOffset = st.uboRangeOffset[binding];
|
||||
final int base = pm != null ? (int) pm.stagingIndex(rangeOffset) : rangeOffset;
|
||||
sb.append(" | ffpUbo=buffer").append(glId).append(" base=").append(rangeOffset);
|
||||
appendUboFloat(buf, base, FFPUniformBlock.ALPHA_REF, " u_AlphaRef=");
|
||||
appendUboMatrix(buf, base, FFPUniformBlock.MVP_MATRIX, " u_MVPMatrix=");
|
||||
appendUboMatrix(buf, base, FFPUniformBlock.LIGHTMAP_TEXTURE_MATRIX, " u_LightmapTextureMatrix=");
|
||||
|
||||
+2
-2
@@ -698,8 +698,8 @@ public final class PipelineApplier {
|
||||
final int offset = st.uboRangeOffset[binding];
|
||||
final PersistentMapping pm = resourceManager.getPersistentMapping(glId);
|
||||
if (pm != null) {
|
||||
if (offset < 0 || offset + size > pm.staging.capacity()) return 0L;
|
||||
return MemoryUtil.memAddress0(pm.staging) + offset;
|
||||
if (!pm.covers(offset, size)) return 0L;
|
||||
return MemoryUtil.memAddress0(pm.staging) + pm.stagingIndex(offset);
|
||||
}
|
||||
final ByteBuffer shadow = resourceManager.getUboShadow(glId);
|
||||
if (shadow != null) {
|
||||
|
||||
+4
-7
@@ -8,16 +8,13 @@ public final class ByteRegionCopy {
|
||||
private ByteRegionCopy() {}
|
||||
|
||||
public static void copyByteRegion(ByteBuffer src, int srcOff, ByteBuffer dst, int dstOff, int len) {
|
||||
if ((srcOff | dstOff | len) < 0 || srcOff + (long) len > src.capacity() || dstOff + (long) len > dst.capacity()) {
|
||||
throw new IllegalStateException("copyByteRegion out of bounds: len=" + len + " srcOff=" + srcOff + " src.capacity=" + src.capacity() + " dstOff=" + dstOff + " dst.capacity=" + dst.capacity());
|
||||
}
|
||||
if (src.isDirect() && dst.isDirect()) {
|
||||
MemoryUtil.memCopy(MemoryUtil.memAddress(src, srcOff), MemoryUtil.memAddress(dst, dstOff), len);
|
||||
return;
|
||||
}
|
||||
if (src.hasArray() && dst.hasArray()) {
|
||||
System.arraycopy(src.array(), src.arrayOffset() + srcOff, dst.array(), dst.arrayOffset() + dstOff, len);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
dst.put(dstOff + i, src.get(srcOff + i));
|
||||
}
|
||||
dst.put(dstOff, src, srcOff, len);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.resource;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public final class MappedRange {
|
||||
public int glId;
|
||||
public ByteBuffer staging;
|
||||
public long offset;
|
||||
public long length;
|
||||
public boolean invalidate;
|
||||
public int accessFlags;
|
||||
}
|
||||
+35
-21
@@ -33,11 +33,14 @@ public final class PersistentBufferSync {
|
||||
this.sink = sink;
|
||||
}
|
||||
|
||||
public void onPersistentBufferWrite(int glId, long offset, long size) {
|
||||
public boolean onPersistentBufferWrite(int glId, long offset, long size) {
|
||||
final PersistentMapping pm = resourceManager.getPersistentMapping(glId);
|
||||
if (pm != null && pm.markDirty(offset, size)) {
|
||||
if (pm == null || size == 0) return true;
|
||||
if (offset < 0 || size < 0 || offset > pm.length || size > pm.length - offset) return false;
|
||||
if (pm.markDirty(offset, size)) {
|
||||
resourceManager.trackPersistentDirty();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void uploadDirtyPersistentRegion() { processDirtyPersistentRegions(false); }
|
||||
@@ -59,30 +62,41 @@ public final class PersistentBufferSync {
|
||||
for (int i = 0; i < n; i++) {
|
||||
final PersistentMapping pm = vals.get(i);
|
||||
if (!pm.isDirty()) continue;
|
||||
final long gpuHandle = resourceManager.getBufferHandle(keys.getInt(i));
|
||||
if (gpuHandle == 0) continue;
|
||||
final long claimed = pm.claimDirty();
|
||||
if (PersistentMapping.isClean(claimed)) continue;
|
||||
final long off = PersistentMapping.rangeOffset(claimed);
|
||||
final long size = PersistentMapping.rangeSize(claimed);
|
||||
if (Tracy.ENABLED) frameManager.notePersistentDrain();
|
||||
if (defer) {
|
||||
final long seq = sink.nextSeq();
|
||||
pm.lastEnqueuedSeq = seq;
|
||||
sink.enqueue(TransferThread.StagingReadUpload.acquire(pm.staging, off, size, gpuHandle, off, seq, false));
|
||||
enqueued++;
|
||||
} else {
|
||||
pm.staging.position((int) off).limit((int) (off + size));
|
||||
resourceManager.uploadToBuffer(frameManager.ensureCopyPass(), pm.staging, gpuHandle, off, false);
|
||||
pm.staging.clear();
|
||||
final int glId = keys.getInt(i);
|
||||
long copyPass = 0L;
|
||||
if (!defer) {
|
||||
if (resourceManager.getBufferHandle(glId) == 0) continue;
|
||||
copyPass = frameManager.ensureCopyPass();
|
||||
}
|
||||
resourceManager.beginPersistentDrain();
|
||||
try {
|
||||
if (resourceManager.getPersistentMapping(glId) != pm) continue;
|
||||
final long gpuHandle = resourceManager.getBufferHandle(glId);
|
||||
if (gpuHandle == 0) continue;
|
||||
final long claimed = pm.claimDirty();
|
||||
if (PersistentMapping.isClean(claimed)) continue;
|
||||
resourceManager.clearPersistentDirty();
|
||||
final long off = PersistentMapping.rangeOffset(claimed);
|
||||
final long size = PersistentMapping.rangeSize(claimed);
|
||||
if (Tracy.ENABLED) frameManager.notePersistentDrain();
|
||||
if (defer) {
|
||||
final long seq = sink.nextSeq();
|
||||
pm.lastEnqueuedSeq = seq;
|
||||
sink.enqueue(TransferThread.StagingReadUpload.acquire(pm.staging, off, size, gpuHandle, pm.offset + off, seq, false));
|
||||
enqueued++;
|
||||
} else {
|
||||
resourceManager.uploadRangeToBuffer(copyPass, pm.staging, off, size, gpuHandle, pm.offset + off, false);
|
||||
}
|
||||
} finally {
|
||||
resourceManager.endPersistentDrain();
|
||||
}
|
||||
resourceManager.clearPersistentDirty();
|
||||
}
|
||||
return enqueued;
|
||||
}
|
||||
|
||||
public static void mirrorPersistentCopy(ByteBuffer srcStaging, long srcOffset, ByteBuffer dstStaging, long dstOffset, long size) {
|
||||
ByteRegionCopy.copyByteRegion(srcStaging, (int) srcOffset, dstStaging, (int) dstOffset, (int) size);
|
||||
public static void mirrorPersistentCopy(PersistentMapping src, long readOffset, PersistentMapping dst, long writeOffset, long size) {
|
||||
if (!src.covers(readOffset, size) || !dst.covers(writeOffset, size)) return;
|
||||
ByteRegionCopy.copyByteRegion(src.staging, (int) src.stagingIndex(readOffset), dst.staging, (int) dst.stagingIndex(writeOffset), (int) size);
|
||||
}
|
||||
|
||||
public void mirrorEboShadow(int glId, ByteBuffer src, int dstOffset, int len) {
|
||||
|
||||
+6
@@ -24,6 +24,12 @@ public final class PersistentMapping {
|
||||
public static long rangeSize(long packed) { return (packed & 0xFFFFFFFFL) - (packed >>> 32); }
|
||||
static long packRange(long min, long max) { return (min << 32) | (max & 0xFFFFFFFFL); }
|
||||
|
||||
public long stagingIndex(long bufferOffset) { return bufferOffset - offset; }
|
||||
|
||||
public boolean covers(long bufferOffset, long size) {
|
||||
return size >= 0 && bufferOffset >= offset && bufferOffset - offset <= length && size <= length - (bufferOffset - offset);
|
||||
}
|
||||
|
||||
public boolean isDirty() { return dirtyRange.get() != CLEAN; }
|
||||
|
||||
public long claimDirty() { return dirtyRange.getAndSet(CLEAN); }
|
||||
|
||||
+207
-44
@@ -31,6 +31,7 @@ import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@@ -96,6 +97,9 @@ public final class ResourceManager {
|
||||
|
||||
private final Int2ObjectOpenHashMap<ContextState.VAOState> vaoStates = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2ObjectOpenHashMap<PersistentMapping> persistentMappings = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2ObjectOpenHashMap<MappedRange> plainMappings = new Int2ObjectOpenHashMap<>();
|
||||
private MappedRange[] plainMappingPool = new MappedRange[4];
|
||||
private int plainMappingPoolCount;
|
||||
private final Int2ObjectOpenHashMap<ByteBuffer> uboShadows = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2ObjectOpenHashMap<ByteBuffer> eboShadows = new Int2ObjectOpenHashMap<>();
|
||||
private final IntOpenHashSet eboShadowWanted = new IntOpenHashSet();
|
||||
@@ -244,6 +248,7 @@ public final class ResourceManager {
|
||||
}
|
||||
|
||||
private long createTextureCore(int glId, int glTarget, int sdlFormat, int glFormat, int width, int height, int depth, int levels, int usage) {
|
||||
if (getTextureHandle(glId) != 0) beforeTextureReplaced();
|
||||
usage = reconcileUsageAndWarn(glId, usage);
|
||||
final int sdlType = mapTextureType(glTarget);
|
||||
if (!SDL_GPUTextureSupportsFormat(device.getDevice(), sdlFormat, sdlType, usage)) {
|
||||
@@ -305,7 +310,19 @@ public final class ResourceManager {
|
||||
return sdlFormat;
|
||||
}
|
||||
|
||||
private Runnable textureReplaceHook;
|
||||
|
||||
public void setTextureReplaceHook(Runnable hook) {
|
||||
textureReplaceHook = hook;
|
||||
}
|
||||
|
||||
private void beforeTextureReplaced() {
|
||||
final Runnable hook = textureReplaceHook;
|
||||
if (hook != null) hook.run();
|
||||
}
|
||||
|
||||
public void deleteTexture(int glId) {
|
||||
beforeTextureReplaced();
|
||||
final long handle;
|
||||
wLock.lock();
|
||||
try {
|
||||
@@ -336,6 +353,7 @@ public final class ResourceManager {
|
||||
}
|
||||
|
||||
public void releaseTextureHandleForRealloc(int glId) {
|
||||
beforeTextureReplaced();
|
||||
final long handle;
|
||||
wLock.lock();
|
||||
try {
|
||||
@@ -752,6 +770,7 @@ public final class ResourceManager {
|
||||
|
||||
public void deleteBuffer(int glId) {
|
||||
final ByteBuffer droppedPboStaging;
|
||||
final ByteBuffer droppedPlainStaging;
|
||||
final PersistentMapping droppedPm;
|
||||
wLock.lock();
|
||||
try {
|
||||
@@ -763,6 +782,7 @@ public final class ResourceManager {
|
||||
undefinedContentBuffers.remove(glId);
|
||||
droppedPm = persistentMappings.remove(glId);
|
||||
if (droppedPm != null) mappingsVersion++;
|
||||
droppedPlainStaging = removePlainMappingLocked(glId);
|
||||
droppedPboStaging = pboStagingData.remove(glId);
|
||||
deleteUboShadow(glId);
|
||||
deleteEboShadow(glId);
|
||||
@@ -777,8 +797,11 @@ public final class ResourceManager {
|
||||
if (droppedPboStaging != null) {
|
||||
memFree(droppedPboStaging);
|
||||
}
|
||||
if (droppedPlainStaging != null) {
|
||||
memFree(droppedPlainStaging);
|
||||
}
|
||||
untrackPersistentDirty(droppedPm);
|
||||
releasePersistentStaging(droppedPm);
|
||||
releasePersistentStaging(droppedPm, glId, "delete");
|
||||
}
|
||||
|
||||
public void flushDeferredReleases() {
|
||||
@@ -953,10 +976,81 @@ public final class ResourceManager {
|
||||
try { vaoStates.remove(glId); } finally { wLock.unlock(); }
|
||||
}
|
||||
|
||||
public void putPersistentMapping(int bufferGlId, PersistentMapping m) {
|
||||
public boolean putPersistentMappingIfAbsent(int glId, PersistentMapping pm) {
|
||||
wLock.lock();
|
||||
try { persistentMappings.put(bufferGlId, m); mappingsVersion++; } finally { wLock.unlock(); }
|
||||
try {
|
||||
if (plainMappings.containsKey(glId) || persistentMappings.putIfAbsent(glId, pm) != null) return false;
|
||||
mappingsVersion++;
|
||||
return true;
|
||||
} finally { wLock.unlock(); }
|
||||
}
|
||||
|
||||
public boolean putPlainMappingIfAbsent(int glId, ByteBuffer staging, long offset, long length, boolean invalidate, int accessFlags) {
|
||||
wLock.lock();
|
||||
try {
|
||||
if (plainMappings.containsKey(glId) || persistentMappings.containsKey(glId)) return false;
|
||||
final MappedRange m;
|
||||
if (plainMappingPoolCount > 0) {
|
||||
m = plainMappingPool[--plainMappingPoolCount];
|
||||
plainMappingPool[plainMappingPoolCount] = null;
|
||||
} else {
|
||||
m = new MappedRange();
|
||||
}
|
||||
m.glId = glId;
|
||||
m.staging = staging;
|
||||
m.offset = offset;
|
||||
m.length = length;
|
||||
m.invalidate = invalidate;
|
||||
m.accessFlags = accessFlags;
|
||||
plainMappings.put(glId, m);
|
||||
return true;
|
||||
} finally { wLock.unlock(); }
|
||||
}
|
||||
|
||||
public boolean takePlainMapping(int glId, MappedRange out) {
|
||||
wLock.lock();
|
||||
try {
|
||||
final MappedRange m = plainMappings.get(glId);
|
||||
if (m == null) return false;
|
||||
copyMappedRange(m, out);
|
||||
removePlainMappingLocked(glId);
|
||||
return true;
|
||||
} finally { wLock.unlock(); }
|
||||
}
|
||||
|
||||
public ByteBuffer takePlainStaging(int glId) {
|
||||
wLock.lock();
|
||||
try { return removePlainMappingLocked(glId); } finally { wLock.unlock(); }
|
||||
}
|
||||
|
||||
public boolean peekPlainMapping(int glId, MappedRange out) {
|
||||
if (fastRead()) return copyMappedRange(plainMappings.get(glId), out);
|
||||
rLock.lock();
|
||||
try { return copyMappedRange(plainMappings.get(glId), out); } finally { rLock.unlock(); }
|
||||
}
|
||||
|
||||
private static boolean copyMappedRange(MappedRange m, MappedRange out) {
|
||||
if (m == null) return false;
|
||||
out.glId = m.glId;
|
||||
out.staging = m.staging;
|
||||
out.offset = m.offset;
|
||||
out.length = m.length;
|
||||
out.invalidate = m.invalidate;
|
||||
out.accessFlags = m.accessFlags;
|
||||
return true;
|
||||
}
|
||||
|
||||
private ByteBuffer removePlainMappingLocked(int glId) {
|
||||
final MappedRange m = plainMappings.remove(glId);
|
||||
if (m == null) return null;
|
||||
final ByteBuffer staging = m.staging;
|
||||
m.staging = null;
|
||||
if (plainMappingPoolCount == plainMappingPool.length) plainMappingPool = Arrays.copyOf(plainMappingPool, plainMappingPoolCount * 2);
|
||||
plainMappingPool[plainMappingPoolCount++] = m;
|
||||
return staging;
|
||||
}
|
||||
public void beginPersistentDrain() { rLock.lock(); }
|
||||
public void endPersistentDrain() { rLock.unlock(); }
|
||||
public PersistentMapping getPersistentMapping(int bufferGlId) {
|
||||
if (fastRead()) return persistentMappings.get(bufferGlId);
|
||||
rLock.lock();
|
||||
@@ -965,17 +1059,13 @@ public final class ResourceManager {
|
||||
public PersistentMapping removePersistentMapping(int bufferGlId) {
|
||||
final PersistentMapping dropped;
|
||||
wLock.lock();
|
||||
try { dropped = persistentMappings.remove(bufferGlId); mappingsVersion++; } finally { wLock.unlock(); }
|
||||
try {
|
||||
dropped = persistentMappings.remove(bufferGlId);
|
||||
if (dropped != null) mappingsVersion++;
|
||||
} finally { wLock.unlock(); }
|
||||
untrackPersistentDirty(dropped);
|
||||
return dropped;
|
||||
}
|
||||
public PersistentMapping swapPersistentMapping(int bufferGlId, PersistentMapping fresh) {
|
||||
final PersistentMapping prior;
|
||||
wLock.lock();
|
||||
try { prior = persistentMappings.put(bufferGlId, fresh); mappingsVersion++; } finally { wLock.unlock(); }
|
||||
untrackPersistentDirty(prior);
|
||||
return prior;
|
||||
}
|
||||
|
||||
private volatile int mappingsVersion;
|
||||
|
||||
@@ -989,8 +1079,9 @@ public final class ResourceManager {
|
||||
if (pm != null && !PersistentMapping.isClean(pm.claimDirty())) persistentDirtyCount.decrementAndGet();
|
||||
}
|
||||
|
||||
public void releasePersistentStaging(PersistentMapping pm) {
|
||||
public void releasePersistentStaging(PersistentMapping pm, int glId, String cause) {
|
||||
if (pm == null || pm.staging == null) return;
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Persistent mapping released: buffer {} ({} KiB) via {}", glId, pm.length >> 10, cause);
|
||||
final TransferThread tt = transferThread;
|
||||
final long seq = pm.lastEnqueuedSeq;
|
||||
if (tt != null && seq > tt.getSubmittedSeq()) tt.freeAfterSeq(pm.staging, seq);
|
||||
@@ -1001,24 +1092,20 @@ public final class ResourceManager {
|
||||
rLock.lock();
|
||||
try {
|
||||
final PersistentMapping srcPm = persistentMappings.get(srcGlId);
|
||||
if (srcPm == null) return false;
|
||||
final ByteBuffer slice = srcPm.staging.duplicate();
|
||||
slice.position((int) readOffset).limit((int) (readOffset + size));
|
||||
uploadToBuffer(copyPass, slice, dstHandle, writeOffset, false);
|
||||
if (srcPm == null || !srcPm.covers(readOffset, size)) return false;
|
||||
uploadRangeToBuffer(copyPass, srcPm.staging, srcPm.stagingIndex(readOffset), size, dstHandle, writeOffset, false);
|
||||
final PersistentMapping dstPm = persistentMappings.get(dstGlId);
|
||||
if (dstPm != null) {
|
||||
PersistentBufferSync.mirrorPersistentCopy(srcPm.staging, readOffset, dstPm.staging, writeOffset, size);
|
||||
}
|
||||
if (dstPm != null) PersistentBufferSync.mirrorPersistentCopy(srcPm, readOffset, dstPm, writeOffset, size);
|
||||
return true;
|
||||
} finally { rLock.unlock(); }
|
||||
}
|
||||
|
||||
public boolean enqueuePersistentCopyDeferred(int srcGlId, int dstGlId, long seq,
|
||||
public boolean enqueuePersistentCopyDeferred(int srcGlId, int dstGlId, long seq, long readOffset, long size,
|
||||
BiConsumer<PersistentMapping, PersistentMapping> inLock) {
|
||||
rLock.lock();
|
||||
try {
|
||||
final PersistentMapping srcPm = persistentMappings.get(srcGlId);
|
||||
if (srcPm == null) return false;
|
||||
if (srcPm == null || !srcPm.covers(readOffset, size)) return false;
|
||||
final PersistentMapping dstPm = persistentMappings.get(dstGlId);
|
||||
srcPm.lastEnqueuedSeq = seq;
|
||||
inLock.accept(srcPm, dstPm);
|
||||
@@ -1192,7 +1279,7 @@ public final class ResourceManager {
|
||||
public void mirrorArrayShadowFull(int glId, ByteBuffer data) {
|
||||
final int len = data.remaining();
|
||||
final ByteBuffer shadow = getOrAllocArrayShadow(glId, len);
|
||||
copyShadowRegion(data, data.position(), shadow, 0, len);
|
||||
ByteRegionCopy.copyByteRegion(data, data.position(), shadow, 0, len);
|
||||
shadow.position(0).limit(len);
|
||||
}
|
||||
|
||||
@@ -1206,7 +1293,7 @@ public final class ResourceManager {
|
||||
removeArrayShadowLocked(glId);
|
||||
return;
|
||||
}
|
||||
copyShadowRegion(data, data.position(), shadow, dstOffset, len);
|
||||
ByteRegionCopy.copyByteRegion(data, data.position(), shadow, dstOffset, len);
|
||||
} finally { wLock.unlock(); }
|
||||
}
|
||||
|
||||
@@ -1226,18 +1313,6 @@ public final class ResourceManager {
|
||||
arrayShadowWanted.remove(glId);
|
||||
}
|
||||
|
||||
private static void copyShadowRegion(ByteBuffer src, int srcOff, ByteBuffer dst, int dstOff, int len) {
|
||||
if (src.isDirect() && dst.isDirect()) {
|
||||
MemoryUtil.memCopy(MemoryUtil.memAddress(src) + srcOff, MemoryUtil.memAddress(dst) + dstOff, len);
|
||||
return;
|
||||
}
|
||||
final ByteBuffer s = src.duplicate();
|
||||
s.position(srcOff).limit(srcOff + len);
|
||||
final ByteBuffer d = dst.duplicate();
|
||||
d.position(dstOff);
|
||||
d.put(s);
|
||||
}
|
||||
|
||||
public EBOSplitScanner.EboSplit[] getOrScanSplits(int glId, int indexType, int sentinel) {
|
||||
if (!hasEboShadow(glId)) return null;
|
||||
final long version = getEboShadowVersion(glId);
|
||||
@@ -1502,10 +1577,10 @@ public final class ResourceManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static final int COPY_CALLSITE_UPLOAD_BUFFER = 1;
|
||||
public static final int COPY_CALLSITE_UPLOAD_TEX_BATCH = 2;
|
||||
public static final int COPY_CALLSITE_UPLOAD_TEX_DIRECT = 3;
|
||||
public static final int COPY_CALLSITE_ARENA = 4;
|
||||
public static final int COPY_CALLSITE_FILL_BUFFER = 5;
|
||||
|
||||
public boolean arenaUpload(ByteBuffer data, long dstHandle, long dstOffset, boolean cycle) {
|
||||
final FrameManager.FrameState f = frameManager.frame();
|
||||
@@ -1600,7 +1675,13 @@ public final class ResourceManager {
|
||||
}
|
||||
|
||||
public void uploadToBuffer(long copyPass, ByteBuffer data, long gpuBuffer, long offset, boolean cycle) {
|
||||
final long size = data.remaining();
|
||||
uploadRangeToBuffer(copyPass, data, data.position(), data.remaining(), gpuBuffer, offset, cycle);
|
||||
}
|
||||
|
||||
public void uploadRangeToBuffer(long copyPass, ByteBuffer src, long srcOff, long size, long gpuBuffer, long dstOff, boolean cycle) {
|
||||
if (srcOff < 0 || size < 0 || srcOff + size > src.capacity()) {
|
||||
throw new IllegalStateException("uploadRangeToBuffer: bad source range srcOff=" + srcOff + " size=" + size + " src.capacity=" + src.capacity());
|
||||
}
|
||||
final long xfer = acquireTransferBuffer(size);
|
||||
if (xfer == 0) return;
|
||||
|
||||
@@ -1609,28 +1690,102 @@ public final class ResourceManager {
|
||||
if (mapped == null || mapped.capacity() < size) {
|
||||
SDL_UnmapGPUTransferBuffer(device.getDevice(), xfer);
|
||||
returnTransferBuffer(xfer, size);
|
||||
throw new IllegalStateException("uploadToBuffer: bad mapping size=" + size + " mapSize=" + mapSize + " mapped=" + (mapped == null ? "null" : "cap=" + mapped.capacity()));
|
||||
throw new IllegalStateException("uploadRangeToBuffer: bad mapping size=" + size + " mapSize=" + mapSize + " mapped=" + (mapped == null ? "null" : "cap=" + mapped.capacity()));
|
||||
}
|
||||
if (src.isDirect()) {
|
||||
MemoryUtil.memCopy(MemoryUtil.memAddress0(src) + srcOff, MemoryUtil.memAddress0(mapped), size);
|
||||
} else {
|
||||
mapped.put(0, src, (int) srcOff, (int) size);
|
||||
}
|
||||
copyMappedFromData(mapped, data, (int) size, COPY_CALLSITE_UPLOAD_BUFFER);
|
||||
SDL_UnmapGPUTransferBuffer(device.getDevice(), xfer);
|
||||
|
||||
try (var stack = stackPush()) {
|
||||
final SDL_GPUTransferBufferLocation src = SDL_GPUTransferBufferLocation.calloc(stack)
|
||||
final SDL_GPUTransferBufferLocation srcLoc = SDL_GPUTransferBufferLocation.calloc(stack)
|
||||
.transfer_buffer(xfer)
|
||||
.offset(0);
|
||||
|
||||
final SDL_GPUBufferRegion dst = SDL_GPUBufferRegion.calloc(stack)
|
||||
.buffer(gpuBuffer)
|
||||
.offset((int) offset)
|
||||
.offset((int) dstOff)
|
||||
.size((int) size);
|
||||
|
||||
SDL_UploadToGPUBuffer(copyPass, src, dst, cycle);
|
||||
SDL_UploadToGPUBuffer(copyPass, srcLoc, dst, cycle);
|
||||
}
|
||||
|
||||
frameManager.recordUploadCommands(size, 1);
|
||||
returnTransferBuffer(xfer, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills {@code size} bytes of a buffer by staging {@code pattern} once and uploading it repeatedly. Staging every
|
||||
* byte instead maps a transfer buffer per chunk, which stalls for seconds on the hundreds of megabytes some shader
|
||||
* packs allocate as storage buffers.
|
||||
*/
|
||||
public void fillBuffer(long copyPass, ByteBuffer pattern, long gpuBuffer, long offset, long size) {
|
||||
final int chunk = pattern.remaining();
|
||||
if (chunk <= 0 || size <= 0) return;
|
||||
final long xfer = acquireTransferBuffer(chunk);
|
||||
if (xfer == 0) return;
|
||||
try {
|
||||
final ByteBuffer mapped = SDL_MapGPUTransferBuffer(device.getDevice(), xfer, true, getTransferBufferMapSize(chunk));
|
||||
if (mapped == null || mapped.capacity() < chunk) {
|
||||
SDL_UnmapGPUTransferBuffer(device.getDevice(), xfer);
|
||||
throw new IllegalStateException("fillBuffer: bad mapping size=" + chunk + " mapped=" + (mapped == null ? "null" : "cap=" + mapped.capacity()));
|
||||
}
|
||||
copyMappedFromData(mapped, pattern, chunk, COPY_CALLSITE_FILL_BUFFER);
|
||||
SDL_UnmapGPUTransferBuffer(device.getDevice(), xfer);
|
||||
|
||||
int uploads = 0;
|
||||
try (var stack = stackPush()) {
|
||||
final SDL_GPUTransferBufferLocation src = SDL_GPUTransferBufferLocation.calloc(stack).transfer_buffer(xfer).offset(0);
|
||||
final SDL_GPUBufferRegion dst = SDL_GPUBufferRegion.calloc(stack).buffer(gpuBuffer);
|
||||
for (long done = 0; done < size; done += chunk) {
|
||||
dst.offset((int) (offset + done)).size((int) Math.min(chunk, size - done));
|
||||
SDL_UploadToGPUBuffer(copyPass, src, dst, false);
|
||||
uploads++;
|
||||
}
|
||||
}
|
||||
frameManager.recordUploadCommands(size, uploads);
|
||||
} finally {
|
||||
returnTransferBuffer(xfer, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean zeroTexture3D(long copyPass, long gpuTexture, int w, int h, int d, int level, int bytesPerTexel) {
|
||||
final long sliceBytes = (long) w * h * bytesPerTexel;
|
||||
if (sliceBytes <= 0 || sliceBytes > Integer.MAX_VALUE) return false;
|
||||
final long xfer = acquireTransferBuffer(sliceBytes);
|
||||
if (xfer == 0) return false;
|
||||
try {
|
||||
final ByteBuffer mapped = SDL_MapGPUTransferBuffer(device.getDevice(), xfer, true, getTransferBufferMapSize(sliceBytes));
|
||||
if (mapped == null || mapped.capacity() < sliceBytes) {
|
||||
SDL_UnmapGPUTransferBuffer(device.getDevice(), xfer);
|
||||
return false;
|
||||
}
|
||||
MemoryUtil.memSet(MemoryUtil.memAddress(mapped), 0, sliceBytes);
|
||||
SDL_UnmapGPUTransferBuffer(device.getDevice(), xfer);
|
||||
|
||||
flushBatchedUploads(copyPass);
|
||||
try (var stack = stackPush()) {
|
||||
final SDL_GPUTextureTransferInfo src = SDL_GPUTextureTransferInfo.calloc(stack).transfer_buffer(xfer).offset(0);
|
||||
final SDL_GPUTextureRegion dst = SDL_GPUTextureRegion.calloc(stack)
|
||||
.texture(gpuTexture)
|
||||
.mip_level(level)
|
||||
.x(0).y(0)
|
||||
.w(w).h(h).d(1);
|
||||
for (int z = 0; z < d; z++) {
|
||||
dst.z(z);
|
||||
SDL_UploadToGPUTexture(copyPass, src, dst, false);
|
||||
}
|
||||
}
|
||||
markTextureContentDefined(gpuTexture);
|
||||
frameManager.recordUploadCommands(sliceBytes * d, d);
|
||||
return true;
|
||||
} finally {
|
||||
returnTransferBuffer(xfer, sliceBytes);
|
||||
}
|
||||
}
|
||||
|
||||
public static final int BATCH_SEGMENT_CAPACITY = 4 * 1024 * 1024;
|
||||
|
||||
public void uploadToTexture(long copyPass, ByteBuffer data, long gpuTexture, int x, int y, int w, int h, int level) {
|
||||
@@ -2506,12 +2661,20 @@ public final class ResourceManager {
|
||||
arrayShadowWanted.clear();
|
||||
for (ByteBuffer bb : pboStagingData.values()) memFree(bb);
|
||||
pboStagingData.clear();
|
||||
for (PersistentMapping pm : persistentMappings.values()) {
|
||||
for (var e : persistentMappings.int2ObjectEntrySet()) {
|
||||
final PersistentMapping pm = e.getValue();
|
||||
untrackPersistentDirty(pm);
|
||||
releasePersistentStaging(pm);
|
||||
releasePersistentStaging(pm, e.getIntKey(), "shutdown");
|
||||
}
|
||||
persistentMappings.clear();
|
||||
mappingsVersion++;
|
||||
for (MappedRange m : plainMappings.values()) {
|
||||
if (m.staging != null) memFree(m.staging);
|
||||
m.staging = null;
|
||||
}
|
||||
plainMappings.clear();
|
||||
Arrays.fill(plainMappingPool, null);
|
||||
plainMappingPoolCount = 0;
|
||||
}
|
||||
|
||||
public record TextureMeta(int glTarget, int glFormat, int sdlFormat, int width, int height, int depth, int levels, int usage) {}
|
||||
|
||||
+9
-5
@@ -329,11 +329,15 @@ public final class TransferThread {
|
||||
final ByteBuffer mapped = SDL_MapGPUTransferBuffer(device.getDevice(), xfer, true, mapSize);
|
||||
if (mapped != null) {
|
||||
final ByteBuffer src = sr.stagingBuffer;
|
||||
final int prevPos = src.position();
|
||||
final int prevLim = src.limit();
|
||||
src.limit((int) (sr.srcOffset + size)).position((int) sr.srcOffset);
|
||||
mapped.put(src);
|
||||
src.limit(prevLim).position(prevPos);
|
||||
if (sr.srcOffset < 0 || sr.srcOffset + size > src.capacity() || size > mapped.capacity()) {
|
||||
LOG.error("StagingReadUpload out of bounds: srcOffset={} size={} src.capacity={} mapped.capacity={}; upload dropped", sr.srcOffset, size, src.capacity(), mapped.capacity());
|
||||
SDL_UnmapGPUTransferBuffer(device.getDevice(), xfer);
|
||||
resourceManager.returnTransferBufferThreadSafe(xfer, size);
|
||||
retireSeq(sr.seq);
|
||||
StagingReadUpload.release(sr);
|
||||
return;
|
||||
}
|
||||
MemoryUtil.memCopy(MemoryUtil.memAddress0(src) + sr.srcOffset, MemoryUtil.memAddress0(mapped), size);
|
||||
}
|
||||
SDL_UnmapGPUTransferBuffer(device.getDevice(), xfer);
|
||||
recordBufferUpload(xfer, size, sr.dstGpuBuffer, sr.dstOffset, sr.seq, sr.cycle);
|
||||
|
||||
Loaded 100 of 267 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user