mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-15 01:34:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
713509a87b | ||
|
|
102c6cad86 | ||
|
|
8af9340be6 | ||
|
|
fc8d3e8b89 | ||
|
|
e84c8c8d7a | ||
|
|
73d61d385c | ||
|
|
69124e3f2f | ||
|
|
98703a11be | ||
|
|
67ca46a220 | ||
|
|
a037a96fa4 | ||
|
|
ed260a34c4 | ||
|
|
e98c927a93 | ||
|
|
b446968e66 | ||
|
|
32f8eef15b | ||
|
|
2d9d1a45b7 | ||
|
|
fa8842a64b | ||
|
|
69019b9a62 | ||
|
|
482dece0f7 | ||
|
|
ab3bf17696 | ||
|
|
cfc6e12e00 | ||
|
|
8537835d0c | ||
|
|
8e1809622c | ||
|
|
f81ee4b03a | ||
|
|
49db8e111d | ||
|
|
bd9a3c3c5c | ||
|
|
01ff612d1d |
No files matched your search
@@ -9,7 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@05e7cd7ce0ea015acda8c10c1c39fb3a20a49e60
|
||||
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@master
|
||||
with:
|
||||
disable-spotless-pr: true
|
||||
java-distribution: temurin
|
||||
|
||||
@@ -170,7 +170,13 @@ val archScanAsm: Configuration by configurations.creating {
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
val glsmRedirectAgent: Configuration by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
glsmRedirectAgent(project(":glsm", "redirectAgent"))
|
||||
archScanAsm(libs.asm.tree.test)
|
||||
archScanAsm(libs.asm.commons.test)
|
||||
}
|
||||
@@ -212,6 +218,8 @@ val glCoreTest by tasks.registering(Test::class) {
|
||||
.plus(downgraded(downgradeRootMainClasses))
|
||||
.plus(sourceSets["test"].runtimeClasspath.minus(rawMain).minus(rawTest))
|
||||
|
||||
javaAgent(glsmRedirectAgent)
|
||||
|
||||
configureAngelicaJava8()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.process.CommandLineArgumentProvider
|
||||
|
||||
fun Test.javaAgent(jar: FileCollection) {
|
||||
inputs.files(jar).withPropertyName("javaAgent")
|
||||
jvmArgumentProviders.add(CommandLineArgumentProvider { listOf("-javaagent:${jar.singleFile.path}") })
|
||||
}
|
||||
@@ -64,7 +64,7 @@ DependencyHandlerScope.of(dependencies).apply {
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:BuildCraft:7.1.63:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Botania:1.13.32-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:LogisticsPipes:1.5.32-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:OpenComputers:1.12.55-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:OpenComputers:1.12.64-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Mobs-Info:0.5.18-GTNH:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:IguanaTweaksTConstruct:2.7.10:dev")
|
||||
runtimeOnlyNonPublishable("com.github.GTNewHorizons:OpenBlocks:1.12.18-GTNH:dev")
|
||||
|
||||
@@ -267,6 +267,22 @@ tasks.test {
|
||||
configureGlsmJava8()
|
||||
}
|
||||
|
||||
val redirectAgentJar by tasks.registering(Jar::class) {
|
||||
archiveClassifier = "redirect-agent"
|
||||
manifest {
|
||||
attributes("Premain-Class" to "com.gtnewhorizons.angelica.glsm.GlsmRedirectAgent")
|
||||
}
|
||||
}
|
||||
|
||||
val redirectAgent by configurations.creating {
|
||||
isCanBeConsumed = true
|
||||
isCanBeResolved = false
|
||||
}
|
||||
|
||||
artifacts {
|
||||
add(redirectAgent.name, redirectAgentJar)
|
||||
}
|
||||
|
||||
val glTestTasks = GL_TASK_TAGS.map { (taskName, tag) ->
|
||||
tasks.register<Test>(taskName) {
|
||||
description = "Runs the $tag GL tests in their own JVM, so they get their own Display and GL profile."
|
||||
@@ -275,6 +291,7 @@ val glTestTasks = GL_TASK_TAGS.map { (taskName, tag) ->
|
||||
includeTags = setOf(tag)
|
||||
}
|
||||
configureGlsmJava8()
|
||||
javaAgent(files(redirectAgentJar))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FragmentKey;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.Uniforms;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.FogStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.LightStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState;
|
||||
@@ -66,7 +68,13 @@ public class CompatUniformManager {
|
||||
static final int LOC_IRIS_MODELVIEW_INVERSE = LOC_MAT_BASE + MAT_FIELDS; // 48
|
||||
static final int LOC_IRIS_PROJECTION_INVERSE = LOC_IRIS_MODELVIEW_INVERSE + 1; // 49
|
||||
|
||||
static final int LOC_COUNT = LOC_IRIS_PROJECTION_INVERSE + 1; // 50
|
||||
static final int LOC_FOG_MODE = LOC_IRIS_PROJECTION_INVERSE + 1; // 50
|
||||
static final int LOC_FOG_PARAMS = LOC_FOG_MODE + 1; // 51
|
||||
static final int LOC_FOG_DISTANCE_MODE = LOC_FOG_PARAMS + 1; // 52
|
||||
|
||||
static final int LOC_MODELVIEW_PROJECTION = LOC_FOG_DISTANCE_MODE + 1; // 53
|
||||
|
||||
static final int LOC_COUNT = LOC_MODELVIEW_PROJECTION + 1; // 54
|
||||
|
||||
private static final String[] LIGHT_FIELD_NAMES = {
|
||||
"ambient", "diffuse", "specular", "position", "halfVector",
|
||||
@@ -84,6 +92,7 @@ public class CompatUniformManager {
|
||||
UNIFORM_NAMES[LOC_MODELVIEW] = "angelica_ModelViewMatrix";
|
||||
UNIFORM_NAMES[LOC_MODELVIEW_INVERSE] = "angelica_ModelViewMatrixInverse";
|
||||
UNIFORM_NAMES[LOC_PROJECTION] = "angelica_ProjectionMatrix";
|
||||
UNIFORM_NAMES[LOC_MODELVIEW_PROJECTION] = "angelica_ModelViewProjectionMatrix";
|
||||
UNIFORM_NAMES[LOC_PROJECTION_INVERSE] = "angelica_ProjectionMatrixInverse";
|
||||
UNIFORM_NAMES[LOC_NORMAL] = "angelica_NormalMatrix";
|
||||
UNIFORM_NAMES[LOC_LIGHTMAP_TEXTURE_MATRIX] = "angelica_LightmapTextureMatrix";
|
||||
@@ -98,6 +107,9 @@ public class CompatUniformManager {
|
||||
UNIFORM_NAMES[LOC_IRIS_TEXTURE_MATRIX] = "iris_TextureMatrix";
|
||||
UNIFORM_NAMES[LOC_IRIS_MODELVIEW_INVERSE] = "iris_ModelViewMatrixInverse";
|
||||
UNIFORM_NAMES[LOC_IRIS_PROJECTION_INVERSE] = "iris_ProjectionMatrixInverse";
|
||||
UNIFORM_NAMES[LOC_FOG_MODE] = "angelica_FogMode";
|
||||
UNIFORM_NAMES[LOC_FOG_PARAMS] = "angelica_FogParams";
|
||||
UNIFORM_NAMES[LOC_FOG_DISTANCE_MODE] = "angelica_FogDistanceMode";
|
||||
UNIFORM_NAMES[LOC_ALPHA_TEST_REF] = "angelica_currentAlphaTest";
|
||||
UNIFORM_NAMES[LOC_SCENE_COLOR] = "angelica_SceneColor";
|
||||
UNIFORM_NAMES[LOC_CLIP_PLANES] = "angelica_ClipPlane[0]";
|
||||
@@ -120,7 +132,14 @@ public class CompatUniformManager {
|
||||
GLContextState owner;
|
||||
int lastMvGen = -1, lastMvLinearGen = -1, lastProjGen = -1, lastTexMatGen = -1;
|
||||
int lastFragmentGen = -1, lastLightingGen = -1, lastClipPlaneGen = -1;
|
||||
ProgramUniforms(int[] locs) { this.locs = locs; }
|
||||
int lastFogMode = -1, lastFogDistanceMode = -1;
|
||||
final boolean lights;
|
||||
ProgramUniforms(int[] locs) {
|
||||
this.locs = locs;
|
||||
boolean any = false;
|
||||
for (int i = LOC_LIGHT_BASE; i < LOC_MAT_BASE; i++) any |= locs[i] != -1;
|
||||
this.lights = any;
|
||||
}
|
||||
}
|
||||
|
||||
// Reusable NIO buffers for upload
|
||||
@@ -186,6 +205,7 @@ public class CompatUniformManager {
|
||||
pu.owner = glCtx;
|
||||
pu.lastMvGen = pu.lastMvLinearGen = pu.lastProjGen = pu.lastTexMatGen = -1;
|
||||
pu.lastFragmentGen = pu.lastLightingGen = pu.lastClipPlaneGen = -1;
|
||||
pu.lastFogMode = pu.lastFogDistanceMode = -1;
|
||||
}
|
||||
|
||||
// Matrix uniforms — skip if this program's storage already holds the current generation
|
||||
@@ -212,12 +232,15 @@ public class CompatUniformManager {
|
||||
uploadFragmentUniforms(locs);
|
||||
}
|
||||
|
||||
pu.lastFogMode = uploadIfChanged(locs[LOC_FOG_MODE], FragmentKey.activeFogMode(glCtx), pu.lastFogMode);
|
||||
pu.lastFogDistanceMode = uploadIfChanged(locs[LOC_FOG_DISTANCE_MODE], glCtx.fogState.getFogDistanceMode(), pu.lastFogDistanceMode);
|
||||
|
||||
// Lighting-derived uniforms (scene color, light sources, material)
|
||||
final int litGen = glCtx.lightingGeneration;
|
||||
if (litGen != pu.lastLightingGen) {
|
||||
pu.lastLightingGen = litGen;
|
||||
if (locs[LOC_SCENE_COLOR] != -1) uploadSceneColor(locs);
|
||||
uploadLightSources(locs);
|
||||
if (pu.lights) uploadLightSources(locs);
|
||||
uploadMaterial(locs);
|
||||
}
|
||||
|
||||
@@ -262,6 +285,11 @@ public class CompatUniformManager {
|
||||
}
|
||||
}
|
||||
|
||||
if ((mvChanged || projChanged) && locs[LOC_MODELVIEW_PROJECTION] != -1) {
|
||||
Uniforms.modelViewProjection(proj, mv, scratchMatrix).get(mat4Buf);
|
||||
RENDER_BACKEND.uniformMatrix4(locs[LOC_MODELVIEW_PROJECTION], false, mat4Buf);
|
||||
}
|
||||
|
||||
if (mvLinearChanged) {
|
||||
// Normal Matrix (inverse-transpose of upper-left 3x3 of ModelView)
|
||||
if (locs[LOC_NORMAL] != -1 || locs[LOC_IRIS_NORMAL] != -1) {
|
||||
@@ -324,6 +352,11 @@ public class CompatUniformManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static int uploadIfChanged(int location, int value, int last) {
|
||||
if (location != -1 && value != last) RENDER_BACKEND.uniform1i(location, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static void uploadFragmentUniforms(int[] locs) {
|
||||
// Fog uniforms
|
||||
FogStateStack fog = GLStateManager.getFogState();
|
||||
@@ -336,6 +369,9 @@ public class CompatUniformManager {
|
||||
if (locs[LOC_FOG_END] != -1) {
|
||||
RENDER_BACKEND.uniform1f(locs[LOC_FOG_END], fog.getEnd());
|
||||
}
|
||||
if (locs[LOC_FOG_PARAMS] != -1) {
|
||||
RENDER_BACKEND.uniform4f(locs[LOC_FOG_PARAMS], fog.linearScale(), fog.linearBias(), fog.expScale(), fog.exp2Scale());
|
||||
}
|
||||
if (locs[LOC_FOG_COLOR] != -1) {
|
||||
vec4Buf.clear();
|
||||
vec4Buf.put((float) fog.getFogColor().x).put((float) fog.getFogColor().y).put((float) fog.getFogColor().z).put(fog.getFogAlpha());
|
||||
@@ -372,8 +408,6 @@ public class CompatUniformManager {
|
||||
}
|
||||
|
||||
private static void uploadLightSources(int[] locs) {
|
||||
// Early exit: if the first light field isn't present, no lighting uniforms exist
|
||||
if (locs[LOC_LIGHT_BASE] == -1) return;
|
||||
final LightStateStack[] lights = GLStateManager.getLightDataStates();
|
||||
for (int li = 0; li < 2; li++) {
|
||||
final int base = LOC_LIGHT_BASE + li * LIGHT_FIELDS;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.states.Color4;
|
||||
import com.gtnewhorizons.angelica.glsm.states.ColorMask;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public final class ExitState {
|
||||
public static final int POLYGON_OFFSET_ENABLE_WRITTEN = 1;
|
||||
public static final int POLYGON_OFFSET_VALUES_WRITTEN = 2;
|
||||
final Values entry = new Values();
|
||||
final Values value = new Values();
|
||||
final MaterialState frontMaterial = new MaterialState(GL11.GL_FRONT);
|
||||
final MaterialState backMaterial = new MaterialState(GL11.GL_BACK);
|
||||
long writes, dependencies;
|
||||
boolean valid;
|
||||
int textureDeleteCount;
|
||||
|
||||
public void clear() {
|
||||
valid = false;
|
||||
writes = dependencies = 0;
|
||||
}
|
||||
|
||||
static final class Values {
|
||||
final Color4 color = new Color4();
|
||||
final Vector3f normal = new Vector3f();
|
||||
final Vector4f texCoord = new Vector4f();
|
||||
final Vector4f shaderColor = new Vector4f();
|
||||
final Vector4f overlayColor = new Vector4f();
|
||||
final BlendState blend = new BlendState();
|
||||
final AlphaState alpha = new AlphaState();
|
||||
final ColorMask colorMask = new ColorMask();
|
||||
final Matrix4f textureMatrix = new Matrix4f();
|
||||
long enabled;
|
||||
int activeUnit, clientActiveUnit, matrixMode, texture, depthFunction, cullFace, frontFace, shadeModel;
|
||||
int materialFace, materialParameter;
|
||||
boolean depthMask;
|
||||
float lightmapX, lightmapY, offsetFactor, offsetUnits, offsetClamp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,597 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredAlphaHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredDepthColorHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.CowStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.states.MaterialState;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Matrix4fStack;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Records the GL state a block of rendering code leaves behind, so a later run of the same block can be skipped and
|
||||
* its exit state applied instead.
|
||||
*/
|
||||
final class ExitStateCapture {
|
||||
// Fields 0..12 are the capabilities, in CAPS order. FRONT_* and BACK_* are four contiguous fields each, ordered like
|
||||
// vector(): apply() and finish() address them as 15L << FRONT_AMBIENT and (field - FRONT_AMBIENT) & 3.
|
||||
static final int BLEND = 0, ALPHA = 1, DEPTH = 2, CULL = 3, LIGHTING = 4, NORMALIZE = 5, RESCALE = 6, LIGHT0 = 7, LIGHT1 = 8, TEXTURE0 = 9, TEXTURE1 = 10, OFFSET = 11, COLOR_MATERIAL = 12, COLOR = 13, NORMAL = 14, TEXCOORD = 15,
|
||||
LIGHTMAP = 16, SHADER_COLOR = 17, BLEND_FUNC = 18, ALPHA_FUNC = 19, DEPTH_FUNC = 20, DEPTH_MASK = 21, COLOR_MASK = 22, CULL_FACE = 23, FRONT_FACE = 24, SHADE_MODEL = 25, ACTIVE_UNIT = 26, TEXTURE_BINDING = 27, MATRIX_MODE = 28,
|
||||
TEXTURE_MATRIX = 29, OFFSET_VALUES = 30, COLOR_MATERIAL_SETTINGS = 31, FRONT_AMBIENT = 32, FRONT_DIFFUSE = 33, FRONT_SPECULAR = 34, FRONT_EMISSION = 35, BACK_AMBIENT = 36, BACK_DIFFUSE = 37, BACK_SPECULAR = 38,
|
||||
BACK_EMISSION = 39, OVERLAY_COLOR = 40, CLIENT_ACTIVE_UNIT = 41;
|
||||
private static final int FIELD_COUNT = 42;
|
||||
private static final long IMPLICIT_INPUTS = bits(ACTIVE_UNIT, MATRIX_MODE);
|
||||
private static final long MATERIAL_BAKE_INPUTS = bits(COLOR, COLOR_MATERIAL_SETTINGS);
|
||||
private static final long DRAW_INPUTS = bits(CULL, TEXTURE0, TEXTURE1, COLOR, LIGHTMAP, OVERLAY_COLOR, CULL_FACE, FRONT_FACE, TEXTURE_BINDING);
|
||||
private static final long VERTEX_INPUTS = bits(NORMAL, TEXCOORD);
|
||||
private static final long MATERIAL_INPUTS = bits(BLEND, ALPHA, DEPTH, BLEND_FUNC, ALPHA_FUNC, DEPTH_FUNC, DEPTH_MASK, COLOR_MASK);
|
||||
private static final int[] CAPS = {GL11.GL_BLEND, GL11.GL_ALPHA_TEST, GL11.GL_DEPTH_TEST, GL11.GL_CULL_FACE, GL11.GL_LIGHTING, GL11.GL_NORMALIZE, GL12.GL_RESCALE_NORMAL, GL11.GL_LIGHT0, GL11.GL_LIGHT1, GL11.GL_TEXTURE_2D,
|
||||
GL11.GL_TEXTURE_2D, GL11.GL_POLYGON_OFFSET_FILL, GL11.GL_COLOR_MATERIAL};
|
||||
|
||||
private static final AtomicInteger openCaptures = new AtomicInteger();
|
||||
|
||||
private final GLContextState context;
|
||||
private final Matrix4f modelView = new Matrix4f();
|
||||
private final long[] attribWrites = new long[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
|
||||
private final long[] matrixWrites = new long[GLStateManager.MAX_TEXTURE_STACK_DEPTH];
|
||||
private final boolean[] matrixInherited = new boolean[GLStateManager.MAX_TEXTURE_STACK_DEPTH];
|
||||
private final ExitState.Values current = new ExitState.Values();
|
||||
private final Reference2LongOpenHashMap<StateSet> restoredFieldsBySet = new Reference2LongOpenHashMap<>();
|
||||
private ExitState result;
|
||||
private int suspended, attribDepth, modelViewDepth, textureDepth, entryOverrides;
|
||||
private int filterTexture, filterMin, filterMag;
|
||||
private float filterAniso;
|
||||
private boolean filterOperation;
|
||||
private boolean textureInherited;
|
||||
|
||||
ExitStateCapture(GLContextState context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
static boolean anyOpen() {
|
||||
return openCaptures.get() != 0;
|
||||
}
|
||||
|
||||
private static boolean blendWritesReachVanilla() {
|
||||
final DeferredBlendHandler handler = GLSMHooks.blendHandler;
|
||||
return handler == null || !handler.isOverrideHeld() || handler.isBlendLocked();
|
||||
}
|
||||
|
||||
private static boolean alphaWritesReachVanilla() {
|
||||
final DeferredAlphaHandler handler = GLSMHooks.alphaHandler;
|
||||
return handler == null || !handler.isOverrideHeld() || handler.isAlphaTestLocked();
|
||||
}
|
||||
|
||||
private static boolean depthColorWritesReachVanilla() {
|
||||
final DeferredDepthColorHandler handler = GLSMHooks.depthColorHandler;
|
||||
return handler == null || !handler.isOverrideHeld() || handler.isDepthColorLocked();
|
||||
}
|
||||
|
||||
private static int overridesInControl() {
|
||||
return (blendWritesReachVanilla() ? 0 : 1) | (alphaWritesReachVanilla() ? 0 : 2) | (depthColorWritesReachVanilla() ? 0 : 4);
|
||||
}
|
||||
|
||||
private static boolean same(int field, ExitState.Values a, ExitState.Values b) {
|
||||
return switch (field) {
|
||||
case COLOR -> a.color.sameAs(b.color);
|
||||
case NORMAL -> a.normal.equals(b.normal);
|
||||
case TEXCOORD -> a.texCoord.equals(b.texCoord);
|
||||
case LIGHTMAP -> a.lightmapX == b.lightmapX && a.lightmapY == b.lightmapY;
|
||||
case OVERLAY_COLOR -> a.overlayColor.equals(b.overlayColor);
|
||||
case BLEND_FUNC -> a.blend.sameAs(b.blend);
|
||||
case ALPHA_FUNC -> a.alpha.sameAs(b.alpha);
|
||||
case DEPTH_FUNC -> a.depthFunction == b.depthFunction;
|
||||
case DEPTH_MASK -> a.depthMask == b.depthMask;
|
||||
case COLOR_MASK -> a.colorMask.sameAs(b.colorMask);
|
||||
case CULL_FACE -> a.cullFace == b.cullFace;
|
||||
case FRONT_FACE -> a.frontFace == b.frontFace;
|
||||
case ACTIVE_UNIT -> a.activeUnit == b.activeUnit;
|
||||
case TEXTURE_BINDING -> a.texture == b.texture;
|
||||
case MATRIX_MODE -> a.matrixMode == b.matrixMode;
|
||||
case TEXTURE_MATRIX -> a.textureMatrix.equals(b.textureMatrix);
|
||||
case COLOR_MATERIAL_SETTINGS -> a.materialFace == b.materialFace && a.materialParameter == b.materialParameter;
|
||||
default -> throw new IllegalArgumentException("Unsupported renderer state dependency " + field);
|
||||
};
|
||||
}
|
||||
|
||||
private static Vector4f vector(MaterialState state, int component) {
|
||||
return switch (component) {
|
||||
case 0 -> state.ambient;
|
||||
case 1 -> state.diffuse;
|
||||
case 2 -> state.specular;
|
||||
default -> state.emission;
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean has(ExitState state, int field) {
|
||||
return has(state.writes, field);
|
||||
}
|
||||
|
||||
private static boolean has(long fields, int field) {
|
||||
return (fields & (1L << field)) != 0;
|
||||
}
|
||||
|
||||
private static long bits(int... fields) {
|
||||
long bits = 0;
|
||||
for (int field : fields) bits |= 1L << field;
|
||||
return bits;
|
||||
}
|
||||
|
||||
boolean active() {
|
||||
return result != null && result.valid && suspended == 0 && !context.poppingAttributes && !BatchStateGuard.isSuspended();
|
||||
}
|
||||
|
||||
void suspend() {
|
||||
suspended++;
|
||||
}
|
||||
|
||||
void resume() {
|
||||
if (suspended == 0) throw new IllegalStateException("Exit capture suspension underflow");
|
||||
suspended--;
|
||||
}
|
||||
|
||||
void begin(ExitState out) {
|
||||
out.clear();
|
||||
if (result != null) {
|
||||
result.valid = false;
|
||||
return;
|
||||
}
|
||||
result = out;
|
||||
openCaptures.incrementAndGet();
|
||||
filterTexture = 0;
|
||||
filterOperation = false;
|
||||
out.valid = suspended == 0 && !BatchStateGuard.isSuspended() && GLStateManager.isCachingEnabled() && !DisplayListManager.isRecording() && context.savedFilterTextureId == 0;
|
||||
if (!out.valid) return;
|
||||
out.dependencies = IMPLICIT_INPUTS;
|
||||
capture(out.entry, IMPLICIT_INPUTS | DRAW_INPUTS | MATERIAL_INPUTS | MATERIAL_BAKE_INPUTS | VERTEX_INPUTS | (1L << TEXTURE_MATRIX));
|
||||
textureInherited = true;
|
||||
modelView.set(context.modelViewMatrix);
|
||||
attribDepth = context.attribDepth;
|
||||
modelViewDepth = GLStateManager.getMatrixStackDepth(context.modelViewMatrix);
|
||||
textureDepth = GLStateManager.getMatrixStackDepth(context.textures.getTextureUnitMatrix(0));
|
||||
entryOverrides = overridesInControl();
|
||||
}
|
||||
|
||||
boolean finish(ExitState out) {
|
||||
if (result != out) {
|
||||
out.clear();
|
||||
return false;
|
||||
}
|
||||
if (textureInherited && has(out, TEXTURE_MATRIX)) out.dependencies |= 1L << TEXTURE_MATRIX;
|
||||
result = null;
|
||||
openCaptures.decrementAndGet();
|
||||
if (out.valid) out.valid = suspended == 0 && filterTexture == 0 && stacksMatchEntry() && overridesInControl() == entryOverrides;
|
||||
if (out.valid) {
|
||||
capture(out.value, out.writes);
|
||||
if ((out.writes & (15L << FRONT_AMBIENT)) != 0) out.frontMaterial.set(context.frontMaterial);
|
||||
if ((out.writes & (15L << BACK_AMBIENT)) != 0) out.backMaterial.set(context.backMaterial);
|
||||
out.textureDeleteCount = TextureInfoCache.INSTANCE.deleteCount();
|
||||
}
|
||||
filterTexture = 0;
|
||||
return out.valid;
|
||||
}
|
||||
|
||||
private boolean stacksMatchEntry() {
|
||||
return context.attribDepth == attribDepth && GLStateManager.getMatrixStackDepth(context.modelViewMatrix) == modelViewDepth
|
||||
&& GLStateManager.getMatrixStackDepth(context.textures.getTextureUnitMatrix(0)) == textureDepth && modelView.equals(context.modelViewMatrix);
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
if (result != null) {
|
||||
result.clear();
|
||||
openCaptures.decrementAndGet();
|
||||
}
|
||||
result = null;
|
||||
filterTexture = 0;
|
||||
filterOperation = false;
|
||||
}
|
||||
|
||||
void reject() {
|
||||
if (active()) result.valid = false;
|
||||
}
|
||||
|
||||
// Callers record before their cache's unchanged-value early return
|
||||
void write(int field) {
|
||||
if (active()) record(field);
|
||||
}
|
||||
|
||||
void writeBlend() {
|
||||
if (active() && blendWritesReachVanilla()) record(ExitStateCapture.BLEND_FUNC);
|
||||
}
|
||||
|
||||
void writeAlpha() {
|
||||
if (active() && alphaWritesReachVanilla()) record(ExitStateCapture.ALPHA_FUNC);
|
||||
}
|
||||
|
||||
void writeDepthColor(int field) {
|
||||
if (active() && depthColorWritesReachVanilla()) record(field);
|
||||
}
|
||||
|
||||
private void record(int field) {
|
||||
result.writes |= 1L << field;
|
||||
}
|
||||
|
||||
void requireDrawState(boolean materialFromState) {
|
||||
if (!active()) return;
|
||||
long fields = (materialFromState ? DRAW_INPUTS | MATERIAL_INPUTS : DRAW_INPUTS) & ~(result.writes | result.dependencies);
|
||||
if ((fields & (1L << BLEND_FUNC)) != 0 && !context.blendMode.isEffectivelyEnabled()) fields &= ~(1L << BLEND_FUNC);
|
||||
if ((fields & (1L << ALPHA_FUNC)) != 0 && !context.alphaTest.isEffectivelyEnabled()) fields &= ~(1L << ALPHA_FUNC);
|
||||
result.dependencies |= fields;
|
||||
if (textureInherited) result.dependencies |= 1L << TEXTURE_MATRIX;
|
||||
}
|
||||
|
||||
void requireMaterialBakeInputs() {
|
||||
if (active()) result.dependencies |= MATERIAL_BAKE_INPUTS & ~result.writes;
|
||||
}
|
||||
|
||||
void requireVertexDefaults(boolean normal, boolean texCoord, boolean color, boolean lightmap) {
|
||||
if (!active()) return;
|
||||
final long fields = (normal ? 1L << NORMAL : 0) | (texCoord ? 1L << TEXCOORD : 0) | (color ? 1L << COLOR : 0) | (lightmap ? 1L << LIGHTMAP : 0);
|
||||
result.dependencies |= fields & ~result.writes;
|
||||
}
|
||||
|
||||
boolean beginTextureFilter(boolean nearest) {
|
||||
if (filterOperationBlocked()) return false;
|
||||
if (!nearest || filterTexture != 0 || context.savedFilterTextureId <= 0 || context.activeTextureUnit.getValue() != 0 || context.savedFilterMin < 0 || context.savedFilterMag < 0 || !(context.savedFilterAniso >= 1)) {
|
||||
reject();
|
||||
return false;
|
||||
}
|
||||
filterTexture = context.savedFilterTextureId;
|
||||
filterMin = context.savedFilterMin;
|
||||
filterMag = context.savedFilterMag;
|
||||
filterAniso = context.savedFilterAniso;
|
||||
startFilterOperation();
|
||||
return true;
|
||||
}
|
||||
|
||||
void finishTextureFilterSave() {
|
||||
endFilterOperation();
|
||||
if (active() && (savedFilterDiffers() || context.activeTextureUnit.getValue() != 0 || context.textures.getTextureUnitBindings(0).getBinding() != filterTexture || filterDiffersFrom(GL11.GL_NEAREST, GL11.GL_NEAREST, 1))) reject();
|
||||
}
|
||||
|
||||
boolean beginTextureFilterRestore() {
|
||||
if (filterOperationBlocked()) return false;
|
||||
if (savedFilterDiffers()) {
|
||||
reject();
|
||||
return false;
|
||||
}
|
||||
startFilterOperation();
|
||||
return true;
|
||||
}
|
||||
|
||||
void finishTextureFilterRestore() {
|
||||
endFilterOperation();
|
||||
if (active() && (savedFilterDiffers() || filterDiffersFrom(filterMin, filterMag, filterAniso))) reject();
|
||||
filterTexture = 0;
|
||||
}
|
||||
|
||||
private boolean filterOperationBlocked() {
|
||||
if (filterOperation) {
|
||||
failTextureFilter();
|
||||
return true;
|
||||
}
|
||||
return !active();
|
||||
}
|
||||
|
||||
private void startFilterOperation() {
|
||||
filterOperation = true;
|
||||
suspend();
|
||||
}
|
||||
|
||||
private void endFilterOperation() {
|
||||
resume();
|
||||
filterOperation = false;
|
||||
}
|
||||
|
||||
void failTextureFilter() {
|
||||
if (result != null) result.valid = false;
|
||||
}
|
||||
|
||||
void requireTextureFilter(int texture, boolean nearest) {
|
||||
if (filterTexture != 0 && texture == filterTexture && !nearest && active()) reject();
|
||||
}
|
||||
|
||||
private boolean savedFilterDiffers() {
|
||||
return filterTexture == 0 || context.savedFilterTextureId != filterTexture || context.savedFilterMin != filterMin || context.savedFilterMag != filterMag || context.savedFilterAniso != filterAniso;
|
||||
}
|
||||
|
||||
private boolean filterDiffersFrom(int min, int mag, float aniso) {
|
||||
final TextureInfo info = TextureInfoCache.INSTANCE.getInfo(filterTexture);
|
||||
return info == null || info.getMinFilter() != min || info.getMagFilter() != mag || info.getMaxAnisotropy() != aniso;
|
||||
}
|
||||
|
||||
boolean lightingWritten() {
|
||||
return active() && has(result, LIGHTING);
|
||||
}
|
||||
|
||||
int polygonOffsetWrites() {
|
||||
if (!active()) return 0;
|
||||
return (has(result, OFFSET) ? ExitState.POLYGON_OFFSET_ENABLE_WRITTEN : 0) | (has(result, OFFSET_VALUES) ? ExitState.POLYGON_OFFSET_VALUES_WRITTEN : 0);
|
||||
}
|
||||
|
||||
void capability(int cap, int unit) {
|
||||
if (!active()) return;
|
||||
int field = -1;
|
||||
for (int i = 0; i < CAPS.length; i++) {
|
||||
if (CAPS[i] == cap) {
|
||||
field = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (field == TEXTURE0) field = unit == 0 ? TEXTURE0 : unit == 1 ? TEXTURE1 : -1;
|
||||
if (field < 0) result.valid = false;
|
||||
else if (field == BLEND ? blendWritesReachVanilla() : field != ALPHA || alphaWritesReachVanilla()) record(field);
|
||||
}
|
||||
|
||||
void material(int face, int parameter) {
|
||||
if (!active()) return;
|
||||
int first = switch (parameter) {
|
||||
case GL11.GL_AMBIENT, GL11.GL_AMBIENT_AND_DIFFUSE -> 0;
|
||||
case GL11.GL_DIFFUSE -> 1;
|
||||
case GL11.GL_SPECULAR -> 2;
|
||||
case GL11.GL_EMISSION -> 3;
|
||||
default -> -1;
|
||||
};
|
||||
if (first < 0) {
|
||||
result.valid = false;
|
||||
return;
|
||||
}
|
||||
if (face != GL11.GL_BACK) {
|
||||
record(FRONT_AMBIENT + first);
|
||||
if (parameter == GL11.GL_AMBIENT_AND_DIFFUSE) record(FRONT_DIFFUSE);
|
||||
}
|
||||
if (face != GL11.GL_FRONT) {
|
||||
record(BACK_AMBIENT + first);
|
||||
if (parameter == GL11.GL_AMBIENT_AND_DIFFUSE) record(BACK_DIFFUSE);
|
||||
}
|
||||
}
|
||||
|
||||
// Pushes and pops check result rather than active()
|
||||
void pushAttrib() {
|
||||
if (result == null) return;
|
||||
if (context.attribDepth < attribWrites.length) attribWrites[context.attribDepth] = result.writes;
|
||||
else result.valid = false;
|
||||
}
|
||||
|
||||
void popAttrib(StateSet set) {
|
||||
if (result == null) return;
|
||||
if (context.attribDepth <= attribDepth) {
|
||||
result.valid = false;
|
||||
return;
|
||||
}
|
||||
final long restored = restoredFields(set);
|
||||
result.writes = (result.writes & ~restored) | (attribWrites[context.attribDepth - 1] & restored);
|
||||
}
|
||||
|
||||
private long restoredFields(StateSet set) {
|
||||
long restored = restoredFieldsBySet.getOrDefault(set, -1L);
|
||||
if (restored >= 0) return restored;
|
||||
restored = 0;
|
||||
for (int field = 0; field < FIELD_COUNT; field++) {
|
||||
final CowStateStack<?> stack = stack(field);
|
||||
if (stack != null && set.contains(stack.stackId())) restored |= 1L << field;
|
||||
}
|
||||
restoredFieldsBySet.put(set, restored);
|
||||
return restored;
|
||||
}
|
||||
|
||||
private CowStateStack<?> stack(int field) {
|
||||
return switch (field) {
|
||||
case BLEND -> context.blendMode;
|
||||
case ALPHA -> context.alphaTest;
|
||||
case DEPTH -> context.depthTest;
|
||||
case CULL -> context.cullState;
|
||||
case LIGHTING -> context.lightingState;
|
||||
case NORMALIZE -> context.normalizeState;
|
||||
case RESCALE -> context.rescaleNormalState;
|
||||
case LIGHT0, LIGHT1 -> context.lightStates[field - LIGHT0];
|
||||
case TEXTURE0, TEXTURE1 -> context.textures.getTextureUnitStates(field - TEXTURE0);
|
||||
case OFFSET -> context.polygonOffsetFillState;
|
||||
case COLOR_MATERIAL -> context.colorMaterial;
|
||||
case COLOR -> context.color;
|
||||
case NORMAL -> context.ffp.normalStack;
|
||||
case TEXCOORD -> context.ffp.texCoordStack;
|
||||
case BLEND_FUNC -> context.blendState;
|
||||
case ALPHA_FUNC -> context.alphaState;
|
||||
case DEPTH_FUNC, DEPTH_MASK -> context.depthState;
|
||||
case COLOR_MASK -> context.colorMask;
|
||||
case CULL_FACE, FRONT_FACE, OFFSET_VALUES -> context.polygonState;
|
||||
case SHADE_MODEL -> context.shadeModelState;
|
||||
case ACTIVE_UNIT -> context.activeTextureUnit;
|
||||
case TEXTURE_BINDING -> context.textures.getTextureUnitBindings(0);
|
||||
case MATRIX_MODE -> context.matrixMode;
|
||||
// Face and parameter are restored together (GL_LIGHTING_BIT and the batch set hold both).
|
||||
case COLOR_MATERIAL_SETTINGS -> context.colorMaterialFace;
|
||||
case FRONT_AMBIENT, FRONT_DIFFUSE, FRONT_SPECULAR, FRONT_EMISSION -> context.frontMaterial;
|
||||
case BACK_AMBIENT, BACK_DIFFUSE, BACK_SPECULAR, BACK_EMISSION -> context.backMaterial;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
void matrixChange(boolean absolute) {
|
||||
if (result == null) return;
|
||||
final int mode = context.matrixMode.getMode();
|
||||
if ((absolute || mode != GL11.GL_MODELVIEW) && active()) recordMatrixChange(mode, context.activeTextureUnit.getValue(), absolute);
|
||||
}
|
||||
|
||||
void matrixChange(int mode, int unit, boolean absolute) {
|
||||
if (active()) recordMatrixChange(mode, unit, absolute);
|
||||
}
|
||||
|
||||
private void recordMatrixChange(int mode, int unit, boolean absolute) {
|
||||
if (mode == GL11.GL_MODELVIEW) {
|
||||
if (absolute) result.valid = false;
|
||||
return;
|
||||
}
|
||||
if (mode != GL11.GL_TEXTURE || unit != 0) {
|
||||
result.valid = false;
|
||||
return;
|
||||
}
|
||||
record(TEXTURE_MATRIX);
|
||||
if (absolute) textureInherited = false;
|
||||
}
|
||||
|
||||
void pushMatrix() {
|
||||
if (result == null) return;
|
||||
final int mode = context.matrixMode.getMode();
|
||||
if (mode == GL11.GL_TEXTURE && context.activeTextureUnit.getValue() == 0) {
|
||||
final int depth = GLStateManager.getMatrixStackDepth(context.textures.getTextureUnitMatrix(0));
|
||||
if (depth < matrixWrites.length) {
|
||||
matrixWrites[depth] = result.writes;
|
||||
matrixInherited[depth] = textureInherited;
|
||||
} else {
|
||||
result.valid = false;
|
||||
}
|
||||
} else if (mode != GL11.GL_MODELVIEW) {
|
||||
reject();
|
||||
}
|
||||
}
|
||||
|
||||
void popMatrix() {
|
||||
if (result == null) return;
|
||||
final int mode = context.matrixMode.getMode();
|
||||
final boolean texture = mode == GL11.GL_TEXTURE && context.activeTextureUnit.getValue() == 0;
|
||||
final Matrix4fStack matrix;
|
||||
final int entryDepth;
|
||||
if (mode == GL11.GL_MODELVIEW) {
|
||||
matrix = context.modelViewMatrix;
|
||||
entryDepth = modelViewDepth;
|
||||
} else if (texture) {
|
||||
matrix = context.textures.getTextureUnitMatrix(0);
|
||||
entryDepth = textureDepth;
|
||||
} else {
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
final int depth = GLStateManager.getMatrixStackDepth(matrix);
|
||||
if (depth <= entryDepth) {
|
||||
result.valid = false;
|
||||
return;
|
||||
}
|
||||
if (texture) {
|
||||
final long bit = 1L << TEXTURE_MATRIX;
|
||||
result.writes = (result.writes & ~bit) | (matrixWrites[depth - 1] & bit);
|
||||
textureInherited = matrixInherited[depth - 1];
|
||||
}
|
||||
}
|
||||
|
||||
private void capture(ExitState.Values out, long fields) {
|
||||
out.enabled = 0;
|
||||
for (int field = 0; field < CAPS.length; field++) {
|
||||
if (has(fields, field) && ((BooleanStateStack) Objects.requireNonNull(stack(field))).isEffectivelyEnabled()) out.enabled |= 1L << field;
|
||||
}
|
||||
if (has(fields, COLOR)) out.color.set(context.color);
|
||||
if (has(fields, NORMAL)) out.normal.set(context.ffp.currentNormal);
|
||||
if (has(fields, TEXCOORD)) out.texCoord.set(context.ffp.currentTexCoords[0]);
|
||||
if (has(fields, LIGHTMAP)) {
|
||||
out.lightmapX = context.lastBrightnessX;
|
||||
out.lightmapY = context.lastBrightnessY;
|
||||
}
|
||||
if (has(fields, SHADER_COLOR)) out.shaderColor.set(context.shaderColorR, context.shaderColorG, context.shaderColorB, context.shaderColorA);
|
||||
if (has(fields, OVERLAY_COLOR)) out.overlayColor.set(context.overlayR, context.overlayG, context.overlayB, context.overlayA);
|
||||
if (has(fields, BLEND_FUNC)) context.blendState.readEffective(out.blend);
|
||||
if (has(fields, ALPHA_FUNC)) context.alphaState.readEffective(out.alpha);
|
||||
if (has(fields, COLOR_MASK)) context.colorMask.readEffective(out.colorMask);
|
||||
if (has(fields, DEPTH_FUNC)) out.depthFunction = context.depthState.getFunc();
|
||||
if (has(fields, DEPTH_MASK)) out.depthMask = context.depthState.isEffectiveMaskEnabled();
|
||||
if (has(fields, CULL_FACE)) out.cullFace = context.polygonState.getCullFaceMode();
|
||||
if (has(fields, FRONT_FACE)) out.frontFace = context.polygonState.getFrontFace();
|
||||
if (has(fields, OFFSET_VALUES)) {
|
||||
out.offsetFactor = context.polygonState.getOffsetFactor();
|
||||
out.offsetUnits = context.polygonState.getOffsetUnits();
|
||||
out.offsetClamp = context.polygonState.getOffsetClamp();
|
||||
}
|
||||
if (has(fields, SHADE_MODEL)) out.shadeModel = context.shadeModelState.getValue();
|
||||
if (has(fields, ACTIVE_UNIT)) out.activeUnit = context.activeTextureUnit.getValue();
|
||||
if (has(fields, CLIENT_ACTIVE_UNIT)) out.clientActiveUnit = context.clientActiveTextureUnit;
|
||||
if (has(fields, MATRIX_MODE)) out.matrixMode = context.matrixMode.getMode();
|
||||
if (has(fields, TEXTURE_BINDING)) out.texture = context.textures.getTextureUnitBindings(0).getBinding();
|
||||
if (has(fields, TEXTURE_MATRIX)) out.textureMatrix.set(context.textures.getTextureUnitMatrix(0));
|
||||
if (has(fields, COLOR_MATERIAL_SETTINGS)) {
|
||||
out.materialFace = context.colorMaterialFace.getValue();
|
||||
out.materialParameter = context.colorMaterialParameter.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
boolean canApply(ExitState in) {
|
||||
if (!in.valid || result != null || !GLStateManager.isCachingEnabled() || DisplayListManager.isRecording() || context.savedFilterTextureId != 0) return false;
|
||||
if (in.textureDeleteCount != TextureInfoCache.INSTANCE.deleteCount()) return false;
|
||||
capture(current, in.dependencies);
|
||||
if (((current.enabled ^ in.entry.enabled) & in.dependencies & ((1L << CAPS.length) - 1)) != 0) return false;
|
||||
long remaining = in.dependencies & -(1L << CAPS.length);
|
||||
while (remaining != 0) {
|
||||
final int field = Long.numberOfTrailingZeros(remaining);
|
||||
if (!same(field, in.entry, current)) return false;
|
||||
remaining &= remaining - 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void apply(ExitState state) {
|
||||
if (!state.valid) throw new IllegalStateException("Invalid renderer exit state");
|
||||
if (result != null) throw new IllegalStateException("Exit state applied while a capture is open");
|
||||
final ExitState.Values in = state.value;
|
||||
final long fields = state.writes;
|
||||
final int liveUnit = context.activeTextureUnit.getValue();
|
||||
// Capabilities go first so the glColor4f below bakes into the material only if color material is on at exit.
|
||||
for (int field = 0; field < CAPS.length; field++) {
|
||||
if (!has(fields, field)) continue;
|
||||
if (field == TEXTURE0 || field == TEXTURE1) GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + field - TEXTURE0);
|
||||
// glEnable would bake the live color, not the exit color, into the material.
|
||||
if (field == COLOR_MATERIAL) {
|
||||
context.colorMaterial.setEnabled((in.enabled & (1L << field)) != 0);
|
||||
} else if ((in.enabled & (1L << field)) != 0) GLStateManager.glEnable(CAPS[field]);
|
||||
else GLStateManager.glDisable(CAPS[field]);
|
||||
}
|
||||
if (has(fields, COLOR)) GLStateManager.glColor4f(in.color.getRed(), in.color.getGreen(), in.color.getBlue(), in.color.getAlpha());
|
||||
if (has(fields, NORMAL)) GLStateManager.glNormal3f(in.normal.x, in.normal.y, in.normal.z);
|
||||
if (has(fields, TEXCOORD)) GLStateManager.glTexCoord4f(in.texCoord.x, in.texCoord.y, in.texCoord.z, in.texCoord.w);
|
||||
if (has(fields, LIGHTMAP)) GLStateManager.setLightmapTextureCoords(GL13.GL_TEXTURE1, in.lightmapX, in.lightmapY);
|
||||
if (has(fields, SHADER_COLOR)) GLStateManager.setShaderColor(in.shaderColor.x, in.shaderColor.y, in.shaderColor.z, in.shaderColor.w);
|
||||
if (has(fields, OVERLAY_COLOR)) GLStateManager.setOverlayColor(in.overlayColor.x, in.overlayColor.y, in.overlayColor.z, in.overlayColor.w);
|
||||
if (has(fields, BLEND_FUNC)) GLStateManager.tryBlendFuncSeparate(in.blend.getSrcRgb(), in.blend.getDstRgb(), in.blend.getSrcAlpha(), in.blend.getDstAlpha());
|
||||
if (has(fields, ALPHA_FUNC)) GLStateManager.glAlphaFunc(in.alpha.getFunction(), in.alpha.getReference());
|
||||
if (has(fields, DEPTH_FUNC)) GLStateManager.glDepthFunc(in.depthFunction);
|
||||
if (has(fields, DEPTH_MASK)) GLStateManager.glDepthMask(in.depthMask);
|
||||
if (has(fields, COLOR_MASK)) GLStateManager.glColorMask(in.colorMask.red, in.colorMask.green, in.colorMask.blue, in.colorMask.alpha);
|
||||
if (has(fields, CULL_FACE)) GLStateManager.glCullFace(in.cullFace);
|
||||
if (has(fields, FRONT_FACE)) GLStateManager.glFrontFace(in.frontFace);
|
||||
if (has(fields, SHADE_MODEL)) GLStateManager.glShadeModel(in.shadeModel);
|
||||
if (has(fields, OFFSET_VALUES)) GLStateManager.glPolygonOffsetClamp(in.offsetFactor, in.offsetUnits, in.offsetClamp);
|
||||
if (has(fields, TEXTURE_BINDING)) {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, in.texture);
|
||||
}
|
||||
if (has(fields, TEXTURE_MATRIX)) GLStateManager.setTextureMatrix(0, in.textureMatrix);
|
||||
if (has(fields, COLOR_MATERIAL_SETTINGS)) GLStateManager.glColorMaterial(in.materialFace, in.materialParameter);
|
||||
boolean materialChanged = false;
|
||||
for (int field = FRONT_AMBIENT; field <= BACK_EMISSION; field++) {
|
||||
if (!has(fields, field)) continue;
|
||||
final boolean front = field < BACK_AMBIENT;
|
||||
final int component = (field - FRONT_AMBIENT) & 3;
|
||||
final Vector4f source = vector(front ? state.frontMaterial : state.backMaterial, component);
|
||||
final var target = front ? context.frontMaterial : context.backMaterial;
|
||||
if (vector(target, component).equals(source)) continue;
|
||||
target.beforeModify();
|
||||
vector(target, component).set(source);
|
||||
materialChanged = true;
|
||||
}
|
||||
if (materialChanged) context.lightingGeneration++;
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + (has(fields, ACTIVE_UNIT) ? in.activeUnit : liveUnit));
|
||||
if (has(fields, CLIENT_ACTIVE_UNIT)) GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0 + in.clientActiveUnit);
|
||||
if (has(fields, MATRIX_MODE)) GLStateManager.glMatrixMode(in.matrixMode);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.AlphaStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BlendStateStack;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
@@ -34,6 +35,7 @@ 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;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.Instancing;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
|
||||
@@ -57,6 +59,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GLContextState {
|
||||
final ExitStateCapture exitCapture = new ExitStateCapture(this);
|
||||
private final List<IStateStack<?>> allStacksBuilder = new ArrayList<>();
|
||||
public IStateStack<?>[] allStacks;
|
||||
final List<BooleanStateStack> allBooleanStates = new ArrayList<>();
|
||||
@@ -104,6 +107,8 @@ public class GLContextState {
|
||||
public boolean dirtyNormalAttrib;
|
||||
public boolean dirtyTexCoordAttrib;
|
||||
public boolean dirtyLightmapAttrib = true;
|
||||
public long entityIds = InstancedAttribs.DEFAULT_ENTITY_IDS;
|
||||
public boolean dirtyEntityIdsAttrib = true;
|
||||
public boolean unit23TexCoordSetDuringDraw = false;
|
||||
public int maxBoundTextureUnit = 0;
|
||||
public int maxBoundImageUnit = 0;
|
||||
@@ -236,6 +241,8 @@ public class GLContextState {
|
||||
public int boundCopyReadBuffer;
|
||||
public int boundCopyWriteBuffer;
|
||||
public final Int2IntMap boundOtherBuffers = new Int2IntOpenHashMap();
|
||||
public final int[] uniformBufferBindings = new int[GLStateManager.INDEXED_BUFFER_SLOTS];
|
||||
public final int[] storageBufferBindings = new int[GLStateManager.INDEXED_BUFFER_SLOTS];
|
||||
public PixelStoreState pixelUnpackState = PixelStoreState.DEFAULT;
|
||||
public PixelStoreState pixelPackState = PixelStoreState.DEFAULT;
|
||||
public final int[] clientAttribSavedMask = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
@@ -245,7 +252,6 @@ public class GLContextState {
|
||||
public final int[] clientAttribSavedPackBuffer = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public final VAOManager.ClientArrays[] clientAttribSavedArrays = new VAOManager.ClientArrays[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
|
||||
public int clientAttribStackPointer = 0;
|
||||
public float maxPointSize = Float.NaN;
|
||||
public int restoreChangedMask;
|
||||
public long restoreUnitChangedMask;
|
||||
public final BlendState vanillaBlendBefore = new BlendState();
|
||||
@@ -253,6 +259,7 @@ public class GLContextState {
|
||||
public boolean vanillaBlendEnabledBefore;
|
||||
public int drawFramebuffer = 0;
|
||||
public int readFramebuffer = 0;
|
||||
public final Int2ObjectOpenHashMap<int[]> framebufferTextures = new Int2ObjectOpenHashMap<>();
|
||||
public int texGenGeneration;
|
||||
public int defaultVAO;
|
||||
public final VAOManager vaos = new VAOManager();
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -46,6 +46,8 @@ public class RenderSystem {
|
||||
private static boolean supportsMultiDrawIndirect;
|
||||
private static boolean supportsBufferStorage;
|
||||
private static boolean supportsClearTexture;
|
||||
private static boolean supportsStencilTexturing;
|
||||
private static boolean supportsTextureStorage;
|
||||
private static boolean supportsTesselation;
|
||||
private static boolean supportsSamplerObjects;
|
||||
private static boolean supportsProgramBinary;
|
||||
@@ -128,6 +130,8 @@ public class RenderSystem {
|
||||
supportsMultiDrawIndirect = GLStateManager.capabilities.OpenGL43 || GLStateManager.capabilities.GL_ARB_multi_draw_indirect;
|
||||
supportsBufferStorage = !isGLES && (GLStateManager.capabilities.OpenGL44 || GLStateManager.capabilities.GL_ARB_buffer_storage);
|
||||
supportsClearTexture = GLStateManager.capabilities.OpenGL44 || GLStateManager.capabilities.GL_ARB_clear_texture;
|
||||
supportsStencilTexturing = GLStateManager.capabilities.OpenGL43 || GLStateManager.capabilities.GL_ARB_stencil_texturing;
|
||||
supportsTextureStorage = isGLES ? glesVersion >= 300 : GLStateManager.capabilities.OpenGL42 || GLStateManager.capabilities.GL_ARB_texture_storage;
|
||||
hasClipCullDistance = !isGLES || hasExtension("GL_EXT_clip_cull_distance");
|
||||
|
||||
// Cache maximum image units
|
||||
@@ -194,14 +198,17 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void generateMipmaps(int texture, int mipmapTarget) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.generateMipmaps(texture, mipmapTarget);
|
||||
}
|
||||
|
||||
public static void bindAttributeLocation(int program, int index, CharSequence name) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.bindAttribLocation(program, index, name);
|
||||
}
|
||||
|
||||
public static void texImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable ByteBuffer pixels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
final GLStateManager.GLESTexImageRemap remap = GLStateManager.remapTexImageForGLES(internalformat, format, type);
|
||||
internalformat = remap.internalFormat();
|
||||
format = remap.format();
|
||||
@@ -216,6 +223,7 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void uniformMatrix4fv(int location, boolean transpose, FloatBuffer matrix) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniformMatrix4(location, transpose, matrix);
|
||||
}
|
||||
|
||||
@@ -224,118 +232,151 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void uniform1f(int location, float v0) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform1f(location, v0);
|
||||
}
|
||||
|
||||
public static void uniform1i(int location, int v0) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform1i(location, v0);
|
||||
}
|
||||
|
||||
public static void uniform2f(int location, float v0, float v1) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform2f(location, v0, v1);
|
||||
}
|
||||
|
||||
public static void uniform2i(int location, int v0, int v1) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform2i(location, v0, v1);
|
||||
}
|
||||
|
||||
public static void uniform3f(int location, float v0, float v1, float v2) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform3f(location, v0, v1, v2);
|
||||
}
|
||||
|
||||
public static void uniform3i(int location, int v0, int v1, int v2) {RENDER_BACKEND.uniform3i(location, v0, v1, v2);}
|
||||
public static void uniform3i(int location, int v0, int v1, int v2) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform3i(location, v0, v1, v2);
|
||||
}
|
||||
|
||||
public static void uniform4f(int location, float v0, float v1, float v2, float v3) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform4f(location, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
public static void uniform4i(int location, int v0, int v1, int v2, int v3) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.uniform4i(location, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
public static int getAttribLocation(int programId, String name) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return RENDER_BACKEND.getAttribLocation(programId, name);
|
||||
}
|
||||
|
||||
public static int getUniformLocation(int programId, String name) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return RENDER_BACKEND.getUniformLocation(programId, name);
|
||||
}
|
||||
|
||||
public static void texParameteriv(int texture, int target, int pname, IntBuffer params) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.texParameteriv(texture, target, pname, params);
|
||||
}
|
||||
|
||||
public static void copyTexSubImage2D(int destTexture, int target, int i, int i1, int i2, int i3, int i4, int width, int height) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.copyTexSubImage2D(destTexture, target, i, i1, i2, i3, i4, width, height);
|
||||
if (i == 0) TextureInfoCache.INSTANCE.onContentsChanged(destTexture);
|
||||
}
|
||||
|
||||
public static void texParameteri(int texture, int target, int pname, int param) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.texParameteri(texture, target, pname, param);
|
||||
}
|
||||
|
||||
public static void texParameterf(int texture, int target, int pname, float param) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.texParameterf(texture, target, pname, param);
|
||||
}
|
||||
|
||||
public static void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureImage2D(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
TextureInfoCache.INSTANCE.onTextureImage2D(texture, target, level, internalformat, width, height);
|
||||
}
|
||||
|
||||
public static void textureImage2D(int texture, int target, int level, int internalformat, int width, int height, int border, int format, int type, IntBuffer pixels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureImage2D(texture, target, level, internalformat, width, height, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
TextureInfoCache.INSTANCE.onTextureImage2D(texture, target, level, internalformat, width, height);
|
||||
}
|
||||
|
||||
public static void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, ByteBuffer pixels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureSubImage2D(texture, target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
if (level == 0) TextureInfoCache.INSTANCE.onContentsChanged(texture);
|
||||
}
|
||||
|
||||
public static void textureSubImage2D(int texture, int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, IntBuffer pixels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
dsaState.textureSubImage2D(texture, target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
if (level == 0) TextureInfoCache.INSTANCE.onContentsChanged(texture);
|
||||
}
|
||||
|
||||
public static void texImage1D(int texture, int target, int level, int internalformat, int width, int border, int format, int type, ByteBuffer pixels) {
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLStateManager.glBindTexture(target, texture);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage1D(target, level, internalformat, width, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static void texImage3D(int texture, int target, int level, int internalformat, int width, int height, int depth, int border, int format, int type, ByteBuffer pixels) {
|
||||
RENDER_BACKEND.bindTexture(target, texture);
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLStateManager.glBindTexture(target, texture);
|
||||
GLStateManager.suspendPixelUnpackBuffer();
|
||||
RENDER_BACKEND.texImage3D(target, level, internalformat, width, height, depth, border, format, type, pixels);
|
||||
GLStateManager.restorePixelUnpackBuffer();
|
||||
}
|
||||
|
||||
public static String getProgramInfoLog(int program) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return RENDER_BACKEND.getProgramInfoLog(program, RENDER_BACKEND.getProgrami(program, GL20.GL_INFO_LOG_LENGTH));
|
||||
}
|
||||
|
||||
public static String getShaderInfoLog(int shader) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return RENDER_BACKEND.getShaderInfoLog(shader, RENDER_BACKEND.getShaderi(shader, GL20.GL_INFO_LOG_LENGTH));
|
||||
}
|
||||
|
||||
public static void drawBuffers(int framebuffer, IntBuffer buffers) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.drawBuffers(framebuffer, buffers);
|
||||
}
|
||||
|
||||
public static void readBuffer(int framebuffer, int buffer) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.readBuffer(framebuffer, buffer);
|
||||
}
|
||||
|
||||
public static String getActiveUniform(int program, int index, int maxLength, IntBuffer sizeType) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return RENDER_BACKEND.getActiveUniform(program, index, maxLength, sizeType);
|
||||
}
|
||||
|
||||
public static void readPixels(int x, int y, int width, int height, int format, int type, FloatBuffer pixels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
GLStateManager.suspendPixelPackBuffer();
|
||||
RENDER_BACKEND.readPixels(x, y, width, height, format, type, pixels);
|
||||
GLStateManager.restorePixelPackBuffer();
|
||||
@@ -346,30 +387,37 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static int bufferStorage(int target, FloatBuffer data, int usage) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.bufferStorage(target, data, usage);
|
||||
}
|
||||
|
||||
public static void vertexAttrib4f(int index, float v0, float v1, float v2, float v3) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.vertexAttrib4f(index, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
public static void detachShader(int program, int shader) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.detachShader(program, shader);
|
||||
}
|
||||
|
||||
public static void framebufferTexture2D(int fb, int fbtarget, int attachment, int target, int texture, int levels) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
dsaState.framebufferTexture2D(fb, fbtarget, attachment, target, texture, levels);
|
||||
}
|
||||
|
||||
public static int getTexParameteri(int texture, int target, int pname) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.getTexParameteri(texture, target, pname);
|
||||
}
|
||||
|
||||
public static float getTexParameterf(int texture, int target, int pname) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.getTexParameterf(texture, target, pname);
|
||||
}
|
||||
|
||||
public static int getTexLevelParameteri(int texture, int level, int pname) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.getTexLevelParameteri(texture, level, pname);
|
||||
}
|
||||
|
||||
@@ -453,23 +501,34 @@ public class RenderSystem {
|
||||
return supportsClearTexture;
|
||||
}
|
||||
|
||||
public static boolean supportsStencilTexturing() {
|
||||
return supportsStencilTexturing;
|
||||
}
|
||||
|
||||
public static boolean supportsTextureStorage() {
|
||||
return supportsTextureStorage;
|
||||
}
|
||||
|
||||
public static int getMaxSSBOBindings() {
|
||||
return maxSSBOBindings;
|
||||
}
|
||||
|
||||
public static void getProgramiv(int program, int value, IntBuffer storage) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.getProgramiv(program, value, storage);
|
||||
}
|
||||
|
||||
public static void dispatchCompute(int workX, int workY, int workZ) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.dispatchCompute(workX, workY, workZ);
|
||||
}
|
||||
|
||||
public static void dispatchCompute(Vector3i workGroups) {
|
||||
RENDER_BACKEND.dispatchCompute(workGroups.x, workGroups.y, workGroups.z);
|
||||
dispatchCompute(workGroups.x, workGroups.y, workGroups.z);
|
||||
}
|
||||
|
||||
public static void dispatchComputeIndirect(long offset) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.dispatchComputeIndirect(offset);
|
||||
}
|
||||
|
||||
@@ -478,6 +537,7 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void memoryBarrier(int barriers) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
if (supportsCompute) {
|
||||
RENDER_BACKEND.memoryBarrier(barriers);
|
||||
}
|
||||
@@ -500,12 +560,14 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void bindTextureToUnit(int unit, int texture) {
|
||||
if (unit != 0 && GLStateManager.getBoundTextureForServerState(unit) != texture) GLStateManager.beforeUncapturedStateChange();
|
||||
if (unit == 0) GLStateManager.recordExitTextureBind(GL11.GL_TEXTURE_2D, 0);
|
||||
else if (GLStateManager.getBoundTextureForServerState(unit) != texture) GLStateManager.beforeUncapturedStateChange();
|
||||
dsaState.bindTextureToUnit(unit, texture);
|
||||
}
|
||||
|
||||
public static void bindTextureToUnit(int target, int unit, int texture) {
|
||||
if (unit != 0 && GLStateManager.getBoundTextureForServerState(unit) != texture) GLStateManager.beforeUncapturedStateChange();
|
||||
if (unit == 0) GLStateManager.recordExitTextureBind(target, 0);
|
||||
else if (GLStateManager.getBoundTextureForServerState(unit) != texture) GLStateManager.beforeUncapturedStateChange();
|
||||
dsaState.bindTextureToUnit(target, unit, texture);
|
||||
}
|
||||
|
||||
@@ -525,6 +587,7 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void blitFramebuffer(int source, int dest, int offsetX, int offsetY, int width, int height, int offsetX2, int offsetY2, int width2, int height2, int bufferChoice, int filter) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
final boolean scissor = GLStateManager.glIsEnabled(GL11.GL_SCISSOR_TEST);
|
||||
if (scissor) GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
try {
|
||||
@@ -535,10 +598,12 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static int createFramebuffer() {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.createFramebuffer();
|
||||
}
|
||||
|
||||
public static int createTexture(int target) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
return dsaState.createTexture(target);
|
||||
}
|
||||
|
||||
@@ -556,7 +621,8 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static int getIndexedBufferBinding(int target, int index) {
|
||||
return RENDER_BACKEND.getIndexedBufferBinding(target, index);
|
||||
GLStateManager.rejectExitCapture();
|
||||
return GLStateManager.getIndexedBufferBinding(target, index);
|
||||
}
|
||||
|
||||
public static void bindBufferBase(int target, int index, int buffer) {
|
||||
@@ -587,6 +653,7 @@ public class RenderSystem {
|
||||
}
|
||||
|
||||
public static void clearTexImage(int texture, int target, int level, int format, int type) {
|
||||
GLStateManager.rejectExitCapture();
|
||||
RENDER_BACKEND.clearTexImage(texture, level, format, type);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,11 @@ public final class BackendOptions {
|
||||
}
|
||||
|
||||
public static String sdlGpuDriver() {
|
||||
return SystemProperties.SDL_GPU_DRIVER_OVERRIDE != null ? SystemProperties.SDL_GPU_DRIVER_OVERRIDE : configDriver;
|
||||
return sdlGpuDriver(configDriver);
|
||||
}
|
||||
|
||||
public static String sdlGpuDriver(String driver) {
|
||||
return SystemProperties.SDL_GPU_DRIVER_OVERRIDE != null ? SystemProperties.SDL_GPU_DRIVER_OVERRIDE : driver;
|
||||
}
|
||||
|
||||
public static boolean configSdlGpu() {
|
||||
|
||||
@@ -1,55 +1,96 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
|
||||
public final class BackendStartGuard {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("Angelica/BackendStartGuard");
|
||||
|
||||
private static File pending;
|
||||
static final int STRIKE_LIMIT = 3;
|
||||
|
||||
private static File marker;
|
||||
private static File verified;
|
||||
private static String key;
|
||||
private static int strikes;
|
||||
private static boolean pending;
|
||||
private static boolean tripped;
|
||||
|
||||
private BackendStartGuard() {}
|
||||
|
||||
public static boolean begin(File marker, boolean sdlGpuConfigured, boolean flagForced) {
|
||||
pending = null;
|
||||
tripped = false;
|
||||
if (!sdlGpuConfigured || flagForced) {
|
||||
public static boolean begin(File dir, boolean sdlGpuConfigured, boolean flagForced, int requirementsRevision, String requestedDriver) {
|
||||
marker = new File(dir, "sdlgpu-starting");
|
||||
verified = new File(dir, "sdlgpu-verified");
|
||||
key = requirementsRevision + "/" + requestedDriver;
|
||||
pending = tripped = false;
|
||||
if (flagForced) return false;
|
||||
if (!sdlGpuConfigured) {
|
||||
delete(marker);
|
||||
return false;
|
||||
}
|
||||
if (marker.isFile()) {
|
||||
if (key.equals(read(verified))) return false;
|
||||
final String count = read(marker);
|
||||
final Integer parsed = count == null ? null : Ints.tryParse(count.trim());
|
||||
strikes = parsed == null ? 0 : parsed;
|
||||
if (strikes >= STRIKE_LIMIT) {
|
||||
LOGGER.warn("SDL GPU failed to bring up the device {} times in a row; resetting the render backend to OpenGL", strikes);
|
||||
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());
|
||||
if (strikes == STRIKE_LIMIT - 1) {
|
||||
LOGGER.warn("SDL GPU failed to bring up the device {} times in a row; one more and the render backend switches to OpenGL", strikes);
|
||||
} else if (strikes > 0) {
|
||||
LOGGER.info("The last start ended while bringing up the SDL GPU device");
|
||||
}
|
||||
pending = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void finish() {
|
||||
if (pending == null) return;
|
||||
delete(pending);
|
||||
pending = null;
|
||||
public static void arm() {
|
||||
if (pending) write(marker, Integer.toString(strikes + 1));
|
||||
}
|
||||
|
||||
public static void disarm() {
|
||||
if (pending) delete(marker);
|
||||
}
|
||||
|
||||
public static void latch() {
|
||||
if (!pending) return;
|
||||
pending = false;
|
||||
write(verified, key);
|
||||
LOGGER.info("SDL GPU booted; the startup guard stays off until the requirements revision or driver changes");
|
||||
}
|
||||
|
||||
public static boolean tripped() {
|
||||
return tripped;
|
||||
}
|
||||
|
||||
private static void delete(File marker) {
|
||||
if (marker.isFile() && !marker.delete()) {
|
||||
LOGGER.warn("Could not delete {}", marker);
|
||||
private static String read(File file) {
|
||||
try {
|
||||
return file.isFile() ? new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8) : null;
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Could not read {}: {}", file, e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void write(File file, String content) {
|
||||
try {
|
||||
Files.createDirectories(file.toPath().getParent());
|
||||
Files.write(file.toPath(), content.getBytes(StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Could not write {}: {}", file, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void delete(File file) {
|
||||
if (file.isFile() && !file.delete()) LOGGER.warn("Could not delete {}", file);
|
||||
}
|
||||
}
|
||||
@@ -609,6 +609,21 @@ public final class Lwjgl2GLRenderBackend extends RenderBackend {
|
||||
GL30.glFramebufferTexture2D(target, attachment, textarget, texture, level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void framebufferTexture1D(int target, int attachment, int textarget, int texture, int level) {
|
||||
GL30.glFramebufferTexture1D(target, attachment, textarget, texture, level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void framebufferTexture3D(int target, int attachment, int textarget, int texture, int level, int zoffset) {
|
||||
GL30.glFramebufferTexture3D(target, attachment, textarget, texture, level, zoffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void framebufferTextureLayer(int target, int attachment, int texture, int level, int layer) {
|
||||
GL30.glFramebufferTextureLayer(target, attachment, texture, level, layer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void framebufferTexture(int target, int attachment, int texture, int level) {
|
||||
GL32.glFramebufferTexture(target, attachment, texture, level);
|
||||
|
||||
@@ -99,7 +99,7 @@ public abstract class RenderBackend {
|
||||
|
||||
static VSyncMode resolveTearFreeMode(VSyncMode preferred, Predicate<VSyncMode> supported) {
|
||||
if (preferred != null && preferred.tearFree() && supported.test(preferred)) return preferred;
|
||||
return supported.test(VSyncMode.MAILBOX) ? VSyncMode.MAILBOX : VSyncMode.ON;
|
||||
return VSyncMode.ON;
|
||||
}
|
||||
|
||||
protected abstract VSyncMode applyVSyncMode(VSyncMode preferred);
|
||||
@@ -457,6 +457,9 @@ public abstract class RenderBackend {
|
||||
public abstract void deleteFramebuffers(int framebuffer);
|
||||
public abstract void bindFramebuffer(int target, int framebuffer);
|
||||
public abstract void framebufferTexture2D(int target, int attachment, int textarget, int texture, int level);
|
||||
public abstract void framebufferTexture1D(int target, int attachment, int textarget, int texture, int level);
|
||||
public abstract void framebufferTexture3D(int target, int attachment, int textarget, int texture, int level, int zoffset);
|
||||
public abstract void framebufferTextureLayer(int target, int attachment, int texture, int level, int layer);
|
||||
public abstract void framebufferTexture(int target, int attachment, int texture, int level);
|
||||
public abstract int genRenderbuffers();
|
||||
public abstract void deleteRenderbuffers(int renderbuffer);
|
||||
|
||||
@@ -128,7 +128,7 @@ public class DSAARB extends DSAUnsupported {
|
||||
|
||||
@Override
|
||||
public void framebufferTexture2D(int fb, int fbtarget, int attachment, int target, int texture, int levels) {
|
||||
RENDER_BACKEND.namedFramebufferTexture(fb, attachment, texture, levels);
|
||||
GLStateManager.glNamedFramebufferTexture(fb, attachment, texture, levels);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -139,7 +139,6 @@ public class DSAARB extends DSAUnsupported {
|
||||
|
||||
@Override
|
||||
public int createTexture(int target) {
|
||||
GLStateManager.requireContext("glCreateTextures");
|
||||
return RENDER_BACKEND.createTextures(target);
|
||||
return GLStateManager.glCreateTextures(target);
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,12 @@ package com.gtnewhorizons.angelica.glsm.dsa;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.TextureBindingStack;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureTargets;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
@@ -153,7 +152,7 @@ public class DSAUnsupported implements DSAAccess {
|
||||
@Override
|
||||
public void framebufferTexture2D(int fb, int fbtarget, int attachment, int target, int texture, int levels) {
|
||||
GLStateManager.glBindFramebuffer(fbtarget, fb);
|
||||
RENDER_BACKEND.framebufferTexture2D(fbtarget, attachment, target, texture, levels);
|
||||
GLStateManager.glFramebufferTexture2D(fbtarget, attachment, target, texture, levels);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -166,23 +165,12 @@ public class DSAUnsupported implements DSAAccess {
|
||||
@Override
|
||||
public int createTexture(int target) {
|
||||
final int texture = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
GLStateManager.glBindTexture(target, texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
private static int bindingQuery(int target) {
|
||||
return switch (target) {
|
||||
case GL11.GL_TEXTURE_1D -> GL11.GL_TEXTURE_BINDING_1D;
|
||||
case GL12.GL_TEXTURE_3D -> GL12.GL_TEXTURE_BINDING_3D;
|
||||
case GL13.GL_TEXTURE_CUBE_MAP -> GL13.GL_TEXTURE_BINDING_CUBE_MAP;
|
||||
case GL30.GL_TEXTURE_2D_ARRAY -> GL30.GL_TEXTURE_BINDING_2D_ARRAY;
|
||||
case GL31.GL_TEXTURE_RECTANGLE -> GL31.GL_TEXTURE_BINDING_RECTANGLE;
|
||||
default -> GL11.GL_TEXTURE_BINDING_2D;
|
||||
};
|
||||
}
|
||||
|
||||
private static int previousBinding(int target) {
|
||||
return target == GL11.GL_TEXTURE_2D ? previous2D() : RENDER_BACKEND.getInteger(bindingQuery(target));
|
||||
return target == GL11.GL_TEXTURE_2D ? previous2D() : RENDER_BACKEND.getInteger(TextureTargets.bindingQuery(target));
|
||||
}
|
||||
|
||||
private static int previous2D() {
|
||||
|
||||
@@ -5,6 +5,7 @@ import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
|
||||
public record CubeParams(float minX, float minY, float minZ, float spanX, float spanY, float spanZ, float u0, float v0, float uScaleX, float vScaleY, float uScaleZ, float vScaleZ) {
|
||||
|
||||
private static final float MIN_SPAN = 1.0e-4f;
|
||||
private static final float UV_EPSILON = 1.0e-5f;
|
||||
|
||||
public CubeParams {
|
||||
if (!(spanX > MIN_SPAN && spanY > MIN_SPAN && spanZ > MIN_SPAN)) throw new IllegalArgumentException("degenerate cube span");
|
||||
@@ -43,12 +44,25 @@ public record CubeParams(float minX, float minY, float minZ, float spanX, float
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTexture(long ptr) {
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 8, uScaleZ);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 12, vScaleZ);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX, u0);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 4, v0);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 8, uScaleX);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 12, vScaleY);
|
||||
public boolean uvInsideTexture() {
|
||||
final float u1 = u0 + 2.0f * (Math.abs(uScaleX) + uScaleZ);
|
||||
final float v1 = v0 + vScaleY + vScaleZ;
|
||||
return u0 >= 0.0f && v0 >= 0.0f && u1 <= 1.0f + UV_EPSILON && v1 <= 1.0f + UV_EPSILON;
|
||||
}
|
||||
|
||||
public static boolean allInsideTexture(CubeParams[] cubes) {
|
||||
for (CubeParams cube : cubes) {
|
||||
if (!cube.uvInsideTexture()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void writeTexture(long ptr, float regionU, float regionV, float regionW, float regionH) {
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 8, uScaleZ * regionW);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_LIGHTMAP_SCALE + 12, vScaleZ * regionH);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX, regionU + u0 * regionW);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 4, regionV + v0 * regionH);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 8, uScaleX * regionW);
|
||||
memPutFloat(ptr + CubeInstancedAttribs.OFFSET_CUBE_TEX + 12, vScaleY * regionH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.states.FogState;
|
||||
|
||||
public final class FfpGlsl {
|
||||
|
||||
private FfpGlsl() {}
|
||||
|
||||
// Mesa ffvertex_prog.c build_fog(): FDM_EYE_RADIAL/EYE_PLANE/EYE_PLANE_ABS
|
||||
public static String fogDistance(int mode, String eye) {
|
||||
return switch (mode) {
|
||||
case FogState.DISTANCE_EYE_RADIAL -> "length(" + eye + ".xyz)";
|
||||
case FogState.DISTANCE_EYE_PLANE -> eye + ".z";
|
||||
default -> "abs(" + eye + ".z)";
|
||||
};
|
||||
}
|
||||
|
||||
public static void appendFogFactor(StringBuilder sb, String indent, int fogMode, String coord, String params) {
|
||||
switch (fogMode) {
|
||||
case FragmentKey.FOG_LINEAR -> sb.append(indent).append("f = ").append(coord).append(" * ").append(params).append(".x + ").append(params).append(".y;\n");
|
||||
case FragmentKey.FOG_EXP -> sb.append(indent).append("f = exp2(-(").append(coord).append(" * ").append(params).append(".z));\n");
|
||||
case FragmentKey.FOG_EXP2 -> {
|
||||
sb.append(indent).append("float fogTmp = ").append(coord).append(" * ").append(params).append(".w;\n");
|
||||
sb.append(indent).append("f = exp2(-(fogTmp * fogTmp));\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String quantizeAlpha(String alpha) {
|
||||
return "round(" + alpha + " * 255.0)";
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,16 @@ public final class FragmentKey {
|
||||
public static final int FOG_EXP = 2;
|
||||
public static final int FOG_EXP2 = 3;
|
||||
|
||||
public static int activeFogMode(GLContextState glCtx) {
|
||||
if (!glCtx.fogMode.isEnabled()) return FOG_NONE;
|
||||
return switch (glCtx.fogState.getFogMode()) {
|
||||
case GL11.GL_LINEAR -> FOG_LINEAR;
|
||||
case GL11.GL_EXP -> FOG_EXP;
|
||||
case GL11.GL_EXP2 -> FOG_EXP2;
|
||||
default -> FOG_NONE;
|
||||
};
|
||||
}
|
||||
|
||||
private static final int GLOBAL_BITS = 14;
|
||||
private static final int BIT_COMBINED_GLINT = 61;
|
||||
private static final int BIT_GLINT_REPLACE_ALPHA = 62;
|
||||
@@ -117,15 +127,7 @@ public final class FragmentKey {
|
||||
if (CombinedGlint.replacesAlpha()) global |= 1L << BIT_GLINT_REPLACE_ALPHA;
|
||||
|
||||
// Fog
|
||||
if (glCtx.fogMode.isEnabled()) {
|
||||
final int fogMode = switch (glCtx.fogState.getFogMode()) {
|
||||
case GL11.GL_LINEAR -> FOG_LINEAR;
|
||||
case GL11.GL_EXP -> FOG_EXP;
|
||||
case GL11.GL_EXP2 -> FOG_EXP2;
|
||||
default -> FOG_NONE;
|
||||
};
|
||||
global |= (long) fogMode;
|
||||
}
|
||||
global |= activeFogMode(glCtx);
|
||||
|
||||
// Alpha test
|
||||
if (glCtx.alphaTest.isEnabled()) {
|
||||
|
||||
+2
-13
@@ -305,7 +305,7 @@ public final class FragmentShaderGenerator {
|
||||
final int func = FragmentKey.decodeAlphaFunc(key.alphaTestFunc());
|
||||
|
||||
|
||||
sb.append(" float qa = round(color.a * 255.0);\n");
|
||||
sb.append(" float qa = ").append(FfpGlsl.quantizeAlpha("color.a")).append(";\n");
|
||||
switch (func) {
|
||||
case GL11.GL_NEVER ->
|
||||
sb.append(" discard;\n");
|
||||
@@ -335,18 +335,7 @@ public final class FragmentShaderGenerator {
|
||||
|
||||
sb.append(" // Fog\n");
|
||||
sb.append(" float f;\n");
|
||||
|
||||
switch (fogMode) {
|
||||
case FragmentKey.FOG_LINEAR ->
|
||||
sb.append(" f = v_FogCoord * u_FogParams.x + u_FogParams.y;\n");
|
||||
case FragmentKey.FOG_EXP ->
|
||||
sb.append(" f = exp2(-(v_FogCoord * u_FogParams.z));\n");
|
||||
case FragmentKey.FOG_EXP2 -> {
|
||||
sb.append(" float fogTmp = v_FogCoord * u_FogParams.w;\n");
|
||||
sb.append(" f = exp2(-(fogTmp * fogTmp));\n");
|
||||
}
|
||||
}
|
||||
|
||||
FfpGlsl.appendFogFactor(sb, " ", fogMode, "v_FogCoord", "u_FogParams");
|
||||
sb.append(" f = clamp(f, 0.0, 1.0);\n");
|
||||
sb.append(" color.rgb = mix(u_FogColor.rgb, color.rgb, f);\n");
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ public final class InstancedAttribs {
|
||||
public static final int OFFSET_LIGHTMAP = HEAD_SIZE;
|
||||
public static final int STRIDE = 72;
|
||||
|
||||
public static final long DEFAULT_ENTITY_IDS = packEntityInfo(-1, 0, -1);
|
||||
|
||||
public static void writeHead(long ptr, float[] colMajor, int off, int colorABGR, int overlayABGR, long entityInfo) {
|
||||
writeRows(ptr, colMajor, off);
|
||||
writeTail(ptr, colorABGR, overlayABGR, entityInfo);
|
||||
@@ -60,13 +62,21 @@ public final class InstancedAttribs {
|
||||
return id == 0xFFFF ? -1 : id;
|
||||
}
|
||||
|
||||
public static float normalizedId(long info, int shift) {
|
||||
return ((info >>> shift) & 0xFFFF) / 65535.0f;
|
||||
}
|
||||
|
||||
public static void pointHead(long base, int stride) {
|
||||
for (int r = 0; r < 3; r++) {
|
||||
GLStateManager.glVertexAttribPointer(LOC_ROW0 + r, 4, GL11.GL_FLOAT, false, stride, base + OFFSET_ROWS + r * 16L);
|
||||
}
|
||||
GLStateManager.glVertexAttribPointer(LOC_COLOR, 4, GL11.GL_UNSIGNED_BYTE, true, stride, base + OFFSET_COLOR);
|
||||
GLStateManager.glVertexAttribPointer(LOC_OVERLAY, 4, GL11.GL_UNSIGNED_BYTE, true, stride, base + OFFSET_OVERLAY);
|
||||
GLStateManager.glVertexAttribIPointer(LOC_ENTITY, 4, GL11.GL_UNSIGNED_SHORT, stride, base + OFFSET_ENTITY);
|
||||
pointEntityIds(LOC_ENTITY, stride, base + OFFSET_ENTITY);
|
||||
}
|
||||
|
||||
public static void pointEntityIds(int location, int stride, long offset) {
|
||||
GLStateManager.glVertexAttribPointer(location, 4, GL11.GL_UNSIGNED_SHORT, true, stride, offset);
|
||||
}
|
||||
|
||||
public static void pointTemplate(long base) {
|
||||
|
||||
@@ -139,6 +139,7 @@ public final class ShaderManager {
|
||||
GLSMHooks.resolvePendingProgram();
|
||||
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
|
||||
if (bh != null) bh.flushDeferredBlend();
|
||||
GLStateManager.flushEntityIds(glCtx);
|
||||
|
||||
// Handle FFP & Iris uniforms
|
||||
final int currentProgramId = glCtx.activeProgram;
|
||||
@@ -198,18 +199,21 @@ public final class ShaderManager {
|
||||
}
|
||||
|
||||
public void setNormal(float x, float y, float z) {
|
||||
GLStateManager.recordExitNormal();
|
||||
normalStack.beforeModify();
|
||||
currentNormal.set(x, y, z);
|
||||
normalGeneration++;
|
||||
}
|
||||
|
||||
public void setTexCoord(float s, float t, float r, float q) {
|
||||
GLStateManager.recordExitTexCoord(0);
|
||||
texCoordStack.beforeModify();
|
||||
currentTexCoords[0].set(s, t, r, q);
|
||||
texCoordGeneration++;
|
||||
}
|
||||
|
||||
public void setTexCoord(int unit, float s, float t, float r, float q) {
|
||||
GLStateManager.recordExitTexCoord(unit);
|
||||
if (unit == 0) texCoordStack.beforeModify();
|
||||
currentTexCoords[unit].set(s, t, r, q);
|
||||
texCoordGeneration++;
|
||||
|
||||
@@ -80,9 +80,6 @@ public class Uniforms {
|
||||
private final Vector3f tempVec3 = new Vector3f();
|
||||
private final FloatBuffer clipPlaneBuf = memAllocFloat(32); // 8 planes * vec4
|
||||
|
||||
private static final double LN2 = java.lang.Math.log(2.0);
|
||||
private static final double SQRT_LN2 = java.lang.Math.sqrt(LN2);
|
||||
|
||||
public void upload(GLContextState glCtx) {
|
||||
boolean dirty = false;
|
||||
contentChanged = false;
|
||||
@@ -233,8 +230,11 @@ public class Uniforms {
|
||||
if (projChanged) {
|
||||
putMat4(FFPUniformBlock.PROJECTION_MATRIX, proj);
|
||||
}
|
||||
proj.mul(mv, mvpMatrix);
|
||||
putMat4(FFPUniformBlock.MVP_MATRIX, mvpMatrix);
|
||||
putMat4(FFPUniformBlock.MVP_MATRIX, modelViewProjection(proj, mv, mvpMatrix));
|
||||
}
|
||||
|
||||
public static Matrix4f modelViewProjection(Matrix4f proj, Matrix4f mv, Matrix4f dest) {
|
||||
return proj.mul(mv, dest);
|
||||
}
|
||||
|
||||
private void stageNormalMatrix(GLContextState glCtx) {
|
||||
@@ -343,17 +343,8 @@ public class Uniforms {
|
||||
final var secondary = glCtx.secondaryColor;
|
||||
putVec3(FFPUniformBlock.SECONDARY_COLOR, secondary.getRed(), secondary.getGreen(), secondary.getBlue());
|
||||
|
||||
// Mesa STATE_FOG_PARAMS_OPTIMIZED
|
||||
final FogState fog = glCtx.fogState;
|
||||
final float start = fog.getStart();
|
||||
final float end = fog.getEnd();
|
||||
final float density = fog.getDensity();
|
||||
final float range = end - start;
|
||||
putVec4(FFPUniformBlock.FOG_PARAMS,
|
||||
range != 0.0f ? -1.0f / range : 0.0f,
|
||||
range != 0.0f ? end / range : 1.0f,
|
||||
(float) (density / LN2),
|
||||
(float) (density / SQRT_LN2));
|
||||
putVec4(FFPUniformBlock.FOG_PARAMS, fog.linearScale(), fog.linearBias(), fog.expScale(), fog.exp2Scale());
|
||||
putVec4(FFPUniformBlock.FOG_COLOR,
|
||||
(float) fog.getFogColor().x, (float) fog.getFogColor().y, (float) fog.getFogColor().z,
|
||||
fog.getFogAlpha());
|
||||
@@ -420,6 +411,7 @@ public class Uniforms {
|
||||
if (ring != null) {
|
||||
ring.endFrame();
|
||||
}
|
||||
invalidateBinding();
|
||||
lastFrameBlockWrites = blockWrites;
|
||||
lastFrameBlockSkips = blockSkips;
|
||||
lastFrameStagedMatrices = stagedMatrices;
|
||||
|
||||
@@ -100,9 +100,9 @@ public final class VertexShaderGenerator {
|
||||
private static void emitOutputs(StringBuilder sb, VertexKey key) {
|
||||
sb.append("// Outputs\n");
|
||||
sb.append("out vec4 v_Color;\n");
|
||||
sb.append("invariant gl_Position;\n");
|
||||
if (key.instancing().hasInstanceHead()) {
|
||||
sb.append("out vec4 v_Overlay;\n");
|
||||
sb.append("invariant gl_Position;\n");
|
||||
}
|
||||
if (key.lineStipple()) {
|
||||
sb.append("flat out vec2 v_LineStart;\n");
|
||||
@@ -414,20 +414,11 @@ public final class VertexShaderGenerator {
|
||||
|
||||
private static void emitFogDistance(StringBuilder sb, VertexKey key) {
|
||||
sb.append(" // Fog distance\n");
|
||||
final String own = fogDistance(key, "eyePos");
|
||||
final String fog = key.lineQuads() ? acrossLine(own, fogDistance(key, "lineEyeOther")) : own;
|
||||
final String own = FfpGlsl.fogDistance(key.fogDistanceMode(), "eyePos");
|
||||
final String fog = key.lineQuads() ? acrossLine(own, FfpGlsl.fogDistance(key.fogDistanceMode(), "lineEyeOther")) : own;
|
||||
sb.append(" v_FogCoord = ").append(fog).append(";\n");
|
||||
}
|
||||
|
||||
// Mesa ffvertex_prog.c build_fog(): FDM_EYE_RADIAL/EYE_PLANE/EYE_PLANE_ABS
|
||||
private static String fogDistance(VertexKey key, String eye) {
|
||||
return switch (key.fogDistanceMode()) {
|
||||
case 0 -> "length(" + eye + ".xyz)"; // FDM_EYE_RADIAL: Euclidean distance
|
||||
case 1 -> eye + ".z"; // FDM_EYE_PLANE: raw Z (can be negative, clamped by fog factor)
|
||||
default -> "abs(" + eye + ".z)"; // FDM_EYE_PLANE_ABS: absolute Z
|
||||
};
|
||||
}
|
||||
|
||||
private static void emitClipDistances(StringBuilder sb, VertexKey key) {
|
||||
sb.append(" // Clip distances\n");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
|
||||
public interface DeferredAlphaHandler {
|
||||
boolean isAlphaTestLocked();
|
||||
boolean isOverrideHeld();
|
||||
boolean deferAlphaTestToggle(boolean enabled);
|
||||
boolean deferAlphaFunc(int function, float reference);
|
||||
}
|
||||
@@ -26,6 +26,7 @@ public final class FrameHooks {
|
||||
}
|
||||
|
||||
public static void frameEnd() {
|
||||
GLStateManager.onFrameEnd();
|
||||
popFrameGroup();
|
||||
RENDER_BACKEND.onFrameEnd();
|
||||
}
|
||||
|
||||
+10
@@ -3,15 +3,18 @@ package com.gtnewhorizons.angelica.glsm.hooks;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
|
||||
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public interface ImmediateExtendedAttribHandler {
|
||||
|
||||
int EXT_STRIDE = 12;
|
||||
int ENTITY_IDS_STRIDE = 8;
|
||||
int LOC_MC_ENTITY = 11;
|
||||
int LOC_MID_TEX = 12;
|
||||
int LOC_TANGENT = 13;
|
||||
int LOC_MIDBLOCK = 14;
|
||||
int LOC_ENTITY_IDS = 15;
|
||||
|
||||
int RAW_NORMAL_INDEX = 6;
|
||||
|
||||
@@ -19,6 +22,8 @@ public interface ImmediateExtendedAttribHandler {
|
||||
|
||||
boolean wantsTangent();
|
||||
|
||||
boolean wantsEntityIds();
|
||||
|
||||
boolean wantsExtendedCapture();
|
||||
|
||||
boolean wantsFaceNormals();
|
||||
@@ -65,4 +70,9 @@ public interface ImmediateExtendedAttribHandler {
|
||||
GLStateManager.glEnableVertexAttribArray(LOC_TANGENT);
|
||||
GLStateManager.glVertexAttribPointer(LOC_TANGENT, 4, GL11.GL_BYTE, true, stride, baseOffset + 8);
|
||||
}
|
||||
|
||||
static void setupEntityIdsPointer(long offset, int stride) {
|
||||
GLStateManager.glEnableVertexAttribArray(LOC_ENTITY_IDS);
|
||||
InstancedAttribs.pointEntityIds(LOC_ENTITY_IDS, stride, offset);
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,31 @@ public final class TextureUtilHooks {
|
||||
GLStateManager.ctx().savedFilterTextureId = 0;
|
||||
}
|
||||
|
||||
public static boolean beginFilterSave(boolean blurred, boolean mipmap, float aniso) {
|
||||
captureBoundFilterTexture();
|
||||
return GLStateManager.beginExitTextureFilter(!blurred && !mipmap && aniso == 1);
|
||||
}
|
||||
|
||||
public static void endFilterSave(boolean captured) {
|
||||
if (captured) GLStateManager.finishExitTextureFilterSave();
|
||||
}
|
||||
|
||||
public static boolean beginFilterRestore() {
|
||||
return GLStateManager.beginExitTextureFilterRestore();
|
||||
}
|
||||
|
||||
public static void endFilterRestore(boolean captured) {
|
||||
try {
|
||||
if (captured) GLStateManager.finishExitTextureFilterRestore();
|
||||
} finally {
|
||||
clearSavedFilterTexture();
|
||||
}
|
||||
}
|
||||
|
||||
public static void filterFailed(boolean captured) {
|
||||
if (captured) GLStateManager.failExitTextureFilter();
|
||||
}
|
||||
|
||||
public static void restoreFilter(int min, int mag) {
|
||||
final int id = GLStateManager.ctx().savedFilterTextureId;
|
||||
if (id != 0 && DisplayListManager.getRecordMode() == DisplayListManager.RecordMode.NONE) {
|
||||
|
||||
@@ -167,6 +167,14 @@ public class GLSMRedirector {
|
||||
private static final String EXTTimerQuery = "org/lwjgl/opengl/EXTTimerQuery";
|
||||
private static final String ARBSamplerObjects = "org/lwjgl/opengl/ARBSamplerObjects";
|
||||
private static final String ARBMultiBind = "org/lwjgl/opengl/ARBMultiBind";
|
||||
private static final String ARBDirectStateAccess = "org/lwjgl/opengl/ARBDirectStateAccess";
|
||||
private static final String ARBGeometryShader4 = "org/lwjgl/opengl/ARBGeometryShader4";
|
||||
private static final String EXTGeometryShader4 = "org/lwjgl/opengl/EXTGeometryShader4";
|
||||
private static final String NVGeometryProgram4 = "org/lwjgl/opengl/NVGeometryProgram4";
|
||||
private static final String EXTTextureArray = "org/lwjgl/opengl/EXTTextureArray";
|
||||
private static final String EXTDirectStateAccess = "org/lwjgl/opengl/EXTDirectStateAccess";
|
||||
private static final String ARBShaderImageLoadStore = "org/lwjgl/opengl/ARBShaderImageLoadStore";
|
||||
private static final String EXTShaderImageLoadStore = "org/lwjgl/opengl/EXTShaderImageLoadStore";
|
||||
|
||||
// Redirect VAO related calls from NHLib
|
||||
private static final String UniversalVAO = "com/gtnewhorizon/gtnhlib/client/opengl/UniversalVAO";
|
||||
@@ -513,7 +521,10 @@ public class GLSMRedirector {
|
||||
.add("glDeleteFramebuffers")
|
||||
.add("glGenFramebuffers")
|
||||
.add("glCheckFramebufferStatus")
|
||||
.add("glFramebufferTexture1D")
|
||||
.add("glFramebufferTexture2D")
|
||||
.add("glFramebufferTexture3D")
|
||||
.add("glFramebufferTextureLayer")
|
||||
.add("glVertexAttribIPointer")
|
||||
.add("glGenerateMipmap")
|
||||
.add("glGetFramebufferAttachmentParameteri")
|
||||
@@ -567,11 +578,18 @@ public class GLSMRedirector {
|
||||
final var gl44 = RedirectMap.newMap()
|
||||
.add("glBufferStorage")
|
||||
.add("glBindSamplers")
|
||||
.add("glClearTexImage");
|
||||
.add("glClearTexImage")
|
||||
.add("glBindTextures")
|
||||
.add("glBindImageTextures");
|
||||
final var gl45 = RedirectMap.newMap()
|
||||
.add("glCreateBuffers")
|
||||
.add("glNamedBufferData")
|
||||
.add("glNamedBufferSubData");
|
||||
.add("glNamedBufferSubData")
|
||||
.add("glNamedFramebufferTexture")
|
||||
.add("glNamedFramebufferTextureLayer")
|
||||
.add("glNamedFramebufferRenderbuffer")
|
||||
.add("glCreateTextures")
|
||||
.add("glBindTextureUnit");
|
||||
final var gl46 = RedirectMap.newMap()
|
||||
.add("glPolygonOffsetClamp")
|
||||
.add("glMultiDrawArraysIndirectCount")
|
||||
@@ -630,7 +648,10 @@ public class GLSMRedirector {
|
||||
.add("glDeleteFramebuffers")
|
||||
.add("glGenFramebuffers")
|
||||
.add("glCheckFramebufferStatus")
|
||||
.add("glFramebufferTexture1D")
|
||||
.add("glFramebufferTexture2D")
|
||||
.add("glFramebufferTexture3D")
|
||||
.add("glFramebufferTextureLayer")
|
||||
.add("glBindRenderbuffer")
|
||||
.add("glDeleteRenderbuffers")
|
||||
.add("glGenRenderbuffers")
|
||||
@@ -643,7 +664,9 @@ public class GLSMRedirector {
|
||||
.add("glDeleteFramebuffersEXT", "glDeleteFramebuffers")
|
||||
.add("glGenFramebuffersEXT", "glGenFramebuffers")
|
||||
.add("glCheckFramebufferStatusEXT", "glCheckFramebufferStatus")
|
||||
.add("glFramebufferTexture1DEXT", "glFramebufferTexture1D")
|
||||
.add("glFramebufferTexture2DEXT", "glFramebufferTexture2D")
|
||||
.add("glFramebufferTexture3DEXT", "glFramebufferTexture3D")
|
||||
.add("glBindRenderbufferEXT", "glBindRenderbuffer")
|
||||
.add("glDeleteRenderbuffersEXT", "glDeleteRenderbuffers")
|
||||
.add("glGenRenderbuffersEXT", "glGenRenderbuffers")
|
||||
@@ -793,6 +816,45 @@ public class GLSMRedirector {
|
||||
);
|
||||
methodRedirects.put(ARBMultiBind, RedirectMap.newMap()
|
||||
.add("glBindSamplers")
|
||||
.add("glBindTextures")
|
||||
.add("glBindImageTextures")
|
||||
);
|
||||
methodRedirects.put(ARBDirectStateAccess, RedirectMap.newMap()
|
||||
.add("glNamedFramebufferTexture")
|
||||
.add("glNamedFramebufferTextureLayer")
|
||||
.add("glNamedFramebufferRenderbuffer")
|
||||
.add("glCreateTextures")
|
||||
.add("glBindTextureUnit")
|
||||
);
|
||||
methodRedirects.put(ARBGeometryShader4, RedirectMap.newMap()
|
||||
.add("glFramebufferTextureARB", "glFramebufferTexture")
|
||||
.add("glFramebufferTextureLayerARB", "glFramebufferTextureLayer")
|
||||
.add("glFramebufferTextureFaceARB", "glFramebufferTextureFace")
|
||||
);
|
||||
final var geometryShader4 = RedirectMap.newMap()
|
||||
.add("glFramebufferTextureEXT", "glFramebufferTexture")
|
||||
.add("glFramebufferTextureLayerEXT", "glFramebufferTextureLayer")
|
||||
.add("glFramebufferTextureFaceEXT", "glFramebufferTextureFace");
|
||||
methodRedirects.put(EXTGeometryShader4, geometryShader4);
|
||||
methodRedirects.put(NVGeometryProgram4, geometryShader4);
|
||||
methodRedirects.put(EXTTextureArray, RedirectMap.newMap()
|
||||
.add("glFramebufferTextureLayerEXT", "glFramebufferTextureLayer")
|
||||
);
|
||||
methodRedirects.put(EXTDirectStateAccess, RedirectMap.newMap()
|
||||
.add("glNamedFramebufferTexture1DEXT")
|
||||
.add("glNamedFramebufferTexture2DEXT")
|
||||
.add("glNamedFramebufferTexture3DEXT")
|
||||
.add("glNamedFramebufferTextureEXT")
|
||||
.add("glNamedFramebufferTextureLayerEXT", "glNamedFramebufferTextureLayer")
|
||||
.add("glNamedFramebufferTextureFaceEXT")
|
||||
.add("glNamedFramebufferRenderbufferEXT", "glNamedFramebufferRenderbuffer")
|
||||
.add("glBindMultiTextureEXT")
|
||||
);
|
||||
methodRedirects.put(ARBShaderImageLoadStore, RedirectMap.newMap()
|
||||
.add("glBindImageTexture")
|
||||
);
|
||||
methodRedirects.put(EXTShaderImageLoadStore, RedirectMap.newMap()
|
||||
.add("glBindImageTextureEXT", "glBindImageTexture")
|
||||
);
|
||||
|
||||
// APPLE
|
||||
|
||||
+25
-6
@@ -34,6 +34,17 @@ public final class GlslVulkanPreprocess {
|
||||
"const_cast", "dynamic_cast", "reinterpret_cast", "static_cast", "export",
|
||||
"and", "or", "xor", "bitand", "bitor", "compl", "and_eq", "or_eq", "xor_eq", "not_eq");
|
||||
|
||||
private static final Object2ObjectLinkedOpenHashMap<String, String> IDENTIFIER_RENAMES = identifierRenames();
|
||||
|
||||
private static Object2ObjectLinkedOpenHashMap<String, String> identifierRenames() {
|
||||
final Object2ObjectLinkedOpenHashMap<String, String> renames = new Object2ObjectLinkedOpenHashMap<>();
|
||||
renames.put("sampler", SAMPLER_RENAMED);
|
||||
renames.put("gl_VertexID", "gl_VertexIndex");
|
||||
renames.put("gl_InstanceID", "gl_InstanceIndex");
|
||||
for (String reserved : TARGET_RESERVED) renames.put(reserved, RESERVED_RENAMED_PREFIX + reserved);
|
||||
return renames;
|
||||
}
|
||||
|
||||
public static final String SAMPLERLESS_EXTENSION = "#extension GL_EXT_samplerless_texture_functions : require";
|
||||
|
||||
private static final int MAX_VS_INPUT_LOCATIONS = 16;
|
||||
@@ -191,14 +202,13 @@ public final class GlslVulkanPreprocess {
|
||||
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));
|
||||
addSamplerlessExtension(index, edits);
|
||||
}
|
||||
|
||||
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);
|
||||
for (var it = IDENTIFIER_RENAMES.object2ObjectEntrySet().fastIterator(); it.hasNext(); ) {
|
||||
final var rename = it.next();
|
||||
rename(index, rename.getKey(), rename.getValue(), edits);
|
||||
}
|
||||
|
||||
if (maxExplicitLoc >= 0 && !unlocatedVsInputStarts.isEmpty()) {
|
||||
int next = maxExplicitLoc + 1;
|
||||
@@ -216,6 +226,15 @@ public final class GlslVulkanPreprocess {
|
||||
return new Metadata(bools, explicitInputs, needsSamplerless);
|
||||
}
|
||||
|
||||
public static boolean renamesIdentifier(String name) {
|
||||
return IDENTIFIER_RENAMES.containsKey(name);
|
||||
}
|
||||
|
||||
public static void addSamplerlessExtension(ShaderIndex index, List<Edit> edits) {
|
||||
final int versionEnd = index.directives().versionEnd();
|
||||
if (versionEnd >= 0) edits.add(new Edit(versionEnd, versionEnd - 1, "\n" + SAMPLERLESS_EXTENSION));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ import java.util.Set;
|
||||
*/
|
||||
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);
|
||||
public record Directives(Set<String> identifiers, Set<String> definedMacros, boolean hasConditionalOrMacro, int versionEnd) {
|
||||
private static final Directives NONE = new Directives(Set.of(), Set.of(), false, -1);
|
||||
}
|
||||
|
||||
private final GLSLParser.Translation_unitContext root;
|
||||
@@ -83,6 +83,7 @@ public final class ShaderIndex {
|
||||
if (source.indexOf('#') < 0) return Directives.NONE;
|
||||
final GLSLLexer lexer = GlslTransformUtils.quietLexer(source);
|
||||
final Set<String> names = new HashSet<>();
|
||||
final Set<String> macros = new HashSet<>();
|
||||
boolean conditionalOrMacro = false;
|
||||
int versionEnd = -1;
|
||||
boolean inVersion = false;
|
||||
@@ -100,11 +101,17 @@ public final class ShaderIndex {
|
||||
inVersion = false;
|
||||
switch (type) {
|
||||
case GLSLLexer.PROGRAM_TEXT, GLSLLexer.MACRO_TEXT, GLSLLexer.CONSTANT_EXPRESSION -> collectIdentifiers(token.getText(), names);
|
||||
case GLSLLexer.MACRO_NAME -> macros.add(leadingIdentifier(token.getText()));
|
||||
case GLSLLexer.IF_DIRECTIVE, GLSLLexer.IFDEF_DIRECTIVE, GLSLLexer.IFNDEF_DIRECTIVE, GLSLLexer.DEFINE_DIRECTIVE -> conditionalOrMacro = true;
|
||||
default -> { }
|
||||
}
|
||||
}
|
||||
return new Directives(names, conditionalOrMacro, versionEnd);
|
||||
return new Directives(names, macros, conditionalOrMacro, versionEnd);
|
||||
}
|
||||
|
||||
private static String leadingIdentifier(String text) {
|
||||
final int args = text.indexOf('(');
|
||||
return args < 0 ? text : text.substring(0, args);
|
||||
}
|
||||
|
||||
private static void collectIdentifiers(String text, Set<String> out) {
|
||||
|
||||
@@ -96,6 +96,7 @@ public class BooleanStateStack extends BooleanState implements CowStateStack<Boo
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
final GLContextState o = cow.owner;
|
||||
GLStateManager.recordExitCapability(o, glCap, -1);
|
||||
if (o != null && o.attribDepth > 0
|
||||
&& (stateUnknown || !GLStateManager.isCachingEnabled() || enabled != vanillaValue())) {
|
||||
beforeModify();
|
||||
|
||||
+1
@@ -24,6 +24,7 @@ public final class TextureUnitBooleanStateStack extends BooleanStateStack {
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
GLStateManager.recordExitCapability(cowDepths().owner, glCap, unitIndex);
|
||||
if (!GLStateManager.isCachingEnabled() || enabled != this.enabled) {
|
||||
GLStateManager.beforeUncapturedTextureUnitChange(glCap, unitIndex);
|
||||
beforeModify();
|
||||
|
||||
@@ -9,6 +9,13 @@ import java.nio.FloatBuffer;
|
||||
|
||||
@Getter @Setter
|
||||
public class FogState implements ISettableState<FogState> {
|
||||
public static final int DISTANCE_EYE_RADIAL = 0;
|
||||
public static final int DISTANCE_EYE_PLANE = 1;
|
||||
public static final int DISTANCE_EYE_PLANE_ABS = 2;
|
||||
|
||||
private static final double LN2 = Math.log(2.0);
|
||||
private static final double SQRT_LN2 = Math.sqrt(LN2);
|
||||
|
||||
protected int fogMode = GL11.GL_EXP;
|
||||
protected final Vector3d fogColor = new Vector3d(0.0F, 0.0F, 0.0F);
|
||||
protected float fogAlpha = 1.0F;
|
||||
@@ -16,8 +23,26 @@ public class FogState implements ISettableState<FogState> {
|
||||
protected float density = 1.0F;
|
||||
protected float start;
|
||||
protected float end = 1.0F;
|
||||
/** Fog distance computation mode: 0=EYE_RADIAL, 1=EYE_PLANE, 2=EYE_PLANE_ABS */
|
||||
protected int fogDistanceMode = 2;
|
||||
protected int fogDistanceMode = DISTANCE_EYE_PLANE_ABS;
|
||||
|
||||
// Mesa STATE_FOG_PARAMS_OPTIMIZED
|
||||
public float linearScale() {
|
||||
final float range = end - start;
|
||||
return range != 0.0f ? -1.0f / range : 0.0f;
|
||||
}
|
||||
|
||||
public float linearBias() {
|
||||
final float range = end - start;
|
||||
return range != 0.0f ? end / range : 1.0f;
|
||||
}
|
||||
|
||||
public float expScale() {
|
||||
return (float) (density / LN2);
|
||||
}
|
||||
|
||||
public float exp2Scale() {
|
||||
return (float) (density / SQRT_LN2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FogState set(FogState state) {
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package com.gtnewhorizons.angelica.glsm.states;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
public class ImageUnitBinding implements ISettableState<ImageUnitBinding> {
|
||||
@Getter protected int texture;
|
||||
@Getter protected int level;
|
||||
@Getter protected boolean layered;
|
||||
@Getter protected int layer;
|
||||
@Getter protected int access;
|
||||
@Getter protected int format;
|
||||
@Getter protected int access = GL15.GL_READ_ONLY;
|
||||
@Getter protected int format = GL30.GL_R8;
|
||||
|
||||
public void setBinding(int texture, int level, boolean layered, int layer, int access, int format) {
|
||||
this.texture = texture;
|
||||
@@ -19,6 +21,10 @@ public class ImageUnitBinding implements ISettableState<ImageUnitBinding> {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
setBinding(0, 0, false, 0, GL15.GL_READ_ONLY, GL30.GL_R8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageUnitBinding set(ImageUnitBinding state) {
|
||||
this.texture = state.texture;
|
||||
|
||||
@@ -25,6 +25,7 @@ final class GlStreamingRing {
|
||||
|
||||
private static final long SYNC_WAIT_SLICE_NANOS = 10_000_000L;
|
||||
private static final long SYNC_WAIT_TOTAL_NANOS = 5_000_000_000L;
|
||||
private static long totalFencesIssued;
|
||||
|
||||
private final FenceQueue fences = new FenceQueue();
|
||||
private int writePos;
|
||||
@@ -68,6 +69,9 @@ final class GlStreamingRing {
|
||||
/** Byte offset of a reserved range, or -1 when it can never be served. */
|
||||
long reserve(int bytes, int alignment) {
|
||||
if (bytes > capacity) return -1;
|
||||
if (pendingBytes >= capacity / 4) {
|
||||
fence();
|
||||
}
|
||||
if (remaining < capacity / 2) {
|
||||
reclaim();
|
||||
}
|
||||
@@ -82,9 +86,6 @@ final class GlStreamingRing {
|
||||
writePos = offset + bytes;
|
||||
remaining -= waste + bytes;
|
||||
pendingBytes += waste + bytes;
|
||||
if (pendingBytes >= capacity / 4) {
|
||||
fence();
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
@@ -104,6 +105,7 @@ final class GlStreamingRing {
|
||||
fences.enqueue(GLStateManager.glFenceSync(GL32.GL_SYNC_GPU_COMMANDS_COMPLETE, 0), pendingBytes);
|
||||
pendingBytes = 0;
|
||||
fencesIssued++;
|
||||
totalFencesIssued++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +156,7 @@ final class GlStreamingRing {
|
||||
int remaining() { return remaining; }
|
||||
int wraps() { return wraps; }
|
||||
int fencesIssued() { return fencesIssued; }
|
||||
static long totalFencesIssued() { return totalFencesIssued; }
|
||||
int forcedReclaims() { return forcedReclaims; }
|
||||
|
||||
void destroy() {
|
||||
|
||||
+3
-1
@@ -45,12 +45,14 @@ public final class PersistentStreamingBuffer {
|
||||
public int getBufferId() { return ring.bufferId(); }
|
||||
public int getCapacity() { return ring.capacity(); }
|
||||
public int getRemaining() { return ring.remaining(); }
|
||||
public int getFencesIssued() { return ring.fencesIssued(); }
|
||||
public int getForcedReclaims() { return ring.forcedReclaims(); }
|
||||
public int getWraps() { return ring.wraps(); }
|
||||
public static long getFencesIssuedByAllRings() { return GlStreamingRing.totalFencesIssued(); }
|
||||
|
||||
ByteBuffer getMappedBuffer() { return ring.mapped(); }
|
||||
|
||||
public void postDraw() {
|
||||
public void endFrame() {
|
||||
ring.endFrame();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,25 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
|
||||
public final class StreamingVaos {
|
||||
private static final int FORMAT_COUNT = VertexFlags.BITSET_SIZE;
|
||||
|
||||
static final int MID_TEX_AND_TANGENT = 1;
|
||||
static final int ENTITY_IDS = 2;
|
||||
private static final int EXTENDED_LAYOUTS = MID_TEX_AND_TANGENT | ENTITY_IDS;
|
||||
|
||||
final int[] persistentVAOs = new int[FORMAT_COUNT];
|
||||
final int[] orphanVAOs = new int[FORMAT_COUNT];
|
||||
|
||||
final int[] extendedPersistentVAOs = new int[FORMAT_COUNT];
|
||||
final int[] extendedOrphanVAOs = new int[FORMAT_COUNT];
|
||||
// An extended draw always carries at least one layout, so row 0 is layout 1
|
||||
private final int[][] extendedPersistentVAOs = new int[EXTENDED_LAYOUTS][FORMAT_COUNT];
|
||||
private final int[][] extendedOrphanVAOs = new int[EXTENDED_LAYOUTS][FORMAT_COUNT];
|
||||
|
||||
final int[] lineQuadPersistentVAOs = new int[FORMAT_COUNT];
|
||||
final int[] lineQuadOrphanVAOs = new int[FORMAT_COUNT];
|
||||
|
||||
int[] extendedPersistent(int layout) {
|
||||
return extendedPersistentVAOs[layout - 1];
|
||||
}
|
||||
|
||||
int[] extendedOrphan(int layout) {
|
||||
return extendedOrphanVAOs[layout - 1];
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutLong;
|
||||
|
||||
/**
|
||||
* Entity, block entity, and item ids of the vertices in the main Tessellator batch.
|
||||
*/
|
||||
public final class TessellatorEntityIds {
|
||||
|
||||
private static Tessellator owner;
|
||||
private static int runCount;
|
||||
private static int[] runStarts = new int[8];
|
||||
private static long[] runIds = new long[8];
|
||||
|
||||
private TessellatorEntityIds() {}
|
||||
|
||||
public static void onIdsChanged(long previous, long next) {
|
||||
final Tessellator tess = Tessellator.instance;
|
||||
if (tess == null || !tess.isDrawing || tess.vertexCount == 0) return;
|
||||
final int start = tess.vertexCount;
|
||||
if (owner != tess) {
|
||||
owner = tess;
|
||||
runCount = 0;
|
||||
}
|
||||
if (runCount == 0) addRun(0, previous);
|
||||
if (runStarts[runCount - 1] == start) {
|
||||
runIds[runCount - 1] = next;
|
||||
if (runCount > 1 && runIds[runCount - 2] == next) runCount--;
|
||||
} else {
|
||||
addRun(start, next);
|
||||
}
|
||||
}
|
||||
|
||||
public static void onReset(Tessellator tess) {
|
||||
if (tess != owner) return;
|
||||
owner = null;
|
||||
runCount = 0;
|
||||
}
|
||||
|
||||
public static boolean hasIdChanges(Tessellator tess) {
|
||||
return tess == owner && runCount > 1;
|
||||
}
|
||||
|
||||
static int runsFor(Tessellator tess, int vertexCount) {
|
||||
if (tess != owner) return 0;
|
||||
int runs = runCount;
|
||||
while (runs > 0 && runStarts[runs - 1] >= vertexCount) runs--;
|
||||
return runs;
|
||||
}
|
||||
|
||||
static long firstRunIds() {
|
||||
return runIds[0];
|
||||
}
|
||||
|
||||
static void write(long dst, int stride, int vertexCount, int runs) {
|
||||
for (int run = 0; run < runs; run++) {
|
||||
final int end = run + 1 < runs ? runStarts[run + 1] : vertexCount;
|
||||
final long ids = runIds[run];
|
||||
for (int vertex = runStarts[run]; vertex < end; vertex++) {
|
||||
memPutLong(dst + (long) vertex * stride, ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addRun(int start, long ids) {
|
||||
if (runCount == runStarts.length) {
|
||||
runStarts = Arrays.copyOf(runStarts, runCount * 2);
|
||||
runIds = Arrays.copyOf(runIds, runCount * 2);
|
||||
}
|
||||
runStarts[runCount] = start;
|
||||
runIds[runCount] = ids;
|
||||
runCount++;
|
||||
}
|
||||
}
|
||||
+119
-76
@@ -109,53 +109,7 @@ public class TessellatorStreamingDrawer {
|
||||
if (GLSMConfig.expandVertexFormats) {
|
||||
effectiveFlags = VertexFlags.COLOR_BIT | VertexFlags.TEXTURE_BIT | VertexFlags.NORMAL_BIT | VertexFlags.BRIGHTNESS_BIT;
|
||||
if (effectiveFlags != flags) {
|
||||
final int[] rawBuffer = tess.rawBuffer;
|
||||
final int defaultColor;
|
||||
if (!tess.hasColor) {
|
||||
final var c = GLStateManager.getColor();
|
||||
defaultColor = ((int)(clamp(0f, 1f, c.getAlpha()) * 255) << 24) | ((int)(clamp(0f, 1f, c.getBlue()) * 255) << 16)
|
||||
| ((int)(clamp(0f, 1f, c.getGreen()) * 255) << 8) | (int)(clamp(0f, 1f, c.getRed()) * 255);
|
||||
} else {
|
||||
defaultColor = 0;
|
||||
}
|
||||
|
||||
final int defaultBrightness;
|
||||
if (!tess.hasBrightness) {
|
||||
defaultBrightness = GLSMConfig.packedLastBrightness();
|
||||
} else {
|
||||
defaultBrightness = 0;
|
||||
}
|
||||
|
||||
final ImmediateExtendedAttribHandler normalHandler = GLSMHooks.immediateExtendedHandler;
|
||||
final boolean faceNormals = !tess.hasNormals && normalHandler != null
|
||||
&& ImmediateExtendedAttribHandler.extPrimVerts(tess.drawMode, vertexCount) == 4 && normalHandler.wantsFaceNormals();
|
||||
final boolean fillNormal = !tess.hasNormals && !faceNormals;
|
||||
|
||||
final int defaultNormal;
|
||||
if (fillNormal) {
|
||||
final var n = ShaderManager.getCurrentNormal();
|
||||
defaultNormal = ((int)(clamp(-1f, 1f, n.z) * 127) << 16) | (((int)(clamp(-1f, 1f, n.y) * 127) & 0xFF) << 8) | ((int)(clamp(-1f, 1f, n.x) * 127) & 0xFF);
|
||||
} else {
|
||||
defaultNormal = 0;
|
||||
}
|
||||
|
||||
final int defaultTexU, defaultTexV;
|
||||
if (!tess.hasTexture) {
|
||||
final var tc = ShaderManager.getCurrentTexCoord();
|
||||
defaultTexU = Float.floatToRawIntBits(tc.x);
|
||||
defaultTexV = Float.floatToRawIntBits(tc.y);
|
||||
} else {
|
||||
defaultTexU = 0; defaultTexV = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
final int base = i * 8;
|
||||
if (!tess.hasTexture) { rawBuffer[base + 3] = defaultTexU; rawBuffer[base + 4] = defaultTexV; }
|
||||
if (!tess.hasColor) { rawBuffer[base + 5] = defaultColor; }
|
||||
if (fillNormal) { rawBuffer[base + 6] = defaultNormal; }
|
||||
if (!tess.hasBrightness) { rawBuffer[base + 7] = defaultBrightness; }
|
||||
}
|
||||
if (faceNormals) normalHandler.writeFaceNormals(rawBuffer, vertexCount);
|
||||
fillMissingAttributes(tess);
|
||||
}
|
||||
} else {
|
||||
effectiveFlags = flags;
|
||||
@@ -176,20 +130,34 @@ public class TessellatorStreamingDrawer {
|
||||
final int extPrim = extHandler == null ? 0 : ImmediateExtendedAttribHandler.extPrimVerts(tess.drawMode, vertexCount);
|
||||
final boolean wantsExt = extHandler != null && extHandler.wantsExtended();
|
||||
final boolean doExt = extPrim != 0 && tess.hasTexture && wantsExt;
|
||||
if (doExt) {
|
||||
final int combinedStride = vertexSize + ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int combinedBytes = vertexCount * combinedStride;
|
||||
ensureExtScratch(combinedBytes);
|
||||
interleaveBase(repackAddress, extScratchAddress, vertexCount, vertexSize, combinedStride);
|
||||
// format expansion backfills a normal into rawBuffer, so trust effectiveFlags rather than tess.hasNormals
|
||||
final int normalIndex = (effectiveFlags & VertexFlags.NORMAL_BIT) != 0 ? ImmediateExtendedAttribHandler.RAW_NORMAL_INDEX : -1;
|
||||
extHandler.build(tess.rawBuffer, vertexCount, extPrim, normalIndex, extScratchAddress + vertexSize, combinedStride);
|
||||
extScratch.position(0);
|
||||
extScratch.limit(combinedBytes);
|
||||
uploadAndDrawExtended(extScratch, effectiveFlags, format, combinedStride, tess.drawMode, vertexCount);
|
||||
shrinkExtScratchIfOversized(combinedBytes);
|
||||
} else {
|
||||
uploadAndDraw(repackBuffer, effectiveFlags, format, vertexSize, tess.drawMode, vertexCount);
|
||||
final int idRuns = TessellatorEntityIds.runsFor(tess, vertexCount);
|
||||
final boolean doIds = idRuns > 1 && extHandler != null && extHandler.wantsEntityIds() && !LineQuads.isLineMode(tess.drawMode);
|
||||
final GLContextState swappedCtx = idRuns >= 1 && !doIds ? GLStateManager.ctx() : null;
|
||||
final long restoreIds = swappedCtx != null ? swapEntityIds(swappedCtx, TessellatorEntityIds.firstRunIds()) : 0L;
|
||||
try {
|
||||
if (doExt || doIds) {
|
||||
final int layout = (doExt ? StreamingVaos.MID_TEX_AND_TANGENT : 0) | (doIds ? StreamingVaos.ENTITY_IDS : 0);
|
||||
final int combinedStride = combinedStride(format, layout);
|
||||
final int combinedBytes = vertexCount * combinedStride;
|
||||
ensureExtScratch(combinedBytes);
|
||||
interleaveBase(repackAddress, extScratchAddress, vertexCount, vertexSize, combinedStride);
|
||||
long extAddress = extScratchAddress + vertexSize;
|
||||
if (doExt) {
|
||||
// format expansion backfills a normal into rawBuffer, so trust effectiveFlags rather than tess.hasNormals
|
||||
final int normalIndex = (effectiveFlags & VertexFlags.NORMAL_BIT) != 0 ? ImmediateExtendedAttribHandler.RAW_NORMAL_INDEX : -1;
|
||||
extHandler.build(tess.rawBuffer, vertexCount, extPrim, normalIndex, extAddress, combinedStride);
|
||||
extAddress += ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
}
|
||||
if (doIds) TessellatorEntityIds.write(extAddress, combinedStride, vertexCount, idRuns);
|
||||
extScratch.position(0);
|
||||
extScratch.limit(combinedBytes);
|
||||
uploadAndDrawExtended(extScratch, effectiveFlags, format, layout, combinedStride, tess.drawMode, vertexCount);
|
||||
shrinkExtScratchIfOversized(combinedBytes);
|
||||
} else {
|
||||
uploadAndDraw(repackBuffer, effectiveFlags, format, vertexSize, tess.drawMode, vertexCount);
|
||||
}
|
||||
} finally {
|
||||
if (swappedCtx != null) swapEntityIds(swappedCtx, restoreIds);
|
||||
}
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
@@ -206,6 +174,58 @@ public class TessellatorStreamingDrawer {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void fillMissingAttributes(Tessellator tess) {
|
||||
final int vertexCount = tess.vertexCount;
|
||||
final int[] rawBuffer = tess.rawBuffer;
|
||||
final int defaultColor;
|
||||
if (!tess.hasColor) {
|
||||
final var c = GLStateManager.getColor();
|
||||
defaultColor = ((int)(clamp(0f, 1f, c.getAlpha()) * 255) << 24) | ((int)(clamp(0f, 1f, c.getBlue()) * 255) << 16)
|
||||
| ((int)(clamp(0f, 1f, c.getGreen()) * 255) << 8) | (int)(clamp(0f, 1f, c.getRed()) * 255);
|
||||
} else {
|
||||
defaultColor = 0;
|
||||
}
|
||||
|
||||
final int defaultBrightness;
|
||||
if (!tess.hasBrightness) {
|
||||
defaultBrightness = GLSMConfig.packedLastBrightness();
|
||||
} else {
|
||||
defaultBrightness = 0;
|
||||
}
|
||||
|
||||
final ImmediateExtendedAttribHandler normalHandler = GLSMHooks.immediateExtendedHandler;
|
||||
final boolean faceNormals = !tess.hasNormals && normalHandler != null
|
||||
&& ImmediateExtendedAttribHandler.extPrimVerts(tess.drawMode, vertexCount) == 4 && normalHandler.wantsFaceNormals();
|
||||
final boolean fillNormal = !tess.hasNormals && !faceNormals;
|
||||
GLStateManager.requireExitVertexDefaults(fillNormal, !tess.hasTexture, !tess.hasColor, !tess.hasBrightness);
|
||||
|
||||
final int defaultNormal;
|
||||
if (fillNormal) {
|
||||
final var n = ShaderManager.getCurrentNormal();
|
||||
defaultNormal = ((int)(clamp(-1f, 1f, n.z) * 127) << 16) | (((int)(clamp(-1f, 1f, n.y) * 127) & 0xFF) << 8) | ((int)(clamp(-1f, 1f, n.x) * 127) & 0xFF);
|
||||
} else {
|
||||
defaultNormal = 0;
|
||||
}
|
||||
|
||||
final int defaultTexU, defaultTexV;
|
||||
if (!tess.hasTexture) {
|
||||
final var tc = ShaderManager.getCurrentTexCoord();
|
||||
defaultTexU = Float.floatToRawIntBits(tc.x);
|
||||
defaultTexV = Float.floatToRawIntBits(tc.y);
|
||||
} else {
|
||||
defaultTexU = 0; defaultTexV = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
final int base = i * 8;
|
||||
if (!tess.hasTexture) { rawBuffer[base + 3] = defaultTexU; rawBuffer[base + 4] = defaultTexV; }
|
||||
if (!tess.hasColor) { rawBuffer[base + 5] = defaultColor; }
|
||||
if (fillNormal) { rawBuffer[base + 6] = defaultNormal; }
|
||||
if (!tess.hasBrightness) { rawBuffer[base + 7] = defaultBrightness; }
|
||||
}
|
||||
if (faceNormals) normalHandler.writeFaceNormals(rawBuffer, vertexCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw DirectTessellator data via streaming VBO+VAO. Used for live immediate mode emulation.
|
||||
*/
|
||||
@@ -248,7 +268,7 @@ public class TessellatorStreamingDrawer {
|
||||
final long srcBase = memAddress0(packed) + packed.position();
|
||||
final int texOffset = ImmediateExtendedAttribHandler.texOffset(format);
|
||||
final int normalOffset = ImmediateExtendedAttribHandler.normalOffset(format);
|
||||
final int combinedStride = vertexSize + ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
final int combinedStride = combinedStride(format, StreamingVaos.MID_TEX_AND_TANGENT);
|
||||
final int combinedBytes = vertexCount * combinedStride;
|
||||
final boolean locked = GLStateManager.acquireDrawLock();
|
||||
try {
|
||||
@@ -257,7 +277,7 @@ public class TessellatorStreamingDrawer {
|
||||
extHandler.buildPacked(srcBase, vertexSize, 0, texOffset, normalOffset, vertexCount, extPrim, extScratchAddress + vertexSize, combinedStride);
|
||||
extScratch.position(0);
|
||||
extScratch.limit(combinedBytes);
|
||||
uploadAndDrawExtended(extScratch, flags, format, combinedStride, drawMode, vertexCount);
|
||||
uploadAndDrawExtended(extScratch, flags, format, StreamingVaos.MID_TEX_AND_TANGENT, combinedStride, drawMode, vertexCount);
|
||||
shrinkExtScratchIfOversized(combinedBytes);
|
||||
} finally {
|
||||
if (locked) GLStateManager.releaseDrawLock();
|
||||
@@ -311,7 +331,7 @@ public class TessellatorStreamingDrawer {
|
||||
|
||||
public static void endFrame() {
|
||||
if (persistentBuffer != null) {
|
||||
persistentBuffer.postDraw();
|
||||
persistentBuffer.endFrame();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,6 +463,7 @@ public class TessellatorStreamingDrawer {
|
||||
FfpExtendedAttribs.beginInternalDraw();
|
||||
try {
|
||||
if (drawMode == GL11.GL_QUADS) {
|
||||
GLStateManager.drawCalls++;
|
||||
QuadConverter.drawQuadsAsTriangles(glCtx, firstVertex, vertexCount);
|
||||
} else {
|
||||
GLStateManager.glDrawArrays(drawMode, firstVertex, vertexCount);
|
||||
@@ -452,11 +473,11 @@ public class TessellatorStreamingDrawer {
|
||||
}
|
||||
}
|
||||
|
||||
private static void uploadAndDrawExtended(ByteBuffer combined, int flags, VertexFormat format, int combinedStride, int drawMode, int vertexCount) {
|
||||
private static void uploadAndDrawExtended(ByteBuffer combined, int flags, VertexFormat format, int layout, int combinedStride, int drawMode, int vertexCount) {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
final StreamingVaos vaos = glCtx.streamingVaos;
|
||||
ensureVAO(vaos.persistentVAOs, vaos.orphanVAOs, flags, format);
|
||||
ensureExtendedVAOs(vaos, flags, format);
|
||||
ensureExtendedVAOs(vaos, flags, format, layout);
|
||||
|
||||
int firstVertex = -1;
|
||||
|
||||
@@ -465,9 +486,9 @@ public class TessellatorStreamingDrawer {
|
||||
}
|
||||
|
||||
if (firstVertex >= 0) {
|
||||
GLStateManager.glBindVertexArray(vaos.extendedPersistentVAOs[flags]);
|
||||
GLStateManager.glBindVertexArray(vaos.extendedPersistent(layout)[flags]);
|
||||
} else {
|
||||
GLStateManager.glBindVertexArray(vaos.extendedOrphanVAOs[flags]);
|
||||
GLStateManager.glBindVertexArray(vaos.extendedOrphan(layout)[flags]);
|
||||
orphanBuffers[flags].upload(combined);
|
||||
firstVertex = 0;
|
||||
}
|
||||
@@ -476,14 +497,14 @@ public class TessellatorStreamingDrawer {
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
private static void ensureExtendedVAOs(StreamingVaos vaos, int flags, VertexFormat format) {
|
||||
final int[] extendedOrphanVAOs = vaos.extendedOrphanVAOs;
|
||||
final int[] extendedPersistentVAOs = vaos.extendedPersistentVAOs;
|
||||
private static void ensureExtendedVAOs(StreamingVaos vaos, int flags, VertexFormat format, int layout) {
|
||||
final int[] extendedOrphanVAOs = vaos.extendedOrphan(layout);
|
||||
final int[] extendedPersistentVAOs = vaos.extendedPersistent(layout);
|
||||
if (extendedOrphanVAOs[flags] == 0) {
|
||||
extendedOrphanVAOs[flags] = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(extendedOrphanVAOs[flags]);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, orphanBuffers[flags].getBufferId());
|
||||
setupExtendedBufferState(format);
|
||||
setupExtendedBufferState(format, layout);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
@@ -492,20 +513,42 @@ public class TessellatorStreamingDrawer {
|
||||
extendedPersistentVAOs[flags] = GLStateManager.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(extendedPersistentVAOs[flags]);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, persistentBuffer.getBufferId());
|
||||
setupExtendedBufferState(format);
|
||||
setupExtendedBufferState(format, layout);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setupExtendedBufferState(VertexFormat format) {
|
||||
final int combinedStride = format.getVertexSize() + ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
private static long swapEntityIds(GLContextState glCtx, long ids) {
|
||||
final long previous = glCtx.entityIds;
|
||||
if (previous != ids) {
|
||||
glCtx.entityIds = ids;
|
||||
glCtx.dirtyEntityIdsAttrib = true;
|
||||
}
|
||||
return previous;
|
||||
}
|
||||
|
||||
private static int combinedStride(VertexFormat format, int layout) {
|
||||
int stride = format.getVertexSize();
|
||||
if ((layout & StreamingVaos.MID_TEX_AND_TANGENT) != 0) stride += ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
if ((layout & StreamingVaos.ENTITY_IDS) != 0) stride += ImmediateExtendedAttribHandler.ENTITY_IDS_STRIDE;
|
||||
return stride;
|
||||
}
|
||||
|
||||
private static void setupExtendedBufferState(VertexFormat format, int layout) {
|
||||
final int combinedStride = combinedStride(format, layout);
|
||||
long offset = 0;
|
||||
for (final VertexFormatElement element : format.elementsArray) {
|
||||
element.setupBufferState(offset, combinedStride);
|
||||
offset += element.getByteSize();
|
||||
}
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(offset, combinedStride);
|
||||
if ((layout & StreamingVaos.MID_TEX_AND_TANGENT) != 0) {
|
||||
ImmediateExtendedAttribHandler.setupExtAttribPointers(offset, combinedStride);
|
||||
offset += ImmediateExtendedAttribHandler.EXT_STRIDE;
|
||||
}
|
||||
if ((layout & StreamingVaos.ENTITY_IDS) != 0) {
|
||||
ImmediateExtendedAttribHandler.setupEntityIdsPointer(offset, combinedStride);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureExtScratch(int requiredBytes) {
|
||||
|
||||
@@ -22,8 +22,8 @@ public class TextureInfo {
|
||||
protected int width = -1;
|
||||
protected int height = -1;
|
||||
|
||||
@Setter protected int minFilter = UNSET_INT;
|
||||
@Setter protected int magFilter = UNSET_INT;
|
||||
protected int minFilter = UNSET_INT;
|
||||
protected int magFilter = UNSET_INT;
|
||||
@Setter protected int wrapS = UNSET_INT;
|
||||
@Setter protected int wrapT = UNSET_INT;
|
||||
@Setter protected int maxLevel = UNSET_INT;
|
||||
@@ -37,9 +37,12 @@ public class TextureInfo {
|
||||
@Getter @Setter protected boolean generateMipmap = false;
|
||||
|
||||
@Getter protected int uploadGeneration;
|
||||
@Getter protected boolean writtenOutsideUploads;
|
||||
@Getter protected int changeStamp;
|
||||
|
||||
void bumpUploadGeneration() {
|
||||
uploadGeneration++;
|
||||
changeStamp++;
|
||||
}
|
||||
|
||||
protected TextureInfo(int id) {
|
||||
@@ -78,6 +81,16 @@ public class TextureInfo {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setMinFilter(int minFilter) {
|
||||
this.minFilter = minFilter;
|
||||
changeStamp++;
|
||||
}
|
||||
|
||||
public void setMagFilter(int magFilter) {
|
||||
this.magFilter = magFilter;
|
||||
changeStamp++;
|
||||
}
|
||||
|
||||
public int getMinFilter() {
|
||||
if (minFilter == UNSET_INT) minFilter = queryParameteri(GL11.GL_TEXTURE_MIN_FILTER);
|
||||
return minFilter;
|
||||
|
||||
@@ -14,7 +14,10 @@ import org.lwjgl.opengl.GL32;
|
||||
import org.lwjgl.opengl.GL40;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
public class TextureInfoCache {
|
||||
/**
|
||||
@@ -28,10 +31,45 @@ public class TextureInfoCache {
|
||||
|
||||
private final Int2ObjectMap<TextureInfo> cache = new Int2ObjectOpenHashMap<>();
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final List<IntConsumer> deleteListeners = new ArrayList<>();
|
||||
private int deleteCount;
|
||||
|
||||
private TextureInfoCache() {
|
||||
}
|
||||
|
||||
public int deleteCount() {
|
||||
return deleteCount;
|
||||
}
|
||||
|
||||
public void addDeleteListener(IntConsumer listener) {
|
||||
deleteListeners.add(listener);
|
||||
}
|
||||
|
||||
public void onContentsChanged(int texture) {
|
||||
if (texture <= 0) return;
|
||||
final TextureInfo info = getInfo(texture);
|
||||
if (info != null) info.bumpUploadGeneration();
|
||||
}
|
||||
|
||||
public void onWrittenOutsideUploads(int texture) {
|
||||
if (texture <= 0) return;
|
||||
final TextureInfo info = getInfo(texture);
|
||||
if (info == null || info.writtenOutsideUploads) return;
|
||||
info.writtenOutsideUploads = true;
|
||||
info.changeStamp++;
|
||||
}
|
||||
|
||||
private void defineLevelZero(int texture, int target, int internalformat, int width, int height) {
|
||||
final TextureInfo info = getInfo(texture);
|
||||
if (info == null) return;
|
||||
info.setTarget(target);
|
||||
info.internalFormat = internalformat;
|
||||
info.resolvedInternalFormat = isGenericCompressedInternalFormat(internalformat) ? -1 : internalformat;
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.bumpUploadGeneration();
|
||||
}
|
||||
|
||||
public TextureInfo getInfo(int id) {
|
||||
if (id < 0) return null;
|
||||
|
||||
@@ -48,48 +86,27 @@ public class TextureInfoCache {
|
||||
}
|
||||
|
||||
public void onTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable Buffer pixels) {
|
||||
if (isTrackedTarget(target) && level == 0) {
|
||||
final TextureInfo info = getInfo(GLStateManager.getBoundTextureForServerState());
|
||||
if (info == null) return;
|
||||
info.setTarget(target);
|
||||
info.internalFormat = internalformat;
|
||||
info.resolvedInternalFormat = isGenericCompressedInternalFormat(internalformat) ? -1 : internalformat;
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.bumpUploadGeneration();
|
||||
}
|
||||
if (isTrackedTarget(target) && level == 0) defineLevelZero(GLStateManager.getBoundTextureForServerState(), target, internalformat, width, height);
|
||||
}
|
||||
|
||||
public void onTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, long pixels_buffer_offset) {
|
||||
if (isTrackedTarget(target) && level == 0) {
|
||||
final TextureInfo info = getInfo(GLStateManager.getBoundTextureForServerState());
|
||||
if (info == null) return;
|
||||
info.setTarget(target);
|
||||
info.internalFormat = internalformat;
|
||||
info.resolvedInternalFormat = isGenericCompressedInternalFormat(internalformat) ? -1 : internalformat;
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.bumpUploadGeneration();
|
||||
}
|
||||
if (isTrackedTarget(target) && level == 0) defineLevelZero(GLStateManager.getBoundTextureForServerState(), target, internalformat, width, height);
|
||||
}
|
||||
|
||||
public void onCopyTexImage2D(int target, int level, int internalformat, int width, int height) {
|
||||
if (isTrackedTarget(target) && level == 0) defineLevelZero(GLStateManager.getBoundTextureForServerState(), target, internalformat, width, height);
|
||||
}
|
||||
|
||||
public void onTextureImage2D(int texture, int target, int level, int internalformat, int width, int height) {
|
||||
if (isTrackedTarget(target) && level == 0) defineLevelZero(texture, target, internalformat, width, height);
|
||||
}
|
||||
|
||||
public void onTexStorage2D(int target, int internalformat, int width, int height) {
|
||||
if (!isTrackedTarget(target)) return;
|
||||
final TextureInfo info = getInfo(GLStateManager.getBoundTextureForServerState());
|
||||
if (info == null) return;
|
||||
info.setTarget(target);
|
||||
info.internalFormat = internalformat;
|
||||
info.resolvedInternalFormat = isGenericCompressedInternalFormat(internalformat) ? -1 : internalformat;
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.bumpUploadGeneration();
|
||||
if (isTrackedTarget(target)) defineLevelZero(GLStateManager.getBoundTextureForServerState(), target, internalformat, width, height);
|
||||
}
|
||||
|
||||
public void onTexSubImage2D(int target, int level) {
|
||||
if (!isTrackedTarget(target) || level != 0) return;
|
||||
final TextureInfo info = getInfo(GLStateManager.getBoundTextureForServerState());
|
||||
if (info == null) return;
|
||||
info.bumpUploadGeneration();
|
||||
if (isTrackedTarget(target) && level == 0) onContentsChanged(GLStateManager.getBoundTextureForServerState());
|
||||
}
|
||||
|
||||
public static boolean isGenericCompressedInternalFormat(int internalFormat) {
|
||||
@@ -131,9 +148,11 @@ public class TextureInfoCache {
|
||||
|
||||
public void onDeleteTexture(int id) {
|
||||
if (id < 0) return;
|
||||
deleteCount++;
|
||||
|
||||
if (GLStateManager.isSplashComplete()) {
|
||||
cache.remove(id);
|
||||
for (IntConsumer listener : deleteListeners) listener.accept(id);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,4 +27,20 @@ public final class TextureTargets {
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
public static int bindingQuery(int target) {
|
||||
return switch (target) {
|
||||
case GL11.GL_TEXTURE_1D -> GL11.GL_TEXTURE_BINDING_1D;
|
||||
case GL12.GL_TEXTURE_3D -> GL12.GL_TEXTURE_BINDING_3D;
|
||||
case GL13.GL_TEXTURE_CUBE_MAP -> GL13.GL_TEXTURE_BINDING_CUBE_MAP;
|
||||
case GL30.GL_TEXTURE_1D_ARRAY -> GL30.GL_TEXTURE_BINDING_1D_ARRAY;
|
||||
case GL30.GL_TEXTURE_2D_ARRAY -> GL30.GL_TEXTURE_BINDING_2D_ARRAY;
|
||||
case GL31.GL_TEXTURE_RECTANGLE -> GL31.GL_TEXTURE_BINDING_RECTANGLE;
|
||||
case GL31.GL_TEXTURE_BUFFER -> GL31.GL_TEXTURE_BINDING_BUFFER;
|
||||
case GL32.GL_TEXTURE_2D_MULTISAMPLE -> GL32.GL_TEXTURE_BINDING_2D_MULTISAMPLE;
|
||||
case GL32.GL_TEXTURE_2D_MULTISAMPLE_ARRAY -> GL32.GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY;
|
||||
case GL40.GL_TEXTURE_CUBE_MAP_ARRAY -> GL40.GL_TEXTURE_BINDING_CUBE_MAP_ARRAY;
|
||||
default -> GL11.GL_TEXTURE_BINDING_2D;
|
||||
};
|
||||
}
|
||||
}
|
||||
+264
-499
@@ -7,7 +7,6 @@ import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
@@ -24,8 +23,11 @@ class CompatShaderTransformerTest {
|
||||
return values;
|
||||
}
|
||||
|
||||
private static void assertRewrite(boolean fragment, String src, String[] present, String[] absent) {
|
||||
final String result = CompatShaderTransformer.transform(src, fragment);
|
||||
private static String compact(String s) {
|
||||
return s.replaceAll("\\s+", "");
|
||||
}
|
||||
|
||||
private static void assertNeedles(String result, String[] present, String[] absent) {
|
||||
for (final String s : present) {
|
||||
assertTrue(result.contains(s), () -> "expected '" + s + "' in\n\n" + result);
|
||||
}
|
||||
@@ -34,7 +36,7 @@ class CompatShaderTransformerTest {
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<Arguments> matrixBuiltins() {
|
||||
static Stream<Arguments> rewrites() {
|
||||
return Stream.of(
|
||||
Arguments.of("modelview and projection matrices", false, """
|
||||
#version 120
|
||||
@@ -78,17 +80,7 @@ class CompatShaderTransformerTest {
|
||||
vec4 color = texture2D(tex, vec2(0.0));
|
||||
gl_FragColor = color;
|
||||
}
|
||||
""", strings("angelica_ModelViewMatrix", "angelica_ProjectionMatrix", "angelica_NormalMatrix", "#version 330 core"), strings("gl_ModelViewMatrix", "gl_ProjectionMatrix", "gl_NormalMatrix", "texture2D", "gl_FragColor")));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("matrixBuiltins")
|
||||
void matrixBuiltinsAreRewritten(String name, boolean fragment, String src, String[] present, String[] absent) {
|
||||
assertRewrite(fragment, src, present, absent);
|
||||
}
|
||||
|
||||
static Stream<Arguments> textureFunctions() {
|
||||
return Stream.of(
|
||||
""", strings("angelica_ModelViewMatrix", "angelica_ProjectionMatrix", "angelica_NormalMatrix", "#version 330 core"), strings("gl_ModelViewMatrix", "gl_ProjectionMatrix", "gl_NormalMatrix", "texture2D", "gl_FragColor")),
|
||||
Arguments.of("texture2D", false, """
|
||||
#version 120
|
||||
uniform sampler2D tex;
|
||||
@@ -111,33 +103,7 @@ class CompatShaderTransformerTest {
|
||||
vec4 v = texelFetch3D(vol, ivec3(0), 0);
|
||||
gl_FragColor = v;
|
||||
}
|
||||
""", strings("texelFetch"), strings("texelFetch3D")));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("textureFunctions")
|
||||
void legacyTextureFunctionsAreRenamed(String name, boolean fragment, String src, String[] present, String[] absent) {
|
||||
assertRewrite(fragment, src, present, absent);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFragColorHandling() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_FragColor = vec4(1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, true);
|
||||
assertFalse(result.contains("gl_FragColor"));
|
||||
// Should have layout-qualified output declaration
|
||||
assertTrue(result.contains("angelica_FragData0"));
|
||||
assertTrue(result.contains("layout"));
|
||||
}
|
||||
|
||||
static Stream<Arguments> versionDirectives() {
|
||||
return Stream.of(
|
||||
""", strings("texelFetch"), strings("texelFetch3D")),
|
||||
Arguments.of("110 becomes 330 core", true, """
|
||||
#version 110
|
||||
void main() {
|
||||
@@ -149,51 +115,7 @@ class CompatShaderTransformerTest {
|
||||
void main() {
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""", strings("#version 330 core"), strings("compatibility")));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("versionDirectives")
|
||||
void versionIsUpgradedToCore(String name, boolean fragment, String src, String[] present, String[] absent) {
|
||||
assertRewrite(fragment, src, present, absent);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPassthroughUnchangedShader() {
|
||||
String src = """
|
||||
#version 330 core
|
||||
in vec3 position;
|
||||
uniform mat4 mvp;
|
||||
void main() {
|
||||
gl_Position = mvp * vec4(position, 1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
// Source should be returned as-is (passthrough)
|
||||
assertEquals(src, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFogStructAccess() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
vec4 c = gl_Fog.color;
|
||||
float d = gl_Fog.density;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, true);
|
||||
assertTrue(result.contains("angelica_Fog"), "gl_Fog should be renamed to angelica_Fog");
|
||||
assertFalse(result.contains("gl_Fog"), "gl_Fog should not remain in output");
|
||||
assertTrue(result.contains("angelica_FogParameters"), "Fog struct should be injected");
|
||||
assertTrue(result.contains("uniform float angelica_FogDensity"), "Fog density uniform should be injected");
|
||||
assertTrue(result.contains("uniform vec4 angelica_FogColor"), "Fog color uniform should be injected");
|
||||
}
|
||||
|
||||
static Stream<Arguments> fogFragCoord() {
|
||||
return Stream.of(
|
||||
""", strings("#version 330 core"), strings("compatibility")),
|
||||
Arguments.of("vertex shader declares an out", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
@@ -206,80 +128,7 @@ class CompatShaderTransformerTest {
|
||||
float f = gl_FogFragCoord;
|
||||
gl_FragColor = vec4(f);
|
||||
}
|
||||
""", strings("in float angelica_FogFragCoord"), strings("gl_FogFragCoord")));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("fogFragCoord")
|
||||
void fogFragCoordBecomesAVarying(String name, boolean fragment, String src, String[] present, String[] absent) {
|
||||
assertRewrite(fragment, src, present, absent);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGlFrontColorVertex() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_FrontColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertTrue(result.contains("vec4 angelica_FrontColor"), "gl_FrontColor should become angelica_FrontColor out varying");
|
||||
assertFalse(result.contains("gl_FrontColor"), "gl_FrontColor should not remain");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReservedWordTextureAsVariable() {
|
||||
// Shader declares 'texture' as a sampler variable - this is valid in GLSL 120
|
||||
// but 'texture' is a reserved word in GLSL 330+, causing parse failures without pre-rename
|
||||
String src = """
|
||||
#version 120
|
||||
uniform sampler2D texture;
|
||||
void main() {
|
||||
gl_FragColor = texture2D(texture, vec2(0.5));
|
||||
}
|
||||
""";
|
||||
|
||||
// Should not throw - the pre-parse replaceTexture handles this
|
||||
String result = CompatShaderTransformer.transform(src, true);
|
||||
assertNotNull(result);
|
||||
assertTrue(result.contains("#version 330 core"));
|
||||
// The variable named 'texture' should have been renamed to gtexture (matching CommonTransformer convention)
|
||||
assertTrue(result.contains("gtexture"), "Variable 'texture' should be renamed to gtexture");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCacheHit() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""";
|
||||
|
||||
String result1 = CompatShaderTransformer.transform(src, false);
|
||||
String result2 = CompatShaderTransformer.transform(src, false);
|
||||
assertSame(result1, result2, "Second call should return cached instance (same reference)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAlphaTestDiscardInjected() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, true);
|
||||
assertTrue(result.contains("uniform float angelica_currentAlphaTest"), "Alpha test uniform should be injected");
|
||||
assertTrue(result.contains("angelica_FragData0 . a <= angelica_currentAlphaTest") || result.contains("angelica_FragData0.a <= angelica_currentAlphaTest"),
|
||||
"Alpha test discard should be appended to main()");
|
||||
}
|
||||
|
||||
static Stream<Arguments> vertexAttributes() {
|
||||
return Stream.of(
|
||||
""", strings("in float angelica_FogFragCoord"), strings("gl_FogFragCoord")),
|
||||
Arguments.of("gl_Vertex at location 0", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
@@ -314,17 +163,7 @@ class CompatShaderTransformerTest {
|
||||
vec3 n = gl_NormalMatrix * gl_Normal;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""", strings("in vec3 angelica_Normal", "location = 4"), strings("gl_Normal")));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("vertexAttributes")
|
||||
void vertexAttributeBuiltinsGetFixedLocations(String name, boolean fragment, String src, String[] present, String[] absent) {
|
||||
assertRewrite(fragment, src, present, absent);
|
||||
}
|
||||
|
||||
static Stream<Arguments> texCoordVaryings() {
|
||||
return Stream.of(
|
||||
""", strings("in vec3 angelica_Normal", "location = 4"), strings("gl_Normal")),
|
||||
Arguments.of("vertex shader declares an out", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
@@ -346,144 +185,280 @@ class CompatShaderTransformerTest {
|
||||
gl_TexCoord[1] = gl_MultiTexCoord1;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""", strings("out vec4 angelica_TexCoord0", "out vec4 angelica_TexCoord1"), strings("gl_TexCoord")));
|
||||
""", strings("out vec4 angelica_TexCoord0", "out vec4 angelica_TexCoord1"), strings("gl_TexCoord")),
|
||||
Arguments.of("gl_FragColor becomes a layout-qualified output", true, """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_FragColor = vec4(1.0);
|
||||
}
|
||||
""", strings("angelica_FragData0", "layout"), strings("gl_FragColor")),
|
||||
Arguments.of("gl_Fog struct access is renamed and its uniforms injected", true, """
|
||||
#version 120
|
||||
void main() {
|
||||
vec4 c = gl_Fog.color;
|
||||
float d = gl_Fog.density;
|
||||
}
|
||||
""", strings("angelica_Fog", "angelica_FogParameters", "uniform float angelica_FogDensity", "uniform vec4 angelica_FogColor"), strings("gl_Fog")),
|
||||
Arguments.of("gl_FrontColor becomes an out varying", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_FrontColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
""", strings("vec4 angelica_FrontColor"), strings("gl_FrontColor")),
|
||||
Arguments.of("texture as a variable name is renamed to gtexture", true, """
|
||||
#version 120
|
||||
uniform sampler2D texture;
|
||||
void main() {
|
||||
gl_FragColor = texture2D(texture, vec2(0.5));
|
||||
}
|
||||
""", strings("#version 330 core", "gtexture"), strings()),
|
||||
Arguments.of("vertex gl_Color is the color attribute", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_FrontColor = gl_Color;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""", strings("angelica_Color", "angelica_FrontColor"), strings("gl_Color", "gl_FrontColor")),
|
||||
Arguments.of("fragment gl_Color is the interpolated front color", true, """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_FragColor = gl_Color;
|
||||
}
|
||||
""", strings("angelica_FrontColor"), strings("gl_Color")),
|
||||
Arguments.of("#define survives the transformation", true, """
|
||||
#version 120
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
void main() {
|
||||
float x = 2.0 * M_PI;
|
||||
gl_FragColor = vec4(x);
|
||||
}
|
||||
""", strings("#define M_PI 3.1415926535897932384626433832795"), strings()),
|
||||
Arguments.of("gl_FrontLightModelProduct.sceneColor becomes a uniform", false, """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_FrontColor = gl_FrontLightModelProduct.sceneColor;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""", strings("uniform vec4 angelica_SceneColor"), strings("gl_FrontLightModelProduct")),
|
||||
Arguments.of("version fixup does not eat the next line", false, "#version 330\nsomeword\n", strings("someword", "#version 330 core"), strings()),
|
||||
Arguments.of("modern shader without core keyword keeps its content", false, """
|
||||
#version 430
|
||||
in vec3 Position;
|
||||
uniform mat4 ModelViewMat;
|
||||
uniform mat4 ProjMat;
|
||||
out vec4 vertexColor;
|
||||
out float fogDistance;
|
||||
void main() {
|
||||
vec4 finalPos = vec4(Position, 1.0);
|
||||
vec4 modelPos = ModelViewMat * finalPos;
|
||||
gl_Position = ProjMat * modelPos;
|
||||
fogDistance = length(modelPos.xz);
|
||||
vertexColor = vec4(1.0);
|
||||
}
|
||||
""", strings("#version 430 core", "in vec3 Position", "ModelViewMat", "ProjMat", "fogDistance"), strings()),
|
||||
Arguments.of("vertex attribute and varying become in and out", false, """
|
||||
#version 120
|
||||
attribute float a_Custom;
|
||||
varying vec2 v_TexCoord;
|
||||
void main() {
|
||||
v_TexCoord = vec2(a_Custom);
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""", strings("in float a_Custom", "out vec2 v_TexCoord"), strings("attribute", "varying")),
|
||||
Arguments.of("fragment varying becomes in", true, """
|
||||
#version 120
|
||||
varying vec2 v_TexCoord;
|
||||
void main() {
|
||||
gl_FragColor = vec4(v_TexCoord, 0.0, 1.0);
|
||||
}
|
||||
""", strings("in vec2 v_TexCoord"), strings("varying")));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("texCoordVaryings")
|
||||
void texCoordBecomesPerIndexVaryings(String name, boolean fragment, String src, String[] present, String[] absent) {
|
||||
assertRewrite(fragment, src, present, absent);
|
||||
@MethodSource
|
||||
void rewrites(String name, boolean fragment, String src, String[] present, String[] absent) {
|
||||
assertNeedles(CompatShaderTransformer.transform(src, fragment), present, absent);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGlColorVertexVsFragmentDifferentTreatment() {
|
||||
// Vertex: gl_Color = vertex attribute -> angelica_Color
|
||||
// Fragment: gl_Color = interpolated gl_FrontColor -> angelica_FrontColor
|
||||
String vertSrc = """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_FrontColor = gl_Color;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""";
|
||||
String fragSrc = """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_FragColor = gl_Color;
|
||||
}
|
||||
""";
|
||||
|
||||
String vertResult = CompatShaderTransformer.transform(vertSrc, false);
|
||||
String fragResult = CompatShaderTransformer.transform(fragSrc, true);
|
||||
|
||||
// Vertex shader: gl_Color -> angelica_Color (attribute), gl_FrontColor -> angelica_FrontColor (out)
|
||||
assertTrue(vertResult.contains("angelica_Color"), "vertex gl_Color -> angelica_Color");
|
||||
assertTrue(vertResult.contains("angelica_FrontColor"), "vertex gl_FrontColor -> angelica_FrontColor");
|
||||
assertFalse(vertResult.contains("gl_Color"), "no gl_Color should remain in vertex");
|
||||
assertFalse(vertResult.contains("gl_FrontColor"), "no gl_FrontColor should remain");
|
||||
|
||||
// Fragment shader: gl_Color -> angelica_FrontColor (in varying)
|
||||
assertTrue(fragResult.contains("angelica_FrontColor"), "fragment gl_Color -> angelica_FrontColor");
|
||||
assertFalse(fragResult.contains("gl_Color"), "fragment should NOT have gl_Color");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefinePreservedDuringTransformation() {
|
||||
void testAlphaTestDiscardInjected() {
|
||||
String src = """
|
||||
#version 120
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
void main() {
|
||||
float x = 2.0 * M_PI;
|
||||
gl_FragColor = vec4(x);
|
||||
gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, true);
|
||||
assertTrue(result.contains("#define M_PI 3.1415926535897932384626433832795"),
|
||||
"#define should be preserved through transformation\n\n" + result);
|
||||
assertTrue(result.contains("uniform float angelica_currentAlphaTest"), "Alpha test uniform should be injected");
|
||||
assertTrue(result.contains("angelica_FragData0 . a <= angelica_currentAlphaTest") || result.contains("angelica_FragData0.a <= angelica_currentAlphaTest"),
|
||||
"Alpha test discard should be appended to main()");
|
||||
}
|
||||
|
||||
static Stream<Arguments> inputLocations() {
|
||||
return Stream.of(
|
||||
Arguments.of("custom position attribute is pinned to location zero", """
|
||||
#version 120
|
||||
attribute vec4 a_position;
|
||||
uniform mat4 u_projMat;
|
||||
uniform vec2 u_outSize;
|
||||
varying vec2 v_texCoord;
|
||||
void main(){
|
||||
gl_Position = u_projMat * vec4(a_position.xy, 0.0, 1.0);
|
||||
v_texCoord = a_position.xy / u_outSize;
|
||||
}
|
||||
""", strings("0:vec4 a_position"), strings(), strings("#version 330 core"), strings()),
|
||||
Arguments.of("location-less inputs get sequential locations", """
|
||||
#version 120
|
||||
attribute vec4 a_pos;
|
||||
attribute vec2 a_uv;
|
||||
varying vec2 v_uv;
|
||||
void main() {
|
||||
v_uv = a_uv;
|
||||
gl_Position = vec4(a_pos.xy, 0.0, 1.0);
|
||||
}
|
||||
""", strings("0:vec4 a_pos", "1:vec2 a_uv"), strings(), strings(), strings()),
|
||||
Arguments.of("a custom input is placed around gl_Vertex", """
|
||||
#version 120
|
||||
attribute vec4 a_foo;
|
||||
void main() {
|
||||
gl_Position = ftransform() + a_foo;
|
||||
}
|
||||
""", strings("0:vec4 angelica_Vertex", "1:vec4 a_foo"), strings(), strings(), strings()),
|
||||
Arguments.of("a custom input skips the FFP slots", """
|
||||
#version 120
|
||||
attribute vec4 a_foo;
|
||||
void main() {
|
||||
gl_Position = vec4(a_foo.xy, 0.0, 1.0) + gl_Color;
|
||||
}
|
||||
""", strings("0:vec4 a_foo", "1:vec4 angelica_Color"), strings(), strings(), strings()),
|
||||
Arguments.of("an explicit low location is reserved", """
|
||||
#version 120
|
||||
layout(location = 0) in vec4 a_x;
|
||||
attribute vec4 a_y;
|
||||
void main() {
|
||||
gl_Position = a_x + a_y;
|
||||
}
|
||||
""", strings("0:vec4 a_x", "1:vec4 a_y"), strings(), strings(), strings()),
|
||||
Arguments.of("a 4-slot matrix input is sorted first", """
|
||||
#version 120
|
||||
attribute vec4 a_a;
|
||||
attribute mat4 a_m;
|
||||
void main() {
|
||||
gl_Position = a_m * a_a;
|
||||
}
|
||||
""", strings("0:mat4 a_m", "4:vec4 a_a"), strings(), strings(), strings()),
|
||||
Arguments.of("an input named location is still pinned", """
|
||||
#version 120
|
||||
attribute vec4 a_location;
|
||||
void main() {
|
||||
gl_Position = vec4(a_location.xy, 0.0, 1.0);
|
||||
}
|
||||
""", strings("0:vec4 a_location"), strings(), strings(), strings()),
|
||||
Arguments.of("an input with an explicit layout is left alone", """
|
||||
#version 120
|
||||
layout(location = 3) in vec4 a_data;
|
||||
attribute vec4 a_position;
|
||||
varying vec2 v_uv;
|
||||
void main() {
|
||||
v_uv = a_data.xy;
|
||||
gl_Position = vec4(a_position.xy, 0.0, 1.0);
|
||||
}
|
||||
""", strings("0:vec4 a_position", "3:vec4 a_data"), strings("0:vec4 a_data"), strings(), strings()),
|
||||
Arguments.of("a gl_Vertex shader does not pin a custom attribute", """
|
||||
#version 120
|
||||
attribute float activelights;
|
||||
void main() {
|
||||
gl_Position = ftransform();
|
||||
float x = activelights;
|
||||
}
|
||||
""", strings("0:vec4 angelica_Vertex"), strings("0:float activelights"), strings(), strings()),
|
||||
Arguments.of("array on the declarator round-trips", """
|
||||
#version 120
|
||||
attribute vec4 positions[2];
|
||||
attribute vec4 a_tail;
|
||||
void main() {
|
||||
gl_Position = positions[0] + positions[1] + a_tail;
|
||||
}
|
||||
""", strings("0:vec4 positions[2]", "2:vec4 a_tail"), strings(), strings(), strings()),
|
||||
Arguments.of("array on the type round-trips", """
|
||||
#version 120
|
||||
attribute vec4[2] positions;
|
||||
attribute vec4 a_tail;
|
||||
void main() {
|
||||
gl_Position = positions[0] + positions[1] + a_tail;
|
||||
}
|
||||
""", strings("0:vec4 positions[2]", "2:vec4 a_tail"), strings(), strings(), strings()),
|
||||
Arguments.of("a multi-declarator is split into separate locations", """
|
||||
#version 120
|
||||
attribute vec4 a, b;
|
||||
void main() {
|
||||
gl_Position = a + b;
|
||||
}
|
||||
""", strings("0:vec4 a", "1:vec4 b"), strings(), strings("#version 330 core"), strings("attribute")),
|
||||
Arguments.of("a 2-slot array declarator sorts before a 1-slot one", """
|
||||
#version 120
|
||||
attribute vec4 a, b[2];
|
||||
void main() {
|
||||
gl_Position = a + b[0] + b[1];
|
||||
}
|
||||
""", strings("0:vec4 b[2]", "2:vec4 a"), strings(), strings(), strings("attribute")),
|
||||
Arguments.of("a hex explicit location is preserved and reserved", """
|
||||
#version 120
|
||||
layout(location = 0x1) in vec4 a_x;
|
||||
attribute vec4 a_y;
|
||||
void main() {
|
||||
gl_Position = a_x + a_y;
|
||||
}
|
||||
""", strings("0x1:vec4 a_x", "0:vec4 a_y"), strings("1:vec4 a_y"), strings(), strings()));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource
|
||||
void inputLocations(String name, String src, String[] at, String[] notAt, String[] present, String[] absent) {
|
||||
final String result = CompatShaderTransformer.transform(src, false);
|
||||
final String c = compact(result);
|
||||
for (final String slot : at) {
|
||||
assertTrue(c.contains(locationDecl(slot)), () -> slot + " expected\n\n" + result);
|
||||
}
|
||||
for (final String slot : notAt) {
|
||||
assertFalse(c.contains(locationDecl(slot)), () -> slot + " must not be assigned\n\n" + result);
|
||||
}
|
||||
assertNeedles(result, present, absent);
|
||||
}
|
||||
|
||||
private static String locationDecl(String slot) {
|
||||
final int colon = slot.indexOf(':');
|
||||
return compact("layout(location=" + slot.substring(0, colon) + ")in" + slot.substring(colon + 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGlFrontLightModelProductSceneColor() {
|
||||
void testPassthroughUnchangedShader() {
|
||||
String src = """
|
||||
#version 330 core
|
||||
in vec3 position;
|
||||
uniform mat4 mvp;
|
||||
void main() {
|
||||
gl_Position = mvp * vec4(position, 1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertEquals(src, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCacheHit() {
|
||||
String src = """
|
||||
#version 120
|
||||
void main() {
|
||||
gl_FrontColor = gl_FrontLightModelProduct.sceneColor;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertFalse(result.contains("gl_FrontLightModelProduct"), "gl_FrontLightModelProduct should be replaced");
|
||||
assertTrue(result.contains("uniform vec4 angelica_SceneColor"), "angelica_SceneColor uniform should be injected");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVersionPatternDoesNotEatNextLine() {
|
||||
String src = "#version 330\nsomeword\n";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertTrue(result.contains("someword"), "Next-line word must not be eaten by version fixup");
|
||||
assertTrue(result.contains("#version 330 core"), "Version should be upgraded to core");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testModernShaderWithoutCoreKeyword() {
|
||||
String src = """
|
||||
#version 430
|
||||
in vec3 Position;
|
||||
uniform mat4 ModelViewMat;
|
||||
uniform mat4 ProjMat;
|
||||
out vec4 vertexColor;
|
||||
out float fogDistance;
|
||||
void main() {
|
||||
vec4 finalPos = vec4(Position, 1.0);
|
||||
vec4 modelPos = ModelViewMat * finalPos;
|
||||
gl_Position = ProjMat * modelPos;
|
||||
fogDistance = length(modelPos.xz);
|
||||
vertexColor = vec4(1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertTrue(result.contains("#version 430 core"), "Version should get 'core' profile added");
|
||||
assertTrue(result.contains("in vec3 Position"), "'in' keyword must not be eaten");
|
||||
assertTrue(result.contains("ModelViewMat"), "Shader content must be preserved");
|
||||
assertTrue(result.contains("ProjMat"), "Shader content must be preserved");
|
||||
assertTrue(result.contains("fogDistance"), "Shader content must be preserved");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLegacyQualifierConversion() {
|
||||
String vertSrc = """
|
||||
#version 120
|
||||
attribute float a_Custom;
|
||||
varying vec2 v_TexCoord;
|
||||
void main() {
|
||||
v_TexCoord = vec2(a_Custom);
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
""";
|
||||
String fragSrc = """
|
||||
#version 120
|
||||
varying vec2 v_TexCoord;
|
||||
void main() {
|
||||
gl_FragColor = vec4(v_TexCoord, 0.0, 1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
String vertResult = CompatShaderTransformer.transform(vertSrc, false);
|
||||
String fragResult = CompatShaderTransformer.transform(fragSrc, true);
|
||||
|
||||
// attribute -> in, varying -> out (vertex)
|
||||
assertFalse(vertResult.contains("attribute"), "no 'attribute' keyword in core profile");
|
||||
assertFalse(vertResult.contains("varying"), "no 'varying' keyword in vertex");
|
||||
assertTrue(vertResult.contains("in float a_Custom"), "attribute -> in");
|
||||
assertTrue(vertResult.contains("out vec2 v_TexCoord"), "varying -> out in vertex");
|
||||
|
||||
// varying -> in (fragment)
|
||||
assertFalse(fragResult.contains("varying"), "no 'varying' keyword in fragment");
|
||||
assertTrue(fragResult.contains("in vec2 v_TexCoord"), "varying -> in in fragment");
|
||||
String result1 = CompatShaderTransformer.transform(src, false);
|
||||
String result2 = CompatShaderTransformer.transform(src, false);
|
||||
assertSame(result1, result2, "Second call should return cached instance (same reference)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -525,140 +500,6 @@ class CompatShaderTransformerTest {
|
||||
assertTrue(result.contains("angelica_renamed_new"), "'new' should be renamed to angelica_renamed_new\n\n" + result);
|
||||
}
|
||||
|
||||
private static String compact(String s) {
|
||||
return s.replaceAll("\\s+", "");
|
||||
}
|
||||
|
||||
private static String core(String src) {
|
||||
return compact(CompatShaderTransformer.transform(src, false));
|
||||
}
|
||||
|
||||
private static void assertLoc(String c, int loc, String decl) {
|
||||
assertTrue(c.contains(compact("layout(location=" + loc + ")in" + decl)), decl + " expected at location " + loc + "\n\n" + c);
|
||||
}
|
||||
|
||||
private static void assertNoLoc(String c, int loc, String decl) {
|
||||
assertFalse(c.contains(compact("layout(location=" + loc + ")in" + decl)), decl + " must not be at location " + loc + "\n\n" + c);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomPositionAttributePinnedToLocationZero() {
|
||||
// Betweenlands blshader fix
|
||||
String src = """
|
||||
#version 120
|
||||
attribute vec4 a_position;
|
||||
uniform mat4 u_projMat;
|
||||
uniform vec2 u_outSize;
|
||||
varying vec2 v_texCoord;
|
||||
void main(){
|
||||
gl_Position = u_projMat * vec4(a_position.xy, 0.0, 1.0);
|
||||
v_texCoord = a_position.xy / u_outSize;
|
||||
}
|
||||
""";
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
assertLoc(compact(result), 0, "vec4 a_position");
|
||||
assertTrue(result.contains("#version 330 core"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleLocationlessInputsGetSequentialLocations() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
attribute vec4 a_pos;
|
||||
attribute vec2 a_uv;
|
||||
varying vec2 v_uv;
|
||||
void main() {
|
||||
v_uv = a_uv;
|
||||
gl_Position = vec4(a_pos.xy, 0.0, 1.0);
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 a_pos");
|
||||
assertLoc(c, 1, "vec2 a_uv");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomInputReservesAroundGlVertex() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
attribute vec4 a_foo;
|
||||
void main() {
|
||||
gl_Position = ftransform() + a_foo;
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 angelica_Vertex");
|
||||
assertLoc(c, 1, "vec4 a_foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomInputSkipsFfpSlots() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
attribute vec4 a_foo;
|
||||
void main() {
|
||||
gl_Position = vec4(a_foo.xy, 0.0, 1.0) + gl_Color;
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 a_foo");
|
||||
assertLoc(c, 1, "vec4 angelica_Color");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExplicitLowLocationReserved() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
layout(location = 0) in vec4 a_x;
|
||||
attribute vec4 a_y;
|
||||
void main() {
|
||||
gl_Position = a_x + a_y;
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 a_x");
|
||||
assertLoc(c, 1, "vec4 a_y");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMatrixInputSortedFirst() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
attribute vec4 a_a;
|
||||
attribute mat4 a_m;
|
||||
void main() {
|
||||
gl_Position = a_m * a_a;
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "mat4 a_m"); // 4-slot matrix sorted first
|
||||
assertLoc(c, 4, "vec4 a_a");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInputNamedLocationStillPinned() {
|
||||
assertLoc(core("""
|
||||
#version 120
|
||||
attribute vec4 a_location;
|
||||
void main() {
|
||||
gl_Position = vec4(a_location.xy, 0.0, 1.0);
|
||||
}
|
||||
"""), 0, "vec4 a_location");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInputWithExplicitLayoutLeftAlone() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
layout(location = 3) in vec4 a_data;
|
||||
attribute vec4 a_position;
|
||||
varying vec2 v_uv;
|
||||
void main() {
|
||||
v_uv = a_data.xy;
|
||||
gl_Position = vec4(a_position.xy, 0.0, 1.0);
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 a_position"); // sole location-less input
|
||||
assertLoc(c, 3, "vec4 a_data"); // explicit location preserved
|
||||
assertNoLoc(c, 0, "vec4 a_data");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPinnedShaderRemainsWellFormed() {
|
||||
String src = """
|
||||
@@ -673,8 +514,8 @@ class CompatShaderTransformerTest {
|
||||
|
||||
String result = CompatShaderTransformer.transform(src, false);
|
||||
String c = compact(result);
|
||||
assertLoc(c, 0, "vec4 a_position");
|
||||
assertLoc(c, 1, "vec4 angelica_Color");
|
||||
assertTrue(c.contains(locationDecl("0:vec4 a_position")), result);
|
||||
assertTrue(c.contains(locationDecl("1:vec4 angelica_Color")), result);
|
||||
assertTrue(c.contains("uniformmat4angelica_ModelViewMatrix"), "matrix uniform injected\n\n" + result);
|
||||
assertFalse(c.contains("gl_ModelViewMatrix"), "builtin rewritten (not fallback)\n\n" + result);
|
||||
|
||||
@@ -684,82 +525,6 @@ class CompatShaderTransformerTest {
|
||||
assertTrue(result.indexOf("a_position") < mainIdx, "pinned input before main\n\n" + result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGlVertexShaderDoesNotPinCustomAttribute() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
attribute float activelights;
|
||||
void main() {
|
||||
gl_Position = ftransform();
|
||||
float x = activelights;
|
||||
}
|
||||
""");
|
||||
assertLoc(c, 0, "vec4 angelica_Vertex");
|
||||
assertNoLoc(c, 0, "float activelights");
|
||||
}
|
||||
|
||||
@ParameterizedTest // array on the declarator (vec4 a[2]) and on the type (vec4[2] a) must both round-trip
|
||||
@ValueSource(strings = {"attribute vec4 positions[2];", "attribute vec4[2] positions;"})
|
||||
void testArrayAttributePreservedEitherSyntax(String decl) {
|
||||
String c = core("""
|
||||
#version 120
|
||||
%s
|
||||
attribute vec4 a_tail;
|
||||
void main() {
|
||||
gl_Position = positions[0] + positions[1] + a_tail;
|
||||
}
|
||||
""".formatted(decl));
|
||||
assertLoc(c, 0, "vec4 positions[2]"); // 2-slot array pinned to 0..1
|
||||
assertLoc(c, 2, "vec4 a_tail");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultiDeclaratorSplitIntoSeparateLocations() {
|
||||
String result = CompatShaderTransformer.transform("""
|
||||
#version 120
|
||||
attribute vec4 a, b;
|
||||
void main() {
|
||||
gl_Position = a + b;
|
||||
}
|
||||
""", false);
|
||||
String c = compact(result);
|
||||
assertLoc(c, 0, "vec4 a");
|
||||
assertLoc(c, 1, "vec4 b");
|
||||
assertFalse(result.matches("(?s).*\\battribute\\b.*"), "no deprecated 'attribute' keyword may survive\n\n" + result);
|
||||
assertTrue(result.contains("#version 330 core"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultiDeclaratorWithMixedArray() {
|
||||
String result = CompatShaderTransformer.transform("""
|
||||
#version 120
|
||||
attribute vec4 a, b[2];
|
||||
void main() {
|
||||
gl_Position = a + b[0] + b[1];
|
||||
}
|
||||
""", false);
|
||||
String c = compact(result);
|
||||
// b takes 2 slots; sorted before the 1-slot a, so b -> 0..1, a -> 2.
|
||||
assertLoc(c, 0, "vec4 b[2]");
|
||||
assertLoc(c, 2, "vec4 a");
|
||||
assertFalse(result.matches("(?s).*\\battribute\\b.*"), "no deprecated 'attribute' keyword may survive\n\n" + result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHexExplicitLocationReserved() {
|
||||
String c = core("""
|
||||
#version 120
|
||||
layout(location = 0x1) in vec4 a_x;
|
||||
attribute vec4 a_y;
|
||||
void main() {
|
||||
gl_Position = a_x + a_y;
|
||||
}
|
||||
""");
|
||||
assertTrue(c.contains("layout(location=0x1)invec4a_x"), "hex explicit location preserved\n\n" + c);
|
||||
assertNoLoc(c, 1, "vec4 a_y"); // must not collide with the hex-reserved slot
|
||||
assertLoc(c, 0, "vec4 a_y");
|
||||
}
|
||||
|
||||
@Test
|
||||
void diskCacheHitReplacesTransform(@TempDir Path dir) {
|
||||
final String src = """
|
||||
|
||||
+3
-16
@@ -99,22 +99,6 @@ class DisplayListCompilationParityTest {
|
||||
GLStateManager.glLoadIdentity();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(Caller.class)
|
||||
void compile_doesNotChangeState(Caller c) {
|
||||
testList = GL11.glGenLists(1);
|
||||
c.newList(testList, GL11.GL_COMPILE);
|
||||
c.enable(GL11.GL_BLEND);
|
||||
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_BLEND), "During GL_COMPILE: state unchanged");
|
||||
|
||||
c.endList();
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_BLEND), "After GL_COMPILE: state unchanged");
|
||||
|
||||
c.callList(testList);
|
||||
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND), "After playback: state changed");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(Caller.class)
|
||||
void compileAndExecute_changesStateImmediately(Caller c) {
|
||||
@@ -186,6 +170,9 @@ class DisplayListCompilationParityTest {
|
||||
c.enable(GL11.GL_BLEND);
|
||||
c.enable(GL11.GL_DEPTH_TEST);
|
||||
c.enable(GL11.GL_CULL_FACE);
|
||||
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_BLEND), "During GL_COMPILE: BLEND unchanged");
|
||||
|
||||
c.endList();
|
||||
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_BLEND), "After GL_COMPILE: BLEND unchanged");
|
||||
|
||||
+1
@@ -197,6 +197,7 @@ class DisplayListMatrixRestorationTest extends DisplayListTestFixture {
|
||||
GLStateManager.glCallList(list);
|
||||
String context = "compileMode=" + compileMode;
|
||||
assertEquals(GL13.GL_TEXTURE0, GLStateManager.glGetInteger(GL13.GL_ACTIVE_TEXTURE), context);
|
||||
assertEquals(GL13.GL_TEXTURE0, GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE), context);
|
||||
assertEquals(3f, translation(GL11.GL_MODELVIEW_MATRIX), context);
|
||||
assertEquals(0f, translation(GL11.GL_TEXTURE_MATRIX), context);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
|
||||
@@ -55,36 +55,7 @@ class FeedbackManagerTest {
|
||||
FeedbackManager.glFeedbackBuffer(GL11.GL_2D, buf);
|
||||
FeedbackManager.glRenderMode(GL11.GL_FEEDBACK);
|
||||
|
||||
// Set up identity matrices and a viewport
|
||||
GLStateManager.getModelViewMatrix().identity();
|
||||
GLStateManager.getProjectionMatrix().identity();
|
||||
GLStateManager.getViewportState().setViewPort(0, 0, 800, 600);
|
||||
GLStateManager.getViewportState().setDepthRange(0.0, 1.0);
|
||||
|
||||
// Upload 3 vertices to a VBO and draw
|
||||
final int vao = GL30.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
|
||||
final float[] verts = { 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f, 0f };
|
||||
final FloatBuffer vertBuf = BufferUtils.createFloatBuffer(9);
|
||||
vertBuf.put(verts).flip();
|
||||
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertBuf, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
|
||||
FeedbackManager.processDrawArrays(GL11.GL_TRIANGLES, 0, 3);
|
||||
|
||||
final int result = FeedbackManager.glRenderMode(GL11.GL_RENDER);
|
||||
assertEquals(-1, result, "Overflow should return -1");
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
assertEquals(-1, feedbackDraw(GL11.GL_TRIANGLES, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f, 0f), "Overflow should return -1");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -122,37 +93,13 @@ class FeedbackManagerTest {
|
||||
FeedbackManager.glFeedbackBuffer(GL11.GL_2D, buf);
|
||||
FeedbackManager.glRenderMode(GL11.GL_FEEDBACK);
|
||||
|
||||
GLStateManager.getModelViewMatrix().identity();
|
||||
GLStateManager.getProjectionMatrix().identity();
|
||||
GLStateManager.getViewportState().setViewPort(0, 0, 800, 600);
|
||||
GLStateManager.getViewportState().setDepthRange(0.0, 1.0);
|
||||
|
||||
final int vao = GL30.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
|
||||
final FloatBuffer vertBuf = BufferUtils.createFloatBuffer(verts.length);
|
||||
vertBuf.put(verts).flip();
|
||||
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertBuf, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
|
||||
FeedbackManager.processDrawArrays(mode, 0, verts.length / 3);
|
||||
|
||||
final int count = FeedbackManager.glRenderMode(GL11.GL_RENDER);
|
||||
final int count = feedbackDraw(mode, verts);
|
||||
|
||||
// GL_2D: POLYGON_TOKEN, vertex count, then x, y per vertex
|
||||
assertEquals(expected.length, count, name + " float count");
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
assertEquals(expected[i], buf.get(i), 0.01f, name + " feedback value " + i);
|
||||
}
|
||||
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -161,28 +108,7 @@ class FeedbackManagerTest {
|
||||
FeedbackManager.glFeedbackBuffer(GL11.GL_3D, buf);
|
||||
FeedbackManager.glRenderMode(GL11.GL_FEEDBACK);
|
||||
|
||||
GLStateManager.getModelViewMatrix().identity();
|
||||
GLStateManager.getProjectionMatrix().identity();
|
||||
GLStateManager.getViewportState().setViewPort(0, 0, 800, 600);
|
||||
GLStateManager.getViewportState().setDepthRange(0.0, 1.0);
|
||||
|
||||
final int vao = GL30.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
|
||||
// Single point at NDC origin (0,0,0)
|
||||
final float[] verts = { 0f, 0f, 0f };
|
||||
final FloatBuffer vertBuf = BufferUtils.createFloatBuffer(3);
|
||||
vertBuf.put(verts).flip();
|
||||
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertBuf, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
|
||||
FeedbackManager.processDrawArrays(GL11.GL_POINTS, 0, 1);
|
||||
|
||||
final int count = FeedbackManager.glRenderMode(GL11.GL_RENDER);
|
||||
final int count = feedbackDraw(GL11.GL_POINTS, 0f, 0f, 0f);
|
||||
|
||||
// GL_3D point: POINT_TOKEN, x, y, z = 4 floats
|
||||
assertEquals(4, count);
|
||||
@@ -191,11 +117,31 @@ class FeedbackManagerTest {
|
||||
assertEquals(400.0f, buf.get(1), 0.01f);
|
||||
assertEquals(300.0f, buf.get(2), 0.01f);
|
||||
assertEquals(0.5f, buf.get(3), 0.01f);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
private static int feedbackDraw(int mode, float... verts) {
|
||||
GLStateManager.getModelViewMatrix().identity();
|
||||
GLStateManager.getProjectionMatrix().identity();
|
||||
GLStateManager.getViewportState().setViewPort(0, 0, 800, 600);
|
||||
GLStateManager.getViewportState().setDepthRange(0.0, 1.0);
|
||||
|
||||
final int vao = GL30.glGenVertexArrays();
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
final FloatBuffer vertBuf = BufferUtils.createFloatBuffer(verts.length);
|
||||
vertBuf.put(verts).flip();
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertBuf, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
try {
|
||||
FeedbackManager.processDrawArrays(mode, 0, verts.length / 3);
|
||||
return FeedbackManager.glRenderMode(GL11.GL_RENDER);
|
||||
} finally {
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
GLStateManager.glDeleteVertexArrays(vao);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class GLESFormatRemapTest {
|
||||
@@ -26,82 +31,45 @@ class GLESFormatRemapTest {
|
||||
assertEquals(GL11.GL_RGBA4, remapDesktop(GL11.GL_ALPHA4, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE).internalFormat());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rgb16AndRgba16_remapToHalfFloat_onGles() {
|
||||
final var r1 = remap(GL11.GL_RGB16, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE);
|
||||
assertEquals(GL30.GL_RGB16F, r1.internalFormat());
|
||||
assertEquals(GL30.GL_HALF_FLOAT, r1.type());
|
||||
|
||||
final var r2 = remap(GL11.GL_RGBA16, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE);
|
||||
assertEquals(GL30.GL_RGBA16F, r2.internalFormat());
|
||||
assertEquals(GL30.GL_HALF_FLOAT, r2.type());
|
||||
static Stream<Arguments> remappedType() {
|
||||
return Stream.of(
|
||||
Arguments.of("R16F gets half float", true, GL30.GL_R16F, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, GL30.GL_HALF_FLOAT),
|
||||
Arguments.of("RG16F gets half float", true, GL30.GL_RG16F, GL30.GL_RG, GL11.GL_UNSIGNED_BYTE, GL30.GL_HALF_FLOAT),
|
||||
Arguments.of("RGB16F gets half float", true, GL30.GL_RGB16F, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, GL30.GL_HALF_FLOAT),
|
||||
Arguments.of("RGBA16F gets half float", true, GL30.GL_RGBA16F, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, GL30.GL_HALF_FLOAT),
|
||||
Arguments.of("RGBA32F gets float", true, GL30.GL_RGBA32F, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, GL11.GL_FLOAT),
|
||||
Arguments.of("RGB32F gets float", true, GL30.GL_RGB32F, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, GL11.GL_FLOAT),
|
||||
Arguments.of("RGBA8_SNORM gets byte", true, GL31.GL_RGBA8_SNORM, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, GL11.GL_BYTE),
|
||||
Arguments.of("RGB8_SNORM gets byte", true, GL31.GL_RGB8_SNORM, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, GL11.GL_BYTE),
|
||||
Arguments.of("RGB10_A2 gets packed type", true, GL11.GL_RGB10_A2, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, GL12.GL_UNSIGNED_INT_2_10_10_10_REV),
|
||||
Arguments.of("DEPTH_COMPONENT24 gets unsigned int", true, GL14.GL_DEPTH_COMPONENT24, GL11.GL_DEPTH_COMPONENT, GL11.GL_UNSIGNED_BYTE, GL11.GL_UNSIGNED_INT),
|
||||
Arguments.of("DEPTH_COMPONENT32F gets float", true, GL30.GL_DEPTH_COMPONENT32F, GL11.GL_DEPTH_COMPONENT, GL11.GL_UNSIGNED_BYTE, GL11.GL_FLOAT),
|
||||
Arguments.of("DEPTH24_STENCIL8 gets 24_8", true, GL30.GL_DEPTH24_STENCIL8, GL30.GL_DEPTH_STENCIL, GL11.GL_UNSIGNED_BYTE, GL30.GL_UNSIGNED_INT_24_8),
|
||||
Arguments.of("already valid type is left alone", true, GL30.GL_RGBA16F, GL11.GL_RGBA, GL30.GL_HALF_FLOAT, GL30.GL_HALF_FLOAT),
|
||||
Arguments.of("packed ARGB RGBA remaps to unsigned byte on GLES", true, GL11.GL_RGBA8, GL11.GL_RGBA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, GL11.GL_UNSIGNED_BYTE),
|
||||
Arguments.of("packed ARGB BGRA remaps to unsigned byte on GLES", true, GL11.GL_RGBA8, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, GL11.GL_UNSIGNED_BYTE),
|
||||
Arguments.of("packed ARGB untouched on desktop", false, GL11.GL_RGBA8, GL11.GL_RGBA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, GL12.GL_UNSIGNED_INT_8_8_8_8_REV));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rgb16AndRgba16_passThroughUntouched_onDesktop() {
|
||||
final var r = remapDesktop(GL11.GL_RGB16, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE);
|
||||
assertEquals(GL11.GL_RGB16, r.internalFormat());
|
||||
assertEquals(GL11.GL_UNSIGNED_BYTE, r.type());
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource
|
||||
void remappedType(String label, boolean gles, int internal, int format, int type, int expectedType) {
|
||||
assertEquals(expectedType, GLESFormatRemap.apply(internal, format, type, gles).type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void halfFloatInternalFormats_getHalfFloatType() {
|
||||
assertEquals(GL30.GL_HALF_FLOAT, remap(GL30.GL_R16F, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE).type());
|
||||
assertEquals(GL30.GL_HALF_FLOAT, remap(GL30.GL_RG16F, GL30.GL_RG, GL11.GL_UNSIGNED_BYTE).type());
|
||||
assertEquals(GL30.GL_HALF_FLOAT, remap(GL30.GL_RGB16F, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE).type());
|
||||
assertEquals(GL30.GL_HALF_FLOAT, remap(GL30.GL_RGBA16F, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE).type());
|
||||
static Stream<Arguments> remappedFormatAndType() {
|
||||
return Stream.of(
|
||||
Arguments.of("RGB16 remaps to half float on GLES", true, GL11.GL_RGB16, GL11.GL_RGB, GL30.GL_RGB16F, GL30.GL_HALF_FLOAT),
|
||||
Arguments.of("RGBA16 remaps to half float on GLES", true, GL11.GL_RGBA16, GL11.GL_RGBA, GL30.GL_RGBA16F, GL30.GL_HALF_FLOAT),
|
||||
Arguments.of("RGB16 passes through untouched on desktop", false, GL11.GL_RGB16, GL11.GL_RGB, GL11.GL_RGB16, GL11.GL_UNSIGNED_BYTE),
|
||||
Arguments.of("R11F_G11F_B10F keeps its format and gets half float", true, GL30.GL_R11F_G11F_B10F, GL11.GL_RGB, GL30.GL_R11F_G11F_B10F, GL30.GL_HALF_FLOAT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void floatInternalFormats_getFloatType() {
|
||||
assertEquals(GL11.GL_FLOAT, remap(GL30.GL_RGBA32F, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE).type());
|
||||
assertEquals(GL11.GL_FLOAT, remap(GL30.GL_RGB32F, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE).type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void snormInternalFormats_getByteType() {
|
||||
assertEquals(GL11.GL_BYTE, remap(GL31.GL_RGBA8_SNORM, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE).type());
|
||||
assertEquals(GL11.GL_BYTE, remap(GL31.GL_RGB8_SNORM, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE).type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void r11fG11fB10f_getsHalfFloatType() {
|
||||
final var r = remap(GL30.GL_R11F_G11F_B10F, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE);
|
||||
assertEquals(GL30.GL_R11F_G11F_B10F, r.internalFormat()); // not remapped
|
||||
assertEquals(GL30.GL_HALF_FLOAT, r.type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rgb10A2_getsPackedType() {
|
||||
final var r = remap(GL11.GL_RGB10_A2, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE);
|
||||
assertEquals(GL12.GL_UNSIGNED_INT_2_10_10_10_REV, r.type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void depthFormats_getMatchingType() {
|
||||
assertEquals(GL11.GL_UNSIGNED_INT, remap(GL14.GL_DEPTH_COMPONENT24, GL11.GL_DEPTH_COMPONENT, GL11.GL_UNSIGNED_BYTE).type());
|
||||
assertEquals(GL11.GL_FLOAT, remap(GL30.GL_DEPTH_COMPONENT32F, GL11.GL_DEPTH_COMPONENT, GL11.GL_UNSIGNED_BYTE).type());
|
||||
assertEquals(GL30.GL_UNSIGNED_INT_24_8, remap(GL30.GL_DEPTH24_STENCIL8, GL30.GL_DEPTH_STENCIL, GL11.GL_UNSIGNED_BYTE).type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void alreadyValidType_isLeftAlone() {
|
||||
final var r = remap(GL30.GL_RGBA16F, GL11.GL_RGBA, GL30.GL_HALF_FLOAT);
|
||||
assertEquals(GL30.GL_HALF_FLOAT, r.type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void packedArgb_remapsToUnsignedByte_onGles() {
|
||||
final var r = remap(GL11.GL_RGBA8, GL11.GL_RGBA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV);
|
||||
assertEquals(GL11.GL_UNSIGNED_BYTE, r.type());
|
||||
|
||||
final var r2 = remap(GL11.GL_RGBA8, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV);
|
||||
assertEquals(GL11.GL_UNSIGNED_BYTE, r2.type());
|
||||
}
|
||||
|
||||
@Test
|
||||
void packedArgb_untouched_onDesktop() {
|
||||
final var r = remapDesktop(GL11.GL_RGBA8, GL11.GL_RGBA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV);
|
||||
assertEquals(GL12.GL_UNSIGNED_INT_8_8_8_8_REV, r.type());
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource
|
||||
void remappedFormatAndType(String label, boolean gles, int internal, int format, int expectedInternal, int expectedType) {
|
||||
final GLESFormatRemap.Result r = GLESFormatRemap.apply(internal, format, GL11.GL_UNSIGNED_BYTE, gles);
|
||||
assertEquals(expectedInternal, r.internalFormat());
|
||||
assertEquals(expectedType, r.type());
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
@@ -502,6 +503,7 @@ public class GLSM_ClientArrays_GLTest {
|
||||
|
||||
assertEquals(2, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_SIZE), "array state is not restored once its array buffer is gone");
|
||||
assertEquals(vbo2, GLStateManager.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING));
|
||||
assertEquals(vbo2, GL11.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING));
|
||||
assertEquals(GL13.GL_TEXTURE0, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE));
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
@@ -524,6 +526,7 @@ public class GLSM_ClientArrays_GLTest {
|
||||
GLStateManager.glBindVertexArray(other);
|
||||
GLStateManager.glCallList(list);
|
||||
assertEquals(other, GLStateManager.getBoundVAO(), "replaying the list must not rebind the vertex array that was bound while compiling");
|
||||
assertEquals(other, GL11.glGetInteger(GL30.GL_VERTEX_ARRAY_BINDING), "driver vertex array unchanged by replay");
|
||||
} finally {
|
||||
drainClientAttribStack();
|
||||
GLStateManager.glDeleteLists(list, 1);
|
||||
|
||||
@@ -93,19 +93,6 @@ public class GLSM_CoreRemovedCaps_GLTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void secondaryColorIsCacheOnly() {
|
||||
try {
|
||||
// Calling through to the driver here aborts the JVM in a core profile - the color sum is emulated by the FFP shaders
|
||||
GLStateManager.glSecondaryColor3f(0.25F, 0.5F, 0.75F);
|
||||
assertNoGlError("glSecondaryColor3f");
|
||||
|
||||
assertSecondaryColor(0.25F, 0.5F, 0.75F);
|
||||
} finally {
|
||||
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void secondaryColorIsRestoredByPopAttrib() {
|
||||
try {
|
||||
|
||||
+313
-5
@@ -1,9 +1,20 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL33;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
@@ -11,6 +22,10 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
@GLCoreTest
|
||||
public class GLSM_DeferredTextureDelete_GLTest {
|
||||
|
||||
private static final int RELEASE_DELAY_FRAMES = Reflect.getStatic(GLStateManager.class, "RELEASE_DELAY_FRAMES");
|
||||
private static final int SIZE = 4;
|
||||
private static final int LEVELS = 3;
|
||||
|
||||
private static int immutableTexture() {
|
||||
assumeTrue(GLStateManager.capabilities.OpenGL42, "no glTexStorage2D on this context");
|
||||
final int tex = GLStateManager.glGenTextures();
|
||||
@@ -20,6 +35,292 @@ public class GLSM_DeferredTextureDelete_GLTest {
|
||||
return tex;
|
||||
}
|
||||
|
||||
private static int mipmappedTexture() {
|
||||
final int tex = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
for (int level = 0; level < LEVELS; level++) {
|
||||
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, level, GL11.GL_RGBA8, SIZE >> level, SIZE >> level, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
}
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
return tex;
|
||||
}
|
||||
|
||||
private static int levelWidth(int tex, int level) {
|
||||
final int previous = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
final int width = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, level, GL11.GL_TEXTURE_WIDTH);
|
||||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, previous);
|
||||
return width;
|
||||
}
|
||||
|
||||
private static int cachedBinding(int unit) {
|
||||
return GLStateManager.getTextures().getTextureUnitBindings(unit).getBinding();
|
||||
}
|
||||
|
||||
private static void framesPass(int frames) {
|
||||
for (int i = 0; i < frames; i++) GLStateManager.onFrameEnd();
|
||||
}
|
||||
|
||||
enum Attachment {
|
||||
NONE {
|
||||
@Override int attach(int tex) { return 0; }
|
||||
},
|
||||
FRAMEBUFFER {
|
||||
@Override int attach(int tex) {
|
||||
final int fbo = GLStateManager.glGenFramebuffers();
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, fbo);
|
||||
GLStateManager.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, tex, 0);
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
return fbo;
|
||||
}
|
||||
},
|
||||
NAMED_EXT {
|
||||
@Override int attach(int tex) {
|
||||
final int fbo = GLStateManager.glGenFramebuffers();
|
||||
GLStateManager.glNamedFramebufferTexture2DEXT(fbo, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, tex, 0);
|
||||
assertEquals(0, GL11.glGetInteger(GL30.GL_DRAW_FRAMEBUFFER_BINDING), "named attach must restore the draw binding");
|
||||
return fbo;
|
||||
}
|
||||
};
|
||||
|
||||
abstract int attach(int tex);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(Attachment.class)
|
||||
void storageIsReleasedOnlyAfterTheDelayFramesUnattached(Attachment attachment) {
|
||||
framesPass(RELEASE_DELAY_FRAMES);
|
||||
final int before = GLStateManager.deferredTexturesAwaitingRelease();
|
||||
final int tex = mipmappedTexture();
|
||||
final int fbo = attachment.attach(tex);
|
||||
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
assertEquals(before + 1, GLStateManager.deferredTexturesAwaitingRelease());
|
||||
framesPass(RELEASE_DELAY_FRAMES - 1);
|
||||
assertEquals(SIZE, levelWidth(tex, 0), "storage must outlive the delete by the delay frames");
|
||||
if (fbo != 0) {
|
||||
framesPass(RELEASE_DELAY_FRAMES);
|
||||
assertEquals(SIZE, levelWidth(tex, 0), "an attached texture keeps its storage");
|
||||
assertEquals(before + 1, GLStateManager.deferredTexturesAwaitingRelease());
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, fbo);
|
||||
GLStateManager.glDeleteTextures(GLStateManager.glGenTextures());
|
||||
assertEquals(tex, GL30.glGetFramebufferAttachmentParameteri(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL30.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME), "a flush must not delete an attached texture");
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
GLStateManager.glDeleteFramebuffers(BufferUtils.createIntBuffer(1).put(fbo).flip());
|
||||
framesPass(RELEASE_DELAY_FRAMES - 1);
|
||||
assertEquals(SIZE, levelWidth(tex, 0), "storage must outlive the detach by the delay frames");
|
||||
}
|
||||
framesPass(1);
|
||||
|
||||
for (int level = 0; level < LEVELS; level++) assertEquals(0, levelWidth(tex, level), "level " + level);
|
||||
assertEquals(before, GLStateManager.deferredTexturesAwaitingRelease(), "released storage leaves nothing awaiting release");
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
enum Rejection { ATTACH, BIND_TEXTURE_UNIT }
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(Rejection.class)
|
||||
void deletedNamesAreRejected(Rejection rejection) {
|
||||
final int tex = mipmappedTexture();
|
||||
final int other = mipmappedTexture();
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
try {
|
||||
switch (rejection) {
|
||||
case ATTACH -> {
|
||||
final int fbo = GLStateManager.glGenFramebuffers();
|
||||
try {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, fbo);
|
||||
GLStateManager.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, tex, 0);
|
||||
assertEquals(GL11.GL_NONE, GL30.glGetFramebufferAttachmentParameteri(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL30.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE));
|
||||
} finally {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
GLStateManager.glDeleteFramebuffers(fbo);
|
||||
}
|
||||
}
|
||||
case BIND_TEXTURE_UNIT -> {
|
||||
GLStateManager.glBindTextureUnit(2, other);
|
||||
GLStateManager.glBindTextureUnit(2, tex);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE2);
|
||||
assertEquals(other, GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D), "driver binding");
|
||||
assertEquals(other, GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D), "cached binding");
|
||||
}
|
||||
}
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTextureUnit(2, 0);
|
||||
GLStateManager.glDeleteTextures(other);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindTexturesBindsEachTextureToItsTarget() {
|
||||
final int t2d = mipmappedTexture();
|
||||
final int t3d = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, t3d);
|
||||
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, 0);
|
||||
try {
|
||||
GLStateManager.glBindTextures(1, new int[] {t2d, t3d});
|
||||
|
||||
assertEquals(GL13.GL_TEXTURE0, GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE), "active unit unchanged");
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
assertEquals(t2d, GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D), "unit 1 driver binding");
|
||||
assertEquals(t2d, GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D), "unit 1 cached binding");
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE2);
|
||||
assertEquals(t3d, GL11.glGetInteger(GL12.GL_TEXTURE_BINDING_3D), "unit 2 driver binding");
|
||||
assertEquals(t3d, cachedBinding(2), "unit 2 cached binding");
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
|
||||
final int unit0 = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
|
||||
GLStateManager.glBindTextures(GLStateManager.MAX_TEXTURE_UNITS - 1, new int[] {t3d, t2d});
|
||||
assertEquals(unit0, GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D), "an out-of-range multi-bind must not land on the active unit");
|
||||
|
||||
GLStateManager.glDeleteTextures(t2d);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
assertEquals(0, GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D), "delete unbinds a multi-bound texture");
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
} finally {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glBindTextures(1, new int[] {0, 0});
|
||||
GLStateManager.glDeleteTextures(t3d);
|
||||
}
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void dsaCreated3DTextureIsBoundAs3D() {
|
||||
final int tex = RenderSystem.createTexture(GL12.GL_TEXTURE_3D);
|
||||
final int other = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, other);
|
||||
RenderSystem.texImage3D(tex, GL12.GL_TEXTURE_3D, 0, GL11.GL_RGBA8, SIZE, SIZE, SIZE, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||
assertEquals(tex, GL11.glGetInteger(GL12.GL_TEXTURE_BINDING_3D), "texImage3D binds the texture as 3D");
|
||||
assertEquals(tex, cachedBinding(0), "cached 3D binding");
|
||||
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, 0);
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
GLStateManager.glDeleteTextures(other);
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createdNon2DTextureIsNotReleasedAs2D() {
|
||||
assumeTrue(GLStateManager.capabilities.OpenGL45, "no glCreateTextures on this context");
|
||||
final int tex = GLStateManager.glCreateTextures(GL12.GL_TEXTURE_3D);
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
framesPass(RELEASE_DELAY_FRAMES);
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletingDetachesOnlyFromBoundFramebuffers() {
|
||||
final int tex = mipmappedTexture();
|
||||
final int unbound = Attachment.FRAMEBUFFER.attach(tex);
|
||||
final int bound = Attachment.FRAMEBUFFER.attach(tex);
|
||||
try {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, bound);
|
||||
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
|
||||
assertEquals(GL11.GL_NONE, GL30.glGetFramebufferAttachmentParameteri(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL30.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE));
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, unbound);
|
||||
assertEquals(tex, GL30.glGetFramebufferAttachmentParameteri(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL30.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME));
|
||||
} finally {
|
||||
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
|
||||
GLStateManager.glDeleteFramebuffers(bound);
|
||||
GLStateManager.glDeleteFramebuffers(unbound);
|
||||
}
|
||||
}
|
||||
|
||||
enum Rescue { PENDING, RELEASED, ATTACHED }
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(Rescue.class)
|
||||
void rescuedTextureIsEmptyWithDefaultParameters(Rescue rescue) {
|
||||
final int tex = mipmappedTexture();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_BASE_LEVEL, 1);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 2);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL33.GL_TEXTURE_SWIZZLE_R, GL11.GL_GREEN);
|
||||
GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, 1.5f);
|
||||
GLStateManager.glTexParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_BORDER_COLOR, (FloatBuffer) BufferUtils.createFloatBuffer(4).put(new float[] {1, 1, 1, 1}).flip());
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
final int fbo = rescue == Rescue.ATTACHED ? Attachment.FRAMEBUFFER.attach(tex) : 0;
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
if (rescue != Rescue.PENDING) framesPass(RELEASE_DELAY_FRAMES);
|
||||
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
try {
|
||||
assertEquals(rescue == Rescue.ATTACHED ? SIZE : 0, levelWidth(tex, 0), "only an attached texture keeps its storage");
|
||||
final int[][] defaults = {
|
||||
{GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST_MIPMAP_LINEAR},
|
||||
{GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR},
|
||||
{GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT},
|
||||
{GL12.GL_TEXTURE_BASE_LEVEL, 0},
|
||||
{GL12.GL_TEXTURE_MAX_LEVEL, 1000},
|
||||
{GL33.GL_TEXTURE_SWIZZLE_R, GL11.GL_RED},
|
||||
};
|
||||
for (int[] pair : defaults) {
|
||||
assertEquals(pair[1], GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, pair[0]), "driver pname 0x" + Integer.toHexString(pair[0]));
|
||||
assertEquals(pair[1], GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, pair[0]), "cached pname 0x" + Integer.toHexString(pair[0]));
|
||||
}
|
||||
assertEquals(0.0f, GL11.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS));
|
||||
final FloatBuffer border = BufferUtils.createFloatBuffer(16);
|
||||
GL11.glGetTexParameter(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_BORDER_COLOR, border);
|
||||
assertEquals(0.0f, border.get(0) + border.get(1) + border.get(2) + border.get(3), "border color");
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
if (fbo != 0) GLStateManager.glDeleteFramebuffers(fbo);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void rescuingWithADifferentTargetLeavesTheBoundTextureAlone() {
|
||||
final int t3d = GLStateManager.glGenTextures();
|
||||
final int tex = mipmappedTexture();
|
||||
try {
|
||||
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, t3d);
|
||||
GLStateManager.glTexParameteri(GL12.GL_TEXTURE_3D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
|
||||
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, tex);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "GLSM drops the bind");
|
||||
assertEquals(t3d, cachedBinding(0), "cached 3D binding");
|
||||
assertEquals(GL11.GL_LINEAR, GL11.glGetTexParameteri(GL12.GL_TEXTURE_3D, GL11.GL_TEXTURE_MIN_FILTER));
|
||||
framesPass(RELEASE_DELAY_FRAMES);
|
||||
assertEquals(t3d, GL11.glGetInteger(GL12.GL_TEXTURE_BINDING_3D), "the release must not unbind the 3D texture");
|
||||
assertEquals(0, levelWidth(tex, 0), "the mismatched bind must leave the texture deferred");
|
||||
} finally {
|
||||
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, 0);
|
||||
GLStateManager.glDeleteTextures(t3d);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletingAStaleNameKeepsTheBoundTexture() {
|
||||
final int live = mipmappedTexture();
|
||||
final int stale = GLStateManager.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glDeleteTextures(stale);
|
||||
GLStateManager.glDeleteTextures(GLStateManager.glGenTextures());
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, live);
|
||||
|
||||
GLStateManager.glDeleteTextures(stale);
|
||||
framesPass(RELEASE_DELAY_FRAMES);
|
||||
|
||||
assertEquals(SIZE, levelWidth(live, 0), "a stale delete must not empty the bound texture");
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
} finally {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glDeleteTextures(live);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletingAnImmutableTextureRaisesNoError() {
|
||||
final int tex = immutableTexture();
|
||||
@@ -30,33 +331,40 @@ public class GLSM_DeferredTextureDelete_GLTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletingARescuedImmutableTextureRaisesNoError() {
|
||||
void rescuedImmutableTextureResetsParametersAndDeletesAgain() {
|
||||
final int tex = immutableTexture();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
|
||||
assertEquals(GL11.GL_NEAREST_MIPMAP_LINEAR, GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER));
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletingA2DTextureWhileA3DTextureIsBoundLeavesNothingBoundTo2D() {
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
void deletingA2DTextureWhileA3DTextureIsBound(boolean deletedIsHidden2D) {
|
||||
final int t2d = GLStateManager.glGenTextures();
|
||||
final int t3d = GLStateManager.glGenTextures();
|
||||
final int other = GLStateManager.glGenTextures();
|
||||
try {
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, t2d);
|
||||
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, t3d);
|
||||
|
||||
GLStateManager.glDeleteTextures(t2d);
|
||||
GLStateManager.glDeleteTextures(deletedIsHidden2D ? t2d : other);
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
|
||||
assertEquals(0, GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D), "the deleted texture must not stay bound");
|
||||
assertEquals(deletedIsHidden2D ? 0 : t2d, GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D), "only the deleted texture leaves the hidden 2D binding");
|
||||
assertEquals(t3d, GL11.glGetInteger(GL12.GL_TEXTURE_BINDING_3D));
|
||||
} finally {
|
||||
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, 0);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
GLStateManager.glDeleteTextures(t3d);
|
||||
GLStateManager.glDeleteTextures(deletedIsHidden2D ? other : t2d);
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -6,6 +6,7 @@ import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL21;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@@ -61,6 +62,18 @@ public class GLSM_DeletedBufferBinding_GLTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletedIndexedUniformBufferIsNotRestoredFromCache() {
|
||||
final int index = 3;
|
||||
final int ubo = makeBoundBuffer(GL31.GL_UNIFORM_BUFFER);
|
||||
GLStateManager.glBindBufferBase(GL31.GL_UNIFORM_BUFFER, index, ubo);
|
||||
assertEquals(ubo, GLStateManager.getIndexedBufferBinding(GL31.GL_UNIFORM_BUFFER, index));
|
||||
|
||||
GLStateManager.glDeleteBuffers(ubo);
|
||||
assertEquals(0, GL30.glGetInteger(GL31.GL_UNIFORM_BUFFER_BINDING, index));
|
||||
assertEquals(0, GLStateManager.getIndexedBufferBinding(GL31.GL_UNIFORM_BUFFER, index));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletedElementArrayBufferDoesNotBlockClientDrawElements() {
|
||||
final int ebo = makeBoundBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER);
|
||||
|
||||
+11
-34
@@ -6,6 +6,8 @@ import com.gtnewhorizons.angelica.glsm.recording.DisplayListVBO;
|
||||
import com.gtnewhorizons.angelica.glsm.recording.GLCommand;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -39,26 +41,18 @@ class GLSM_DisplayList_Batching_Test {
|
||||
|
||||
// ==================== Pure Unit Tests (No GL Context) ====================
|
||||
|
||||
@Test
|
||||
void testConsecutiveDrawsWithSameFormatShareSingleVBO() {
|
||||
@ParameterizedTest(name = "{0} draws")
|
||||
@ValueSource(ints = { 3, 100 })
|
||||
void testConsecutiveDrawsWithSameFormatShareSingleVBO(int draws) {
|
||||
CompiledDisplayList compiled = createDisplayList(() -> {
|
||||
createSimpleDraw();
|
||||
createSimpleDraw();
|
||||
createSimpleDraw();
|
||||
for (int i = 0; i < draws; i++) {
|
||||
createSimpleDraw();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Verify: 1 VBO for all draws (format-based batching)
|
||||
assertEquals(1, getVBOCount(compiled),
|
||||
"3 draws with same format should produce 1 shared VBO");
|
||||
|
||||
// Verify: 1 DrawRangeCmd covering all geometry
|
||||
Int2IntMap counts = compiled.getCommandCounts();
|
||||
int drawCount = counts.getOrDefault(GLCommand.DRAW_RANGE, 0);
|
||||
assertEquals(1, drawCount,
|
||||
"3 consecutive draws with same format and transform should produce 1 DrawRangeCmd");
|
||||
|
||||
|
||||
assertEquals(1, getVBOCount(compiled), draws + " draws with same format should produce 1 shared VBO");
|
||||
assertEquals(1, compiled.getCommandCounts().getOrDefault(GLCommand.DRAW_RANGE, 0),
|
||||
draws + " consecutive draws with same format and transform should produce 1 DrawRangeCmd");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -101,23 +95,6 @@ class GLSM_DisplayList_Batching_Test {
|
||||
assertEquals(1, multMatrixCount, "Should have 1 Translate at the end (the transforms in the draws are prebaked)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testManyDrawsSameFormatProduceSingleVBO() {
|
||||
CompiledDisplayList compiled = createDisplayList(() -> {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
createSimpleDraw();
|
||||
}
|
||||
});
|
||||
|
||||
// Verify: 1 shared VBO
|
||||
assertEquals(1, getVBOCount(compiled), "100 draws with same format should produce 1 shared VBO");
|
||||
|
||||
Int2IntMap counts = compiled.getCommandCounts();
|
||||
int drawCount = counts.getOrDefault(GLCommand.DRAW_RANGE, 0);
|
||||
assertEquals(1, drawCount,
|
||||
"100 consecutive draws with same format and transform should produce 1 DrawRangeCmd");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testCancellingTransformsProduceSeparateDrawRangeCmds() {
|
||||
|
||||
+1
-6
@@ -62,12 +62,7 @@ class GLSM_DisplayList_FormatOptimization_Test {
|
||||
GLStateManager.glNewList(displayList, GL11.GL_COMPILE);
|
||||
|
||||
// First draw: position only
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glVertex3f(0, 0, 0);
|
||||
GLStateManager.glVertex3f(1, 0, 0);
|
||||
GLStateManager.glVertex3f(1, 1, 0);
|
||||
GLStateManager.glVertex3f(0, 1, 0);
|
||||
GLStateManager.glEnd();
|
||||
DisplayListTestHelper.immediateQuad(1.0f);
|
||||
|
||||
// Second draw: position + color
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
|
||||
+37
-136
@@ -3,10 +3,12 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
import org.joml.Matrix4f;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@@ -55,71 +57,42 @@ class GLSM_DisplayList_Nesting_Test {
|
||||
GLStateManager.glCallList(childList); // Should render child at (10,0,0)
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Execute parent list
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(parentList);
|
||||
|
||||
// Check final matrix state - should have parent's translate applied
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
|
||||
// Expected: translate(10, 0, 0)
|
||||
Matrix4f expected = new Matrix4f().translate(10.0f, 0.0f, 0.0f);
|
||||
FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16);
|
||||
expected.get(expectedBuf);
|
||||
|
||||
// Compare buffers directly (both are column-major OpenGL format)
|
||||
for (int i = 0; i < 16; i++) {
|
||||
assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f,
|
||||
String.format("Matrix[%d] mismatch", i));
|
||||
}
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
DisplayListTestHelper.assertModelviewAfterCall(parentList, new Matrix4f().translate(10.0f, 0.0f, 0.0f), "parent transform applies to the child");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNestedDisplayListWithBothTransforms() {
|
||||
// Create child list: translate and draw
|
||||
childList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(childList, GL11.GL_COMPILE);
|
||||
GLStateManager.glTranslatef(1.0f, 0.0f, 0.0f); // Child transform
|
||||
GL11.glBegin(GL11.GL_QUADS);
|
||||
GL11.glVertex3f(0, 0, 0);
|
||||
GL11.glVertex3f(1, 0, 0);
|
||||
GL11.glVertex3f(1, 1, 0);
|
||||
GL11.glVertex3f(0, 1, 0);
|
||||
GL11.glEnd();
|
||||
GLStateManager.glEndList();
|
||||
static Stream<Arguments> nestedTranslationsCompose() {
|
||||
return Stream.of(
|
||||
Arguments.of(new float[] { 10.0f, 1.0f }, 11.0f),
|
||||
Arguments.of(new float[] { 100.0f, 10.0f, 1.0f }, 111.0f));
|
||||
}
|
||||
|
||||
// Create parent list: translate then call child
|
||||
parentList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(parentList, GL11.GL_COMPILE);
|
||||
GLStateManager.glTranslatef(10.0f, 0.0f, 0.0f); // Parent transform
|
||||
GLStateManager.glCallList(childList); // Child adds (1,0,0) to parent's (10,0,0)
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Execute parent list
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(parentList);
|
||||
|
||||
// Check final matrix state - should be translate(10,0,0) * translate(1,0,0) = translate(11,0,0)
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
|
||||
// Expected: translate(11, 0, 0) = parent(10) + child(1)
|
||||
Matrix4f expected = new Matrix4f().translate(11.0f, 0.0f, 0.0f);
|
||||
FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16);
|
||||
expected.get(expectedBuf);
|
||||
|
||||
// Compare buffers directly
|
||||
for (int i = 0; i < 16; i++) {
|
||||
assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f,
|
||||
String.format("Matrix[%d] mismatch", i));
|
||||
@ParameterizedTest(name = "{0} composes to {1}")
|
||||
@MethodSource
|
||||
void nestedTranslationsCompose(float[] chain, float expectedX) {
|
||||
final int[] lists = new int[chain.length];
|
||||
try {
|
||||
for (int depth = chain.length - 1; depth >= 0; depth--) {
|
||||
lists[depth] = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(lists[depth], GL11.GL_COMPILE);
|
||||
GLStateManager.glTranslatef(chain[depth], 0.0f, 0.0f);
|
||||
if (depth == chain.length - 1) {
|
||||
GL11.glBegin(GL11.GL_QUADS);
|
||||
GL11.glVertex3f(0, 0, 0);
|
||||
GL11.glVertex3f(1, 0, 0);
|
||||
GL11.glVertex3f(1, 1, 0);
|
||||
GL11.glVertex3f(0, 1, 0);
|
||||
GL11.glEnd();
|
||||
} else {
|
||||
GLStateManager.glCallList(lists[depth + 1]);
|
||||
}
|
||||
GLStateManager.glEndList();
|
||||
}
|
||||
DisplayListTestHelper.assertModelviewAfterCall(lists[0], new Matrix4f().translate(expectedX, 0.0f, 0.0f), "nested translations");
|
||||
} finally {
|
||||
for (int list : lists) {
|
||||
if (list > 0) GLStateManager.glDeleteLists(list, 1);
|
||||
}
|
||||
}
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -145,79 +118,7 @@ class GLSM_DisplayList_Nesting_Test {
|
||||
GLStateManager.glCallList(childList); // Child uses push/pop
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Execute parent list
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(parentList);
|
||||
|
||||
// Check final matrix state - should still be translate(10,0,0) because child did push/pop
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
|
||||
// Expected: translate(10, 0, 0) only (child's transform was popped)
|
||||
Matrix4f expected = new Matrix4f().translate(10.0f, 0.0f, 0.0f);
|
||||
FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16);
|
||||
expected.get(expectedBuf);
|
||||
|
||||
// Compare buffers directly
|
||||
for (int i = 0; i < 16; i++) {
|
||||
assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f,
|
||||
String.format("Matrix[%d] mismatch", i));
|
||||
}
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeepNesting() {
|
||||
// Create deeply nested lists: grandchild -> child -> parent
|
||||
int grandchildList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(grandchildList, GL11.GL_COMPILE);
|
||||
GLStateManager.glTranslatef(1.0f, 0.0f, 0.0f); // Grandchild: +1
|
||||
GL11.glBegin(GL11.GL_QUADS);
|
||||
GL11.glVertex3f(0, 0, 0);
|
||||
GL11.glVertex3f(1, 0, 0);
|
||||
GL11.glVertex3f(1, 1, 0);
|
||||
GL11.glVertex3f(0, 1, 0);
|
||||
GL11.glEnd();
|
||||
GLStateManager.glEndList();
|
||||
|
||||
childList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(childList, GL11.GL_COMPILE);
|
||||
GLStateManager.glTranslatef(10.0f, 0.0f, 0.0f); // Child: +10
|
||||
GLStateManager.glCallList(grandchildList); // Grandchild: +1
|
||||
GLStateManager.glEndList();
|
||||
|
||||
parentList = GL11.glGenLists(1);
|
||||
GLStateManager.glNewList(parentList, GL11.GL_COMPILE);
|
||||
GLStateManager.glTranslatef(100.0f, 0.0f, 0.0f); // Parent: +100
|
||||
GLStateManager.glCallList(childList); // Child: +10, Grandchild: +1
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Execute parent list
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(parentList);
|
||||
|
||||
// Check final matrix state - should be 100 + 10 + 1 = 111
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
|
||||
// Expected: translate(111, 0, 0)
|
||||
Matrix4f expected = new Matrix4f().translate(111.0f, 0.0f, 0.0f);
|
||||
FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16);
|
||||
expected.get(expectedBuf);
|
||||
|
||||
// Compare buffers directly
|
||||
for (int i = 0; i < 16; i++) {
|
||||
assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f,
|
||||
String.format("Matrix[%d] mismatch", i));
|
||||
}
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
|
||||
// Cleanup grandchild
|
||||
GLStateManager.glDeleteLists(grandchildList, 1);
|
||||
DisplayListTestHelper.assertModelviewAfterCall(parentList, new Matrix4f().translate(10.0f, 0.0f, 0.0f), "child push/pop leaves only the parent transform");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+22
-41
@@ -27,18 +27,21 @@ class GLSM_DisplayList_RawTessellatorTransform_Test {
|
||||
GLStateManager.glLoadIdentity();
|
||||
}
|
||||
|
||||
@Test
|
||||
void pendingTransformIsEmittedBeforeARawDraw() {
|
||||
private CompiledDisplayList compileHalfScaled(Runnable body) {
|
||||
testList = GLStateManager.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
|
||||
GLStateManager.glScalef(0.5f, 0.5f, 0.5f);
|
||||
rawQuad(1.0f);
|
||||
|
||||
body.run();
|
||||
GLStateManager.glEndList();
|
||||
|
||||
final CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList);
|
||||
assertNotNull(compiled);
|
||||
return compiled;
|
||||
}
|
||||
|
||||
@Test
|
||||
void pendingTransformIsEmittedBeforeARawDraw() {
|
||||
final CompiledDisplayList compiled = compileHalfScaled(() -> rawQuad(1.0f));
|
||||
|
||||
final IntList opcodes = compiled.getCommandOpcodes();
|
||||
final int scaleIndex = opcodes.indexOf(GLCommand.SCALE);
|
||||
@@ -52,16 +55,7 @@ class GLSM_DisplayList_RawTessellatorTransform_Test {
|
||||
|
||||
@Test
|
||||
void pendingTransformIsNotEmittedBeforeABakedDraw() {
|
||||
testList = GLStateManager.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
|
||||
GLStateManager.glScalef(0.5f, 0.5f, 0.5f);
|
||||
immediateQuad(1.0f);
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
final CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList);
|
||||
assertNotNull(compiled);
|
||||
final CompiledDisplayList compiled = compileHalfScaled(() -> immediateQuad(1.0f));
|
||||
|
||||
final IntList opcodes = compiled.getCommandOpcodes();
|
||||
final int scaleIndex = opcodes.indexOf(GLCommand.SCALE);
|
||||
@@ -73,17 +67,10 @@ class GLSM_DisplayList_RawTessellatorTransform_Test {
|
||||
|
||||
@Test
|
||||
void aRawDrawAfterABakedDrawStillGetsTheTransform() {
|
||||
testList = GLStateManager.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
|
||||
GLStateManager.glScalef(0.5f, 0.5f, 0.5f);
|
||||
immediateQuad(1.0f);
|
||||
rawQuad(1.0f);
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
final CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList);
|
||||
assertNotNull(compiled);
|
||||
final CompiledDisplayList compiled = compileHalfScaled(() -> {
|
||||
immediateQuad(1.0f);
|
||||
rawQuad(1.0f);
|
||||
});
|
||||
|
||||
final IntList opcodes = compiled.getCommandOpcodes();
|
||||
final int scaleIndex = opcodes.indexOf(GLCommand.SCALE);
|
||||
@@ -97,23 +84,17 @@ class GLSM_DisplayList_RawTessellatorTransform_Test {
|
||||
|
||||
@Test
|
||||
void aRawDrawDoesNotFlushWhileBakedGeometryIsPending() {
|
||||
testList = GLStateManager.glGenLists(1);
|
||||
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
|
||||
final CompiledDisplayList compiled = compileHalfScaled(() -> {
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(1.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(1.0f, 1.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(0.0f, 1.0f, 0.0f);
|
||||
|
||||
GLStateManager.glScalef(0.5f, 0.5f, 0.5f);
|
||||
GLStateManager.glBegin(GL11.GL_QUADS);
|
||||
GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(1.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(1.0f, 1.0f, 0.0f);
|
||||
GLStateManager.glVertex3f(0.0f, 1.0f, 0.0f);
|
||||
rawQuad(1.0f);
|
||||
|
||||
rawQuad(1.0f);
|
||||
|
||||
GLStateManager.glEnd();
|
||||
GLStateManager.glEndList();
|
||||
|
||||
final CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList);
|
||||
assertNotNull(compiled);
|
||||
GLStateManager.glEnd();
|
||||
});
|
||||
|
||||
final IntList opcodes = compiled.getCommandOpcodes();
|
||||
final int scaleIndex = opcodes.indexOf(GLCommand.SCALE);
|
||||
|
||||
+19
-30
@@ -48,6 +48,20 @@ public class GLSM_DisplayList_StateSet_GLTest {
|
||||
return id;
|
||||
}
|
||||
|
||||
private int recordCutoutBracket(int mode) {
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, mode);
|
||||
final int d = GLStateManager.pushState(StateSet.CUTOUT);
|
||||
GLStateManager.enableAlphaTest();
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1f);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.enableTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.popStateTo(d);
|
||||
GLStateManager.glEndList();
|
||||
return list;
|
||||
}
|
||||
|
||||
private static Int2IntMap commandCounts(int listId) {
|
||||
final CompiledDisplayList compiled = DisplayListManager.getDisplayList(listId);
|
||||
assertNotNull(compiled, "compiled display list must exist for id " + listId);
|
||||
@@ -65,16 +79,7 @@ public class GLSM_DisplayList_StateSet_GLTest {
|
||||
final int unit0 = GLStateManager.getActiveTextureUnit();
|
||||
final int depth0 = GLStateManager.getAttribDepth();
|
||||
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
final int d = GLStateManager.pushState(StateSet.CUTOUT);
|
||||
GLStateManager.enableAlphaTest();
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1f);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.enableTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.popStateTo(d);
|
||||
GLStateManager.glEndList();
|
||||
final int list = recordCutoutBracket(GL11.GL_COMPILE);
|
||||
|
||||
verifyIsEnabled(GL11.GL_ALPHA_TEST, true, "cache alpha test enable unchanged after COMPILE-only bracket");
|
||||
assertEquals(func0, GLStateManager.getAlphaState().getFunction(), "cache alpha func unchanged after COMPILE-only bracket");
|
||||
@@ -86,16 +91,7 @@ public class GLSM_DisplayList_StateSet_GLTest {
|
||||
|
||||
@Test
|
||||
void compiledBracketReplayRestoresState() {
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
final int d = GLStateManager.pushState(StateSet.CUTOUT);
|
||||
GLStateManager.enableAlphaTest();
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1f);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.enableTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.popStateTo(d);
|
||||
GLStateManager.glEndList();
|
||||
final int list = recordCutoutBracket(GL11.GL_COMPILE);
|
||||
|
||||
GLStateManager.glAlphaFunc(GL11.GL_LESS, 0.9f);
|
||||
GLStateManager.disableAlphaTest();
|
||||
@@ -119,21 +115,13 @@ public class GLSM_DisplayList_StateSet_GLTest {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
final int depth0 = GLStateManager.getAttribDepth();
|
||||
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
|
||||
final int d = GLStateManager.pushState(StateSet.CUTOUT);
|
||||
GLStateManager.enableAlphaTest();
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1f);
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.enableTexture();
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.popStateTo(d);
|
||||
GLStateManager.glEndList();
|
||||
final int list = recordCutoutBracket(GL11.GL_COMPILE_AND_EXECUTE);
|
||||
|
||||
assertEquals(GL11.GL_LESS, GLStateManager.getAlphaState().getFunction(), "cache alpha func restored after GL_COMPILE_AND_EXECUTE bracket");
|
||||
assertEquals(0.9f, GLStateManager.getAlphaState().getReference(), 0.0001f, "cache alpha ref restored after GL_COMPILE_AND_EXECUTE bracket");
|
||||
verifyIsEnabled(GL11.GL_ALPHA_TEST, false, "cache alpha test restored disabled after GL_COMPILE_AND_EXECUTE bracket");
|
||||
assertEquals(0, GLStateManager.getActiveTextureUnit(), "cache active unit restored after GL_COMPILE_AND_EXECUTE bracket");
|
||||
assertEquals(GL13.GL_TEXTURE0, GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE), "driver active unit restored after GL_COMPILE_AND_EXECUTE bracket");
|
||||
assertEquals(depth0, GLStateManager.getAttribDepth(), "live attrib depth unchanged after balanced GL_COMPILE_AND_EXECUTE bracket");
|
||||
|
||||
GLStateManager.glCallList(list);
|
||||
@@ -143,6 +131,7 @@ public class GLSM_DisplayList_StateSet_GLTest {
|
||||
assertEquals(0.9f, GLStateManager.getAlphaState().getReference(), 0.0001f, "cache alpha ref restored after replay");
|
||||
verifyIsEnabled(GL11.GL_ALPHA_TEST, false, "cache alpha test restored disabled after replay");
|
||||
assertEquals(0, GLStateManager.getActiveTextureUnit(), "cache active unit restored after replay");
|
||||
assertEquals(GL13.GL_TEXTURE0, GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE), "driver active unit restored after replay");
|
||||
assertEquals(depth0, GLStateManager.getAttribDepth(), "live attrib depth unchanged after replay");
|
||||
}
|
||||
|
||||
|
||||
+8
-76
@@ -10,6 +10,8 @@ import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.assertModelviewAfterCall;
|
||||
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.modelviewAfterCall;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@@ -91,19 +93,7 @@ class GLSM_DisplayList_Stress_Test {
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Execute and verify final transform
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(list);
|
||||
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
|
||||
// All push/pop pairs cancel out, only final translate remains
|
||||
assertEquals(42.0f, actualBuf.get(12), 0.0001f,
|
||||
"After many push/pop pairs, only final transform should remain");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
assertEquals(42.0f, modelviewAfterCall(list).get(12), 0.0001f, "After many push/pop pairs, only final transform should remain");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -148,25 +138,7 @@ class GLSM_DisplayList_Stress_Test {
|
||||
GLStateManager.glEndList();
|
||||
}
|
||||
|
||||
// Execute the outermost list
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(chain[chainLength - 1]);
|
||||
|
||||
// Final matrix should have translation = chainLength (each list adds 1)
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
|
||||
Matrix4f expected = new Matrix4f().translate(chainLength, 0.0f, 0.0f);
|
||||
FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16);
|
||||
expected.get(expectedBuf);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f,
|
||||
String.format("Matrix[%d] mismatch for chain of %d lists", i, chainLength));
|
||||
}
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
assertModelviewAfterCall(chain[chainLength - 1], new Matrix4f().translate(chainLength, 0.0f, 0.0f), "chain of " + chainLength + " lists");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -192,19 +164,7 @@ class GLSM_DisplayList_Stress_Test {
|
||||
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Execute and verify
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(list);
|
||||
|
||||
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
|
||||
|
||||
// All pushes and pops cancel out, only final 99 translate remains
|
||||
assertEquals(99.0f, actualBuf.get(12), 0.0001f,
|
||||
"After balanced push/pop, only final transform should remain");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
assertEquals(99.0f, modelviewAfterCall(list).get(12), 0.0001f, "After balanced push/pop, only final transform should remain");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -224,17 +184,7 @@ class GLSM_DisplayList_Stress_Test {
|
||||
// Rapidly switch between calling different lists
|
||||
for (int iteration = 0; iteration < 100; iteration++) {
|
||||
for (int i = 0; i < numLists; i++) {
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(lists[i]);
|
||||
|
||||
// Verify correct transform was applied
|
||||
FloatBuffer buf = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, buf);
|
||||
assertEquals(i + 1, buf.get(12), 0.0001f,
|
||||
"List " + i + " should translate by " + (i + 1));
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
assertEquals(i + 1, modelviewAfterCall(lists[i]).get(12), 0.0001f, "List " + i + " should translate by " + (i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,32 +199,14 @@ class GLSM_DisplayList_Stress_Test {
|
||||
GLStateManager.glTranslatef(5.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Verify first compilation
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(list);
|
||||
|
||||
FloatBuffer buf1 = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, buf1);
|
||||
assertEquals(5.0f, buf1.get(12), 0.0001f, "First compilation should translate by 5");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
assertEquals(5.0f, modelviewAfterCall(list).get(12), 0.0001f, "First compilation should translate by 5");
|
||||
|
||||
// Recompile with different transform
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glTranslatef(10.0f, 0.0f, 0.0f);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
// Verify recompilation
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glCallList(list);
|
||||
|
||||
FloatBuffer buf2 = BufferUtils.createFloatBuffer(16);
|
||||
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, buf2);
|
||||
assertEquals(10.0f, buf2.get(12), 0.0001f, "Recompilation should translate by 10");
|
||||
|
||||
GLStateManager.glPopMatrix();
|
||||
assertEquals(10.0f, modelviewAfterCall(list).get(12), 0.0001f, "Recompilation should translate by 10");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class GLSM_ExitCaptureCoverage_UnitTest {
|
||||
|
||||
private static final String PACKAGE = "com/gtnewhorizons/angelica/glsm/";
|
||||
private static final String MANAGER = PACKAGE + "GLStateManager";
|
||||
private static final String RENDER_SYSTEM = PACKAGE + "RenderSystem";
|
||||
private static final String CAPTURE = PACKAGE + "ExitStateCapture";
|
||||
|
||||
private static final Set<String> RECORDING = Set.of(
|
||||
"reject", "write", "writeBlend", "writeAlpha", "writeDepthColor", "capability", "material",
|
||||
"matrixChange", "pushAttrib", "popAttrib", "pushMatrix", "popMatrix",
|
||||
"requireDrawState", "requireMaterialBakeInputs", "requireVertexDefaults", "requireTextureFilter",
|
||||
"beginTextureFilter", "finishTextureFilterSave", "beginTextureFilterRestore", "finishTextureFilterRestore",
|
||||
"failTextureFilter");
|
||||
|
||||
private static final Set<String> IGNORED = Set.of(
|
||||
// glBegin and glEnd reject
|
||||
"glVertex2d", "glVertex2f", "glVertex2i", "glVertex3d", "glVertex3f", "glVertex3i",
|
||||
// No render state
|
||||
"glDebugMessageCallback", "glDebugMessageControl", "glDebugMessageInsert", "glObjectLabel",
|
||||
"glPushDebugGroup", "glPopDebugGroup",
|
||||
// Others
|
||||
"glInitNames", "glPushName", "glPopName", "glLoadName", "isFramebufferEnabled", "gluUnProject", "gluErrorString");
|
||||
|
||||
private final Map<String, ClassNode> classes = new HashMap<>();
|
||||
private final Set<String> reaching = new HashSet<>();
|
||||
private final Set<String> notReaching = new HashSet<>();
|
||||
private Set<String> counted = RECORDING;
|
||||
|
||||
@Test
|
||||
void everyEntryPointRejectsOrRecordsTheExitCapture() {
|
||||
final Set<String> entryPoints = new HashSet<>(GLSMRedirector.getGLPrefixMethodRedirects().values());
|
||||
GLSMRedirector.getNamedClassMethodRedirects().values().forEach(redirects -> entryPoints.addAll(redirects.values()));
|
||||
final List<String> missing = missing(MANAGER, name -> name.startsWith("gl") || entryPoints.contains(name), method -> reachesCapture(MANAGER, method));
|
||||
assertTrue(missing.isEmpty(), "Entry points that neither reject nor record an exit capture:\n " + String.join("\n ", missing));
|
||||
}
|
||||
|
||||
// None of RenderSystem's writes are modeled. A method that drives the backend or DSA itself must reject itself, since
|
||||
// a GLSM call it also makes (glBindTexture) may only record.
|
||||
@Test
|
||||
void everyRenderSystemCallRejectsTheExitCapture() {
|
||||
counted = Set.of("reject");
|
||||
final List<String> missing = missing(RENDER_SYSTEM, name -> !name.equals("initRenderer") && !name.equals("getVRAM")
|
||||
&& !name.matches("(supports|has|is|getMax|getGl|parse|probe).*"), method -> reachesCapture(RENDER_SYSTEM, method)
|
||||
&& (!calls(method, owner -> owner.startsWith(PACKAGE + "backend/") || owner.startsWith(PACKAGE + "dsa/"))
|
||||
|| calls(method, owner -> owner.equals(MANAGER), "rejectExitCapture", "beforeUncapturedStateChange")));
|
||||
assertTrue(missing.isEmpty(), "RenderSystem methods that do not reject an exit capture:\n " + String.join("\n ", missing));
|
||||
}
|
||||
|
||||
private List<String> missing(String owner, Predicate<String> checked, Predicate<MethodNode> covered) {
|
||||
final List<String> missing = new ArrayList<>();
|
||||
for (MethodNode method : load(owner).methods) {
|
||||
if ((method.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC)) != (Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC)) continue;
|
||||
if ((method.access & Opcodes.ACC_SYNTHETIC) != 0) continue;
|
||||
if (!checked.test(method.name) || IGNORED.contains(method.name)) continue;
|
||||
if (!covered.test(method)) missing.add(method.name + method.desc);
|
||||
}
|
||||
missing.sort(null);
|
||||
return missing;
|
||||
}
|
||||
|
||||
private static boolean calls(MethodNode method, Predicate<String> owner, String... names) {
|
||||
final Set<String> wanted = Set.of(names);
|
||||
for (AbstractInsnNode insn : method.instructions) {
|
||||
if (insn instanceof MethodInsnNode call && owner.test(call.owner) && (wanted.isEmpty() || wanted.contains(call.name))) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean reachesCapture(String owner, MethodNode root) {
|
||||
final String key = owner + '.' + root.name + root.desc;
|
||||
if (reaching.contains(key)) return true;
|
||||
if (notReaching.contains(key)) return false;
|
||||
final Set<String> visited = new HashSet<>();
|
||||
if (search(owner, root, visited)) {
|
||||
reaching.add(key);
|
||||
return true;
|
||||
}
|
||||
notReaching.addAll(visited);
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean search(String owner, MethodNode method, Set<String> visited) {
|
||||
final String key = owner + '.' + method.name + method.desc;
|
||||
if (reaching.contains(key)) return true;
|
||||
if (notReaching.contains(key) || !visited.add(key)) return false;
|
||||
for (AbstractInsnNode insn : method.instructions) {
|
||||
if (!(insn instanceof MethodInsnNode call)) continue;
|
||||
if (call.owner.equals(CAPTURE)) {
|
||||
if (counted.contains(call.name)) return true;
|
||||
continue;
|
||||
}
|
||||
if (!call.owner.startsWith(PACKAGE)) continue;
|
||||
final String[] declaring = new String[1];
|
||||
final MethodNode target = resolve(call.owner, call.name, call.desc, declaring);
|
||||
if (target != null && search(declaring[0], target, visited)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private MethodNode resolve(String owner, String name, String desc, String[] declaring) {
|
||||
for (String type = owner; type != null && type.startsWith(PACKAGE); ) {
|
||||
final ClassNode node = load(type);
|
||||
for (MethodNode method : node.methods) {
|
||||
if (method.name.equals(name) && method.desc.equals(desc)) {
|
||||
if (method.instructions.size() == 0) return null;
|
||||
declaring[0] = type;
|
||||
return method;
|
||||
}
|
||||
}
|
||||
type = node.superName;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ClassNode load(String internalName) {
|
||||
return classes.computeIfAbsent(internalName, name -> {
|
||||
try (InputStream in = GLSM_ExitCaptureCoverage_UnitTest.class.getClassLoader().getResourceAsStream(name + ".class")) {
|
||||
if (in == null) throw new IllegalStateException("Missing class file for " + name);
|
||||
final ClassNode node = new ClassNode();
|
||||
new ClassReader(in).accept(node, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
return node;
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,561 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.DeferredBlendHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaBooleanLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
|
||||
import com.gtnewhorizons.angelica.glsm.states.BlendState;
|
||||
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector4f;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import 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
|
||||
class GLSM_ExitState_GLTest {
|
||||
private final Matrix4f modelView = new Matrix4f();
|
||||
private final Matrix4f projection = new Matrix4f();
|
||||
private final Matrix4f texture = new Matrix4f();
|
||||
private final Vector4f overlay = new Vector4f();
|
||||
private final Vector4f shaderColor = new Vector4f();
|
||||
private int outer;
|
||||
private int clientActiveUnit;
|
||||
private float lightmapX, lightmapY;
|
||||
private ImmediateExtendedAttribHandler extendedHandler;
|
||||
private DeferredBlendHandler blendHandler;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
final GLContextState context = GLStateManager.ctx();
|
||||
modelView.set(context.modelViewMatrix);
|
||||
projection.set(context.projectionMatrix);
|
||||
texture.set(context.textures.getTextureUnitMatrix(0));
|
||||
overlay.set(context.overlayR, context.overlayG, context.overlayB, context.overlayA);
|
||||
shaderColor.set(context.shaderColorR, context.shaderColorG, context.shaderColorB, context.shaderColorA);
|
||||
lightmapX = context.lastBrightnessX;
|
||||
lightmapY = context.lastBrightnessY;
|
||||
extendedHandler = GLSMHooks.immediateExtendedHandler;
|
||||
blendHandler = GLSMHooks.blendHandler;
|
||||
clientActiveUnit = context.clientActiveTextureUnit;
|
||||
outer = GLStateManager.pushState(StateSet.forMask(GL11.GL_ALL_ATTRIB_BITS));
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.setTextureMatrix(0, new Matrix4f());
|
||||
GLStateManager.glColor4f(1, 1, 1, 1);
|
||||
GLStateManager.glColorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE);
|
||||
GLStateManager.disableColorMaterial();
|
||||
GLStateManager.enableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
GLStateManager.glPolygonOffset(2, 3);
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1F);
|
||||
GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
GLStateManager.cancelExitCapture();
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0 + clientActiveUnit);
|
||||
GLSMHooks.blendHandler = blendHandler;
|
||||
GLSMHooks.immediateExtendedHandler = extendedHandler;
|
||||
GLStateManager.getBlendMode().setVanillaLayer(null);
|
||||
GLStateManager.getBlendState().setVanillaLayer(null);
|
||||
GLStateManager.setModelViewMatrix(modelView);
|
||||
GLStateManager.setProjectionMatrix(projection);
|
||||
GLStateManager.setTextureMatrix(0, texture);
|
||||
GLStateManager.setOverlayColor(overlay.x, overlay.y, overlay.z, overlay.w);
|
||||
GLStateManager.setShaderColor(shaderColor.x, shaderColor.y, shaderColor.z, shaderColor.w);
|
||||
GLStateManager.setLightmapTextureCoords(GL13.GL_TEXTURE1, lightmapX, lightmapY);
|
||||
GLStateManager.popStateTo(outer);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unchangedWritesStillApplyAndLeaveOtherPolygonStateAlone() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.enableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
GLStateManager.glEnable(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
GLStateManager.glPolygonOffset(7, 11);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.applyExitState(state);
|
||||
assertTrue(GLStateManager.getCullState().isEnabled());
|
||||
assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode());
|
||||
assertTrue(GLStateManager.getPolygonOffsetFillState().isEnabled());
|
||||
assertEquals(7, GLStateManager.getPolygonState().getOffsetFactor());
|
||||
assertEquals(11, GLStateManager.getPolygonState().getOffsetUnits());
|
||||
}
|
||||
|
||||
@Test
|
||||
void attribPopUndoesTheWritesItRestores() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glEnable(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
assertEquals(ExitState.POLYGON_OFFSET_ENABLE_WRITTEN, GLStateManager.getExitPolygonOffsetWrites());
|
||||
GLStateManager.glPushAttrib(GL11.GL_POLYGON_BIT | GL11.GL_CURRENT_BIT);
|
||||
GLStateManager.glPolygonOffset(5, 6);
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glColor4f(0, 0, 0, 1);
|
||||
assertEquals(3, GLStateManager.getExitPolygonOffsetWrites());
|
||||
GLStateManager.glPopAttrib();
|
||||
assertEquals(ExitState.POLYGON_OFFSET_ENABLE_WRITTEN, GLStateManager.getExitPolygonOffsetWrites());
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glColor4f(0.25F, 0.5F, 0.75F, 1);
|
||||
GLStateManager.glPolygonOffset(8, 9);
|
||||
GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
|
||||
GLStateManager.applyExitState(state);
|
||||
assertFalse(GLStateManager.getCullState().isEnabled());
|
||||
assertEquals(0.25F, GLStateManager.getColor().getRed());
|
||||
assertEquals(8, GLStateManager.getPolygonState().getOffsetFactor());
|
||||
assertTrue(GLStateManager.getPolygonOffsetFillState().isEnabled());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void drawsDependOnTheAlphaFunctionOnlyWhenTheyReadItFromState(boolean materialFromState) {
|
||||
GLStateManager.enableAlphaTest();
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glColor4f(1, 1, 1, 1);
|
||||
GLStateManager.requireExitDrawState(materialFromState);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.glColor4f(0, 0, 0, 1);
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.6F);
|
||||
assertEquals(!materialFromState, GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1F);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.disableCull();
|
||||
assertFalse(GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = {0, 1, 2})
|
||||
void blendAndAlphaFunctionsMatterOnlyToDrawsWithThemEnabled(int mode) {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.disableAlphaTest();
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1F);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.requireExitDrawState(true);
|
||||
if (mode != 0) {
|
||||
if (mode == 2) {
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1F);
|
||||
}
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.enableAlphaTest();
|
||||
GLStateManager.requireExitDrawState(true);
|
||||
}
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.disableAlphaTest();
|
||||
GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
assertEquals(mode != 1, GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5F);
|
||||
assertEquals(mode != 1, GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1F);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
@Test
|
||||
void settingTheClientTextureUnitToItsCurrentValueIsStillRecorded() {
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glPushAttrib(GL11.GL_TEXTURE_BIT);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.glPopAttrib();
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.applyExitState(state);
|
||||
assertEquals(0, GLStateManager.getClientActiveTextureUnit());
|
||||
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
|
||||
GLStateManager.applyExitState(state);
|
||||
assertEquals(1, GLStateManager.getClientActiveTextureUnit());
|
||||
}
|
||||
|
||||
@Test
|
||||
void drawAfterAnAttribPopDependsOnTheOriginalCullState() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.requireExitDrawState(false);
|
||||
GLStateManager.glPopAttrib();
|
||||
GLStateManager.requireExitDrawState(false);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.disableCull();
|
||||
assertFalse(GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
@Test
|
||||
void lightingAndShaderStateAreNotDrawDependencies() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertFalse(GLStateManager.isExitLightingWritten());
|
||||
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
GLStateManager.enableLighting();
|
||||
assertTrue(GLStateManager.isExitLightingWritten());
|
||||
GLStateManager.glPopAttrib();
|
||||
assertFalse(GLStateManager.isExitLightingWritten());
|
||||
GLStateManager.requireExitDrawState(false);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
|
||||
GLStateManager.getLightingState().setEnabled(!GLStateManager.getLightingState().isEnabled());
|
||||
GLStateManager.getNormalizeState().setEnabled(!GLStateManager.getNormalizeState().isEnabled());
|
||||
GLStateManager.getRescaleNormalState().setEnabled(!GLStateManager.getRescaleNormalState().isEnabled());
|
||||
GLStateManager.getLightStates()[0].setEnabled(!GLStateManager.getLightStates()[0].isEnabled());
|
||||
GLStateManager.getLightStates()[1].setEnabled(!GLStateManager.getLightStates()[1].isEnabled());
|
||||
GLStateManager.glShadeModel(GLStateManager.getShadeModelState().getValue() == GL11.GL_FLAT ? GL11.GL_SMOOTH : GL11.GL_FLAT);
|
||||
GLStateManager.setShaderColor(0.1F, 0.2F, 0.3F, 0.4F);
|
||||
GLStateManager.glNormal3f(0.5F, 0.25F, 0.75F);
|
||||
GLStateManager.glTexCoord2f(0.75F, 0.25F);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = {0, 1, 2, 3})
|
||||
void tessellatorDrawsDependOnlyOnTheCurrentValuesTheyFillIn(int mode) {
|
||||
final Tessellator tess = new Tessellator();
|
||||
tess.rawBuffer = new int[32];
|
||||
tess.vertexCount = 4;
|
||||
tess.drawMode = GL11.GL_QUADS;
|
||||
tess.hasNormals = mode != 1 && mode != 3;
|
||||
tess.hasTexture = mode != 2;
|
||||
tess.hasColor = tess.hasBrightness = true;
|
||||
GLSMHooks.immediateExtendedHandler = mode == 3 ? new FaceNormals() : null;
|
||||
GLStateManager.glNormal3f(0, 1, 0);
|
||||
GLStateManager.glTexCoord2f(0.25F, 0.5F);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
TessellatorStreamingDrawer.fillMissingAttributes(tess);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
if (mode == 1) assertEquals(0x7F00, tess.rawBuffer[6]);
|
||||
if (mode == 2) assertEquals(Float.floatToRawIntBits(0.25F), tess.rawBuffer[3]);
|
||||
if (mode == 3) assertEquals(0x7F, tess.rawBuffer[6]);
|
||||
GLStateManager.glNormal3f(1, 0, 0);
|
||||
GLStateManager.glTexCoord2f(0.75F, 0.5F);
|
||||
assertEquals(mode == 0 || mode == 3, GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void deletingTheBoundTextureBlocksApplyingEvenIfTheCaptureUnboundIt(boolean explicitlyUnbound) {
|
||||
final int id = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, id);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
if (explicitlyUnbound) GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.glDeleteTextures(id);
|
||||
assertFalse(GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void textureMatrixMustMatchOnlyWhenTheCaptureMultipliedOntoIt(boolean absolute) {
|
||||
GLStateManager.setTextureMatrix(0, new Matrix4f().translation(1, 2, 0));
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
|
||||
GLStateManager.glPushMatrix();
|
||||
if (absolute) GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glTranslatef(3, 4, 0);
|
||||
GLStateManager.requireExitDrawState(false);
|
||||
GLStateManager.glPopMatrix();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
final Matrix4f mainTexture = new Matrix4f().translation(8, 9, 0);
|
||||
GLStateManager.setTextureMatrix(0, mainTexture);
|
||||
assertEquals(absolute, GLStateManager.canApplyExitState(state));
|
||||
if (absolute) {
|
||||
GLStateManager.applyExitState(state);
|
||||
assertEquals(mainTexture, GLStateManager.getTextures().getTextureUnitMatrix(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void changedOrUnbalancedViewMatricesReject() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glTranslatef(1, 0, 0);
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.setModelViewMatrix(modelView);
|
||||
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glPushMatrix();
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.glPopMatrix();
|
||||
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glTranslatef(2, 3, 4);
|
||||
GLStateManager.glPopMatrix();
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glPushMatrix();
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glPopMatrix();
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = {0, 1, 2, 3, 4, 5})
|
||||
void untrackedCallsAndStateQueriesReject(int operation) {
|
||||
final ExitState state = new ExitState();
|
||||
final int program = GLStateManager.getActiveProgram();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
switch (operation) {
|
||||
case 0 -> GLStateManager.glIsEnabled(GL11.GL_CULL_FACE);
|
||||
case 1 -> GLStateManager.glGetInteger(GL11.GL_DEPTH_FUNC);
|
||||
case 2 -> GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
case 3 -> GLStateManager.glUseProgram(program);
|
||||
case 4 -> GLStateManager.glBlendEquation(GL14.GL_FUNC_ADD);
|
||||
case 5 -> GLStateManager.fogColor((float) GLStateManager.getFogState().getFogColor().x,
|
||||
(float) GLStateManager.getFogState().getFogColor().y, (float) GLStateManager.getFogState().getFogColor().z,
|
||||
GLStateManager.getFogState().getFogAlpha());
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@Test
|
||||
void suspendedCallsAreNeitherRecordedNorRejected() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
final boolean suspended = GLStateManager.suspendExitCapture();
|
||||
try {
|
||||
assertFalse(GLStateManager.isExitCaptureActive());
|
||||
GLStateManager.glIsEnabled(GL11.GL_CULL_FACE);
|
||||
GLStateManager.glColor4f(0, 0, 0, 1);
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
GLStateManager.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
} finally {
|
||||
BatchStateGuard.resume();
|
||||
}
|
||||
} finally {
|
||||
GLStateManager.resumeExitCapture(suspended);
|
||||
}
|
||||
BatchStateGuard.suspend();
|
||||
try {
|
||||
assertFalse(GLStateManager.isExitCaptureActive());
|
||||
GLStateManager.glGetInteger(GL11.GL_DEPTH_FUNC);
|
||||
} finally {
|
||||
BatchStateGuard.resume();
|
||||
}
|
||||
assertTrue(GLStateManager.isExitCaptureActive());
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.glColor4f(0.5F, 1, 1, 1);
|
||||
GLStateManager.applyExitState(state);
|
||||
assertEquals(0.5F, GLStateManager.getColor().getRed());
|
||||
}
|
||||
|
||||
@Test
|
||||
void attribPushedWhileSuspendedRestoresThatCapturesWrites() {
|
||||
final ExitState earlier = new ExitState();
|
||||
GLStateManager.beginExitCapture(earlier);
|
||||
GLStateManager.glColor4f(0.5F, 1, 1, 1);
|
||||
GLStateManager.glPushAttrib(GL11.GL_CURRENT_BIT);
|
||||
GLStateManager.glPopAttrib();
|
||||
assertTrue(GLStateManager.finishExitCapture(earlier));
|
||||
|
||||
GLStateManager.glColor4f(0.25F, 1, 1, 1);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
final boolean suspended = GLStateManager.suspendExitCapture();
|
||||
try {
|
||||
GLStateManager.glPushAttrib(GL11.GL_CURRENT_BIT);
|
||||
} finally {
|
||||
GLStateManager.resumeExitCapture(suspended);
|
||||
}
|
||||
GLStateManager.glColor4f(0, 0, 0, 1);
|
||||
GLStateManager.glPopAttrib();
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
|
||||
GLStateManager.glColor4f(0.75F, 1, 1, 1);
|
||||
GLStateManager.applyExitState(state);
|
||||
assertEquals(0.75F, GLStateManager.getColor().getRed(), "the pop restored color, so the capture leaves it alone");
|
||||
}
|
||||
|
||||
@Test
|
||||
void attribPoppedWhileSuspendedUndoesTheCapturesWrites() {
|
||||
GLStateManager.glColor4f(0.25F, 1, 1, 1);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glPushAttrib(GL11.GL_CURRENT_BIT);
|
||||
GLStateManager.glColor4f(0, 0, 0, 1);
|
||||
final boolean suspended = GLStateManager.suspendExitCapture();
|
||||
try {
|
||||
GLStateManager.glPopAttrib();
|
||||
} finally {
|
||||
GLStateManager.resumeExitCapture(suspended);
|
||||
}
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
|
||||
GLStateManager.glColor4f(0.75F, 1, 1, 1);
|
||||
GLStateManager.applyExitState(state);
|
||||
assertEquals(0.75F, GLStateManager.getColor().getRed(), "the pop restored color, so the capture leaves it alone");
|
||||
}
|
||||
|
||||
@Test
|
||||
void separateAndReusedCapturesKeepOnlyTheirOwnWrites() {
|
||||
final ExitState first = new ExitState();
|
||||
final ExitState second = new ExitState();
|
||||
GLStateManager.beginExitCapture(first);
|
||||
GLStateManager.glColor4f(0.25F, 1, 1, 1);
|
||||
GLStateManager.setOverlayColor(0.1F, 0.2F, 0.3F, 0.4F);
|
||||
assertTrue(GLStateManager.finishExitCapture(first));
|
||||
GLStateManager.beginExitCapture(second);
|
||||
GLStateManager.glColor4f(0.75F, 1, 1, 1);
|
||||
assertTrue(GLStateManager.finishExitCapture(second));
|
||||
GLStateManager.applyExitState(first);
|
||||
assertEquals(0.25F, GLStateManager.getColor().getRed());
|
||||
assertEquals(0.4F, GLStateManager.getOverlayA());
|
||||
GLStateManager.applyExitState(second);
|
||||
assertEquals(0.75F, GLStateManager.getColor().getRed());
|
||||
first.clear();
|
||||
assertFalse(GLStateManager.canApplyExitState(first));
|
||||
GLStateManager.beginExitCapture(first);
|
||||
assertTrue(GLStateManager.finishExitCapture(first));
|
||||
GLStateManager.glColor4f(0.5F, 1, 1, 1);
|
||||
GLStateManager.applyExitState(first);
|
||||
assertEquals(0.5F, GLStateManager.getColor().getRed());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void toggledColorMaterialKeepsItsMaterialWritesUnlessAnAttribPopUndoesThem(boolean scoped) {
|
||||
GLStateManager.enableColorMaterial();
|
||||
GLStateManager.disableColorMaterial();
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glColor4f(1, 1, 1, 1);
|
||||
if (scoped) GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
|
||||
GLStateManager.enableColorMaterial();
|
||||
GLStateManager.disableColorMaterial();
|
||||
if (scoped) GLStateManager.glPopAttrib();
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
final var changed = BufferUtils.createFloatBuffer(4);
|
||||
changed.put(new float[]{0.2F, 0.3F, 0.4F, 1}).flip();
|
||||
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE, changed);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.applyExitState(state);
|
||||
assertEquals(scoped ? 0.2F : 1, GLStateManager.getFrontMaterial().ambient.x);
|
||||
assertEquals(scoped ? 0.2F : 1, GLStateManager.getBackMaterial().diffuse.x);
|
||||
assertFalse(GLStateManager.getColorMaterial().isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
void colorMaterialSettingsTheCaptureNeverTouchedAreLeftAlone() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glColor4f(1, 1, 1, 1);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.glColorMaterial(GL11.GL_BACK, GL11.GL_EMISSION);
|
||||
GLStateManager.enableColorMaterial();
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.applyExitState(state);
|
||||
assertTrue(GLStateManager.getColorMaterial().isEnabled());
|
||||
assertEquals(GL11.GL_BACK, GLStateManager.getColorMaterialFace().getValue());
|
||||
assertEquals(GL11.GL_EMISSION, GLStateManager.getColorMaterialParameter().getValue());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void colorMaterialFaceMustMatchOnlyWhenTheCaptureDidNotSetIt(boolean establishesContext) {
|
||||
GLStateManager.glColorMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.glColor4f(1, 1, 1, 1);
|
||||
if (establishesContext) GLStateManager.glColorMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE);
|
||||
GLStateManager.enableColorMaterial();
|
||||
GLStateManager.disableColorMaterial();
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
GLStateManager.glColorMaterial(GL11.GL_BACK, GL11.GL_EMISSION);
|
||||
assertEquals(establishesContext, GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
@Test
|
||||
void writesUnderALockedIrisOverrideRecordAndApplyTheVanillaValues() {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GLStateManager.getBlendMode().beforeModify();
|
||||
GLStateManager.getBlendState().beforeModify();
|
||||
final LockedBlend lock = new LockedBlend();
|
||||
GLSMHooks.blendHandler = lock;
|
||||
GLStateManager.getBlendMode().setVanillaLayer(lock);
|
||||
GLStateManager.getBlendState().setVanillaLayer(lock);
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
GLStateManager.enableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
lock.enabled = false;
|
||||
lock.blend.setAll(GL11.GL_DST_COLOR, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.applyExitState(state);
|
||||
assertTrue(lock.enabled);
|
||||
assertEquals(GL11.GL_SRC_ALPHA, lock.blend.getSrcRgb());
|
||||
assertFalse(GLStateManager.getBlendMode().isEnabled());
|
||||
assertEquals(GL11.GL_ONE, GLStateManager.getBlendState().getSrcRgb());
|
||||
assertEquals(GL11.GL_ZERO, GLStateManager.getBlendState().getDstRgb());
|
||||
}
|
||||
|
||||
private static final class LockedBlend implements DeferredBlendHandler, VanillaBooleanLayer, VanillaStateLayer<BlendState> {
|
||||
boolean enabled = true;
|
||||
final BlendState blend = new BlendState(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
@Override public boolean isBlendLocked() { return true; }
|
||||
@Override public boolean isOverrideHeld() { return true; }
|
||||
@Override public void deferBlendModeToggle(boolean value) { enabled = value; }
|
||||
@Override public void deferBlendFunc(int sr, int dr, int sa, int da) { blend.setAll(sr, dr, sa, da); }
|
||||
@Override public void flushDeferredBlend() {}
|
||||
@Override public boolean getVanilla() { return enabled; }
|
||||
@Override public void setVanilla(boolean value) { enabled = value; }
|
||||
@Override public void readVanilla(BlendState into) { into.set(blend); }
|
||||
@Override public void writeVanilla(BlendState from) { blend.set(from); }
|
||||
}
|
||||
|
||||
private static final class FaceNormals implements ImmediateExtendedAttribHandler {
|
||||
@Override public boolean wantsExtended() { return false; }
|
||||
@Override public boolean wantsTangent() { return false; }
|
||||
@Override public boolean wantsEntityIds() { return false; }
|
||||
@Override public boolean wantsExtendedCapture() { return false; }
|
||||
@Override public boolean wantsFaceNormals() { return true; }
|
||||
@Override public void writeFaceNormals(int[] data, int count) {
|
||||
for (int i = 0; i < count; i++) data[i * 8 + 6] = 0x7F;
|
||||
}
|
||||
@Override public void build(int[] data, int count, int primitive, int normal, long destination, int stride) {}
|
||||
@Override public void buildPacked(long source, int stride, int position, int texture, int normal,
|
||||
int count, int primitive, long destination, int destinationStride) {}
|
||||
}
|
||||
}
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.TextureUtilHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.lwjgl.opengl.EXTTextureFilterAnisotropic;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@GLCoreTest
|
||||
class GLSM_ExitTextureFilter_GLTest {
|
||||
private int outer, texture, otherTexture, savedMin, savedMag;
|
||||
private float savedAniso;
|
||||
private boolean failOperation;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
outer = GLStateManager.pushState(StateSet.forMask(GL11.GL_TEXTURE_BIT));
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
texture = GLStateManager.glGenTextures();
|
||||
otherTexture = GLStateManager.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, otherTexture);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
GLStateManager.cancelExitCapture();
|
||||
TextureUtilHooks.clearSavedFilterTexture();
|
||||
GLStateManager.glDeleteTextures(texture);
|
||||
GLStateManager.glDeleteTextures(otherTexture);
|
||||
GLStateManager.popStateTo(outer);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nearestFilterSaveAndRestoreIsAllowedAndApplyLeavesFiltersAlone() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertTrue(save(false, false, 1));
|
||||
assertTrue(GLStateManager.isExitCaptureActive());
|
||||
GLStateManager.requireExitTextureFilter(otherTexture, false);
|
||||
GLStateManager.requireExitTextureFilter(texture, true);
|
||||
restore();
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
assertFilters(GL11.GL_LINEAR_MIPMAP_LINEAR, GL11.GL_LINEAR);
|
||||
assertEquals(0, GLStateManager.ctx().savedFilterTextureId);
|
||||
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
GLStateManager.applyExitState(state);
|
||||
assertFilters(GL11.GL_LINEAR, GL11.GL_NEAREST);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void drawingTheTemporarilyFilteredTextureRequiresNearestFiltering(boolean nearest) {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
save(false, false, 1);
|
||||
GLStateManager.requireExitTextureFilter(texture, nearest);
|
||||
restore();
|
||||
assertEquals(nearest, GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = {0, 1, 2})
|
||||
void savingAnythingButNearestFilteringRejects(int request) {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertFalse(save(request == 0, request == 1, request == 2 ? 2 : 1));
|
||||
restore();
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = {0, 1, 2})
|
||||
void textureParameterReadsAndWritesStillReject(int operation) {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
save(false, false, 1);
|
||||
switch (operation) {
|
||||
case 0 -> GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
|
||||
case 1 -> GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
case 2 -> GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
restore();
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingRestoreRejectsAndDoesNotAffectTheNextCapture() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
save(false, false, 1);
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
restore();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@Test
|
||||
void secondSaveBeforeRestoreRejects() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertTrue(save(false, false, 1));
|
||||
assertFalse(save(false, false, 1));
|
||||
restore();
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void restoreMustUseTheSameTextureAndOriginalParameters(boolean wrongTexture) {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
save(false, false, 1);
|
||||
if (wrongTexture) GLStateManager.ctx().savedFilterTextureId = otherTexture;
|
||||
else savedMin = GL11.GL_NEAREST;
|
||||
restore();
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {false, true})
|
||||
void failedSaveOrRestoreRejectsAndTheNextCaptureStillWorks(boolean restoring) {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
if (restoring) save(false, false, 1);
|
||||
failOperation = true;
|
||||
assertThrows(IllegalStateException.class, () -> {
|
||||
if (restoring) restore();
|
||||
else save(false, false, 1);
|
||||
});
|
||||
assertFalse(GLStateManager.finishExitCapture(state));
|
||||
TextureUtilHooks.clearSavedFilterTexture();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertTrue(GLStateManager.isExitCaptureActive());
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
}
|
||||
|
||||
@Test
|
||||
void openFilterSaveBlocksCapturingAndApplying() {
|
||||
final ExitState state = new ExitState();
|
||||
GLStateManager.beginExitCapture(state);
|
||||
assertTrue(GLStateManager.finishExitCapture(state));
|
||||
save(false, false, 1);
|
||||
assertFalse(GLStateManager.canApplyExitState(state));
|
||||
final ExitState nested = new ExitState();
|
||||
GLStateManager.beginExitCapture(nested);
|
||||
assertFalse(GLStateManager.finishExitCapture(nested));
|
||||
restore();
|
||||
assertTrue(GLStateManager.canApplyExitState(state));
|
||||
}
|
||||
|
||||
private boolean save(boolean blurred, boolean mipmap, float aniso) {
|
||||
final boolean captured = TextureUtilHooks.beginFilterSave(blurred, mipmap, aniso);
|
||||
try {
|
||||
savedMin = GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
|
||||
savedMag = GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER);
|
||||
savedAniso = GLStateManager.glGetTexParameterf(GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
|
||||
blurred ? (mipmap ? GL11.GL_LINEAR_MIPMAP_LINEAR : GL11.GL_LINEAR)
|
||||
: (mipmap ? GL11.GL_NEAREST_MIPMAP_LINEAR : GL11.GL_NEAREST));
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, blurred ? GL11.GL_LINEAR : GL11.GL_NEAREST);
|
||||
GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
|
||||
if (failOperation) throw new IllegalStateException("filter save failed");
|
||||
} catch (RuntimeException | Error failure) {
|
||||
TextureUtilHooks.filterFailed(captured);
|
||||
throw failure;
|
||||
} finally {
|
||||
TextureUtilHooks.endFilterSave(captured);
|
||||
}
|
||||
return captured;
|
||||
}
|
||||
|
||||
private void restore() {
|
||||
final boolean captured = TextureUtilHooks.beginFilterRestore();
|
||||
try {
|
||||
TextureUtilHooks.restoreFilter(savedMin, savedMag);
|
||||
TextureUtilHooks.restoreAniso(savedAniso);
|
||||
if (failOperation) throw new IllegalStateException("filter restore failed");
|
||||
} catch (RuntimeException | Error failure) {
|
||||
TextureUtilHooks.filterFailed(captured);
|
||||
throw failure;
|
||||
} finally {
|
||||
TextureUtilHooks.endFilterRestore(captured);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertFilters(int min, int mag) {
|
||||
final var info = TextureInfoCache.INSTANCE.getInfo(texture);
|
||||
assertEquals(min, info.getMinFilter());
|
||||
assertEquals(mag, info.getMagFilter());
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import org.joml.Vector4f;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -84,61 +85,24 @@ public class GLSM_GLU_UnitTest {
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
|
||||
@Test
|
||||
void gluPickMatrix_matchesManual() {
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@CsvSource({
|
||||
"gluPickMatrix center, 0, 0, 800, 600, 400, 300, 5",
|
||||
"gluPickMatrix off-center viewport, 100, 50, 640, 480, 200, 150, 10",
|
||||
})
|
||||
void gluPickMatrix_matchesManual(String label, int vx, int vy, int vw, int vh, float x, float y, float delta) {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
|
||||
IntBuffer viewport = BufferUtils.createIntBuffer(4);
|
||||
viewport.put(0, 0); // x
|
||||
viewport.put(1, 0); // y
|
||||
viewport.put(2, 800); // width
|
||||
viewport.put(3, 600); // height
|
||||
viewport.put(0, vx).put(1, vy).put(2, vw).put(3, vh);
|
||||
|
||||
float x = 400.0f, y = 300.0f, deltaX = 5.0f, deltaY = 5.0f;
|
||||
|
||||
GLStateManager.gluPickMatrix(x, y, deltaX, deltaY, viewport);
|
||||
|
||||
// Reproduce the expected result: translate then scale
|
||||
Matrix4f expected = new Matrix4f()
|
||||
.translate(
|
||||
(800 - 2 * (x - 0)) / deltaX,
|
||||
(600 - 2 * (y - 0)) / deltaY,
|
||||
0.0f)
|
||||
.scale(
|
||||
800.0f / deltaX,
|
||||
600.0f / deltaY,
|
||||
1.0f);
|
||||
assertMatrixEquals(expected, getProjectionMatrix(), "gluPickMatrix center");
|
||||
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
|
||||
@Test
|
||||
void gluPickMatrix_offCenter() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
|
||||
IntBuffer viewport = BufferUtils.createIntBuffer(4);
|
||||
viewport.put(0, 100);
|
||||
viewport.put(1, 50);
|
||||
viewport.put(2, 640);
|
||||
viewport.put(3, 480);
|
||||
|
||||
float x = 200.0f, y = 150.0f, deltaX = 10.0f, deltaY = 10.0f;
|
||||
|
||||
GLStateManager.gluPickMatrix(x, y, deltaX, deltaY, viewport);
|
||||
GLStateManager.gluPickMatrix(x, y, delta, delta, viewport);
|
||||
|
||||
Matrix4f expected = new Matrix4f()
|
||||
.translate(
|
||||
(640 - 2 * (x - 100)) / deltaX,
|
||||
(480 - 2 * (y - 50)) / deltaY,
|
||||
0.0f)
|
||||
.scale(
|
||||
640.0f / deltaX,
|
||||
480.0f / deltaY,
|
||||
1.0f);
|
||||
assertMatrixEquals(expected, getProjectionMatrix(), "gluPickMatrix off-center viewport");
|
||||
.translate((vw - 2 * (x - vx)) / delta, (vh - 2 * (y - vy)) / delta, 0.0f)
|
||||
.scale(vw / delta, vh / delta, 1.0f);
|
||||
assertMatrixEquals(expected, getProjectionMatrix(), label);
|
||||
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
}
|
||||
@@ -178,38 +142,21 @@ public class GLSM_GLU_UnitTest {
|
||||
assertEquals(expected, GLStateManager.gluErrorString(code));
|
||||
}
|
||||
|
||||
@Test
|
||||
void gluUnProject_identity() {
|
||||
FloatBuffer model = BufferUtils.createFloatBuffer(16);
|
||||
FloatBuffer proj = BufferUtils.createFloatBuffer(16);
|
||||
new Matrix4f().get(model);
|
||||
new Matrix4f().get(proj);
|
||||
|
||||
IntBuffer viewport = BufferUtils.createIntBuffer(4);
|
||||
viewport.put(0, 0).put(1, 0).put(2, 800).put(3, 600);
|
||||
|
||||
FloatBuffer result = BufferUtils.createFloatBuffer(3);
|
||||
boolean ok = GLStateManager.gluUnProject(400.0f, 300.0f, 0.5f, model, proj, viewport, result);
|
||||
assertTrue(ok, "gluUnProject should succeed");
|
||||
assertEquals(0.0f, result.get(0), 0.001f, "X");
|
||||
assertEquals(0.0f, result.get(1), 0.001f, "Y");
|
||||
assertEquals(0.0f, result.get(2), 0.001f, "Z");
|
||||
static Stream<Arguments> gluUnProject_roundTrips() {
|
||||
return Stream.of(
|
||||
Arguments.of("identity", new Matrix4f(), new Matrix4f(), 0.001f),
|
||||
Arguments.of("perspective with lookAt", new Matrix4f().perspective((float) Math.toRadians(60.0f), 800.0f / 600.0f, 0.1f, 100.0f),
|
||||
new Matrix4f().lookAt(0, 0, 5, 0, 0, 0, 0, 1, 0), 0.01f));
|
||||
}
|
||||
|
||||
@Test
|
||||
void gluUnProject_withPerspective() {
|
||||
Matrix4f proj = new Matrix4f().perspective((float) Math.toRadians(60.0f), 800.0f / 600.0f, 0.1f, 100.0f);
|
||||
Matrix4f model = new Matrix4f().lookAt(0, 0, 5, 0, 0, 0, 0, 1, 0);
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource
|
||||
void gluUnProject_roundTrips(String label, Matrix4f proj, Matrix4f model, float tolerance) {
|
||||
Vector4f worldPt = new Vector4f(0, 0, 0, 1);
|
||||
Matrix4f mvp = new Matrix4f(proj).mul(model);
|
||||
mvp.transform(worldPt);
|
||||
float ndcX = worldPt.x / worldPt.w;
|
||||
float ndcY = worldPt.y / worldPt.w;
|
||||
float ndcZ = worldPt.z / worldPt.w;
|
||||
float winX = (ndcX * 0.5f + 0.5f) * 800.0f;
|
||||
float winY = (ndcY * 0.5f + 0.5f) * 600.0f;
|
||||
float winZ = ndcZ * 0.5f + 0.5f;
|
||||
new Matrix4f(proj).mul(model).transform(worldPt);
|
||||
float winX = (worldPt.x / worldPt.w * 0.5f + 0.5f) * 800.0f;
|
||||
float winY = (worldPt.y / worldPt.w * 0.5f + 0.5f) * 600.0f;
|
||||
float winZ = worldPt.z / worldPt.w * 0.5f + 0.5f;
|
||||
|
||||
FloatBuffer modelBuf = BufferUtils.createFloatBuffer(16);
|
||||
FloatBuffer projBuf = BufferUtils.createFloatBuffer(16);
|
||||
@@ -220,11 +167,10 @@ public class GLSM_GLU_UnitTest {
|
||||
viewport.put(0, 0).put(1, 0).put(2, 800).put(3, 600);
|
||||
|
||||
FloatBuffer result = BufferUtils.createFloatBuffer(3);
|
||||
boolean ok = GLStateManager.gluUnProject(winX, winY, winZ, modelBuf, projBuf, viewport, result);
|
||||
assertTrue(ok, "gluUnProject should succeed");
|
||||
assertEquals(0.0f, result.get(0), 0.01f, "X should unproject back to 0");
|
||||
assertEquals(0.0f, result.get(1), 0.01f, "Y should unproject back to 0");
|
||||
assertEquals(0.0f, result.get(2), 0.01f, "Z should unproject back to 0");
|
||||
assertTrue(GLStateManager.gluUnProject(winX, winY, winZ, modelBuf, projBuf, viewport, result), "gluUnProject should succeed");
|
||||
assertEquals(0.0f, result.get(0), tolerance, "X should unproject back to 0");
|
||||
assertEquals(0.0f, result.get(1), tolerance, "Y should unproject back to 0");
|
||||
assertEquals(0.0f, result.get(2), tolerance, "Z should unproject back to 0");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -261,6 +207,8 @@ public class GLSM_GLU_UnitTest {
|
||||
|
||||
int result = GLStateManager.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, GL11.GL_RGBA, 4, 4, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
|
||||
assertEquals(0, result, "gluBuild2DMipmaps should return 0 on success");
|
||||
assertEquals(4, GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_WIDTH));
|
||||
assertEquals(1, GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 2, GL11.GL_TEXTURE_WIDTH));
|
||||
|
||||
GL11.glDeleteTextures(texId);
|
||||
}
|
||||
|
||||
+31
@@ -83,13 +83,24 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
|
||||
|
||||
@Test
|
||||
void deletingTheBoundTextureClearsTheImageUnit() {
|
||||
final boolean driverHasImages = GLStateManager.capabilities != null && GLStateManager.capabilities.OpenGL42;
|
||||
final int tex = GL11.glGenTextures();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
try {
|
||||
GLStateManager.glBindImageTexture(1, tex, 0, true, 0, GL15.GL_READ_WRITE, GL30.GL_R32UI);
|
||||
assertEquals(tex, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 1));
|
||||
if (driverHasImages) assertEquals(tex, GL30.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 1));
|
||||
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 1), "GL unbinds a deleted texture from every image unit");
|
||||
if (driverHasImages) assertEquals(0, GL30.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 1), "driver unbinds a deleted texture from every image unit");
|
||||
assertEquals(GL15.GL_READ_ONLY, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_ACCESS, 1));
|
||||
assertEquals(GL30.GL_R8, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_FORMAT, 1));
|
||||
if (driverHasImages) {
|
||||
assertEquals(GL30.glGetInteger(GL42.GL_IMAGE_BINDING_ACCESS, 1), GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_ACCESS, 1));
|
||||
assertEquals(GL30.glGetInteger(GL42.GL_IMAGE_BINDING_FORMAT, 1), GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_FORMAT, 1));
|
||||
}
|
||||
} finally {
|
||||
unbind(1);
|
||||
}
|
||||
@@ -151,9 +162,29 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
|
||||
assertEquals(GL30.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 1), GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 1));
|
||||
assertEquals(GL30.glGetInteger(GL42.GL_IMAGE_BINDING_ACCESS, 1), GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_ACCESS, 1));
|
||||
assertEquals(GL30.glGetInteger(GL42.GL_IMAGE_BINDING_FORMAT, 1), GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_FORMAT, 1));
|
||||
|
||||
assertEquals(GL30.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 3), GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 3));
|
||||
assertEquals(GL30.glGetInteger(GL42.GL_IMAGE_BINDING_ACCESS, 3), GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_ACCESS, 3));
|
||||
assertEquals(GL30.glGetInteger(GL42.GL_IMAGE_BINDING_FORMAT, 3), GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_FORMAT, 3));
|
||||
} finally {
|
||||
unbind(1);
|
||||
GLStateManager.glDeleteTextures(tex);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindingTextureZeroClearsLayeredAndLayer() {
|
||||
try {
|
||||
GLStateManager.glBindImageTexture(2, 0, 0, true, 3, GL15.GL_READ_WRITE, GL11.GL_RGBA8);
|
||||
|
||||
assertEquals(GL11.GL_FALSE, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_LAYERED, 2));
|
||||
assertEquals(0, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_LAYER, 2));
|
||||
if (GLStateManager.capabilities != null && GLStateManager.capabilities.OpenGL42) {
|
||||
assertEquals(GL30.glGetInteger(GL42.GL_IMAGE_BINDING_LAYERED, 2), GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_LAYERED, 2));
|
||||
assertEquals(GL30.glGetInteger(GL42.GL_IMAGE_BINDING_LAYER, 2), GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_LAYER, 2));
|
||||
}
|
||||
} finally {
|
||||
unbind(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -856,6 +856,11 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isDriverHint(int target) {
|
||||
return target == GL11.GL_LINE_SMOOTH_HINT || target == GL11.GL_POLYGON_SMOOTH_HINT
|
||||
|| target == GL13.GL_TEXTURE_COMPRESSION_HINT || target == GL20.GL_FRAGMENT_SHADER_DERIVATIVE_HINT;
|
||||
}
|
||||
|
||||
@Test
|
||||
void hintsRoundTripAndFollowHintBit() {
|
||||
final int base = GLStateManager.getAttribDepth();
|
||||
@@ -869,17 +874,21 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
assertEquals(GL11.GL_DONT_CARE, GLStateManager.glGetInteger(target), "default " + name);
|
||||
GLStateManager.glHint(target, GL11.GL_NICEST);
|
||||
assertEquals(GL11.GL_NICEST, GLStateManager.glGetInteger(target), "nicest " + name);
|
||||
if (isDriverHint(target)) assertEquals(GL11.GL_NICEST, GL11.glGetInteger(target), "driver nicest " + name);
|
||||
GLStateManager.glHint(target, 12345);
|
||||
assertEquals(GL11.GL_NICEST, GLStateManager.glGetInteger(target), "invalid mode " + name);
|
||||
if (isDriverHint(target)) assertEquals(GL11.GL_NICEST, GL11.glGetInteger(target), "driver invalid mode " + name);
|
||||
}
|
||||
GLStateManager.glPushAttrib(GL11.GL_HINT_BIT);
|
||||
for (final int target : HINT_TARGETS) {
|
||||
GLStateManager.glHint(target, GL11.GL_FASTEST);
|
||||
assertEquals(GL11.GL_FASTEST, GLStateManager.glGetInteger(target), "fastest " + hex(target));
|
||||
if (isDriverHint(target)) assertEquals(GL11.GL_FASTEST, GL11.glGetInteger(target), "driver fastest " + hex(target));
|
||||
}
|
||||
GLStateManager.glPopAttrib();
|
||||
for (final int target : HINT_TARGETS) {
|
||||
assertEquals(GL11.GL_NICEST, GLStateManager.glGetInteger(target), "restored " + hex(target));
|
||||
if (isDriverHint(target)) assertEquals(GL11.GL_NICEST, GL11.glGetInteger(target), "driver restored " + hex(target));
|
||||
}
|
||||
assertEquals(GL11.GL_RENDER, GLStateManager.glGetInteger(GL11.GL_RENDER_MODE));
|
||||
} finally {
|
||||
@@ -916,6 +925,7 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
assertEquals(0.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN));
|
||||
assertEquals(2.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX));
|
||||
assertEquals(4.5f, GLStateManager.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE));
|
||||
assertEquals(4.5f, GL11.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE));
|
||||
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, -1f);
|
||||
assertEquals(0.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN));
|
||||
|
||||
@@ -933,6 +943,7 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
|
||||
GLStateManager.glPointParameteri(GL20.GL_POINT_SPRITE_COORD_ORIGIN, GL20.GL_LOWER_LEFT);
|
||||
assertEquals(GL20.GL_LOWER_LEFT, GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN));
|
||||
assertEquals(GL20.GL_LOWER_LEFT, GL11.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN));
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_POINT_BIT);
|
||||
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, 1f);
|
||||
@@ -944,6 +955,7 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
GLStateManager.glPointParameteri(GL20.GL_POINT_SPRITE_COORD_ORIGIN, GL20.GL_UPPER_LEFT);
|
||||
assertEquals(1f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN));
|
||||
assertEquals(GL20.GL_UPPER_LEFT, GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN));
|
||||
assertEquals(GL20.GL_UPPER_LEFT, GL11.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN));
|
||||
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, Float.NaN);
|
||||
assertTrue(Float.isNaN(GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN)));
|
||||
GLStateManager.glPopAttrib();
|
||||
@@ -951,7 +963,9 @@ public class GLSM_LegacyStackQueries_GLTest {
|
||||
assertEquals(0.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN));
|
||||
assertEquals(2.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX));
|
||||
assertEquals(4.5f, GLStateManager.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE));
|
||||
assertEquals(4.5f, GL11.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE));
|
||||
assertEquals(GL20.GL_LOWER_LEFT, GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN));
|
||||
assertEquals(GL20.GL_LOWER_LEFT, GL11.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN));
|
||||
attenuation.clear();
|
||||
GLStateManager.glGetFloat(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation);
|
||||
assertEquals(0.5f, attenuation.get(0));
|
||||
|
||||
+3
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
@@ -24,6 +25,8 @@ public class GLSM_PolygonOffsetClamp_UnitTest {
|
||||
GLStateManager.glPolygonOffsetClamp(1.5f, 2.5f, 0.0f);
|
||||
assertEquals(1.5f, GLStateManager.ctx().polygonState.getOffsetFactor());
|
||||
assertEquals(2.5f, GLStateManager.ctx().polygonState.getOffsetUnits());
|
||||
assertEquals(1.5f, GL11.glGetFloat(GL11.GL_POLYGON_OFFSET_FACTOR));
|
||||
assertEquals(2.5f, GL11.glGetFloat(GL11.GL_POLYGON_OFFSET_UNITS));
|
||||
assertEquals(0.0f, cachedClamp());
|
||||
}
|
||||
|
||||
|
||||
+21
-43
@@ -2,9 +2,14 @@ package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyIsEnabled;
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -42,22 +47,6 @@ public class GLSM_PopAttribMaskFidelity_GLTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void enableBitRestoresCull() {
|
||||
try {
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, false, "Cull - Initial");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_CULL_FACE);
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, true, "Cull - After enable");
|
||||
GLStateManager.glPopAttrib();
|
||||
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, false, "Cull - GL_ENABLE_BIT member, must restore");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_CULL_FACE);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void innerNonMemberPopKeepsChange_OuterMemberPopRestores() {
|
||||
try {
|
||||
@@ -154,21 +143,28 @@ public class GLSM_PopAttribMaskFidelity_GLTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void transformBitPopRestoresClipPlaneAndMask() {
|
||||
static Stream<Arguments> clipPlanePops() {
|
||||
return Stream.of(
|
||||
Arguments.of("GL_TRANSFORM_BIT", GL11.GL_TRANSFORM_BIT, GL11.GL_CLIP_PLANE1, true),
|
||||
Arguments.of("GL_COLOR_BUFFER_BIT", GL11.GL_COLOR_BUFFER_BIT, GL11.GL_CLIP_PLANE2, false));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource
|
||||
void clipPlanePops(String bitName, int bit, int plane, boolean member) {
|
||||
try {
|
||||
verifyClipPlaneMask("Initial");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_TRANSFORM_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_CLIP_PLANE1);
|
||||
verifyIsEnabled(GL11.GL_CLIP_PLANE1, true, "Clip1 - After enable inside GL_TRANSFORM_BIT");
|
||||
verifyClipPlaneMask("After enable inside GL_TRANSFORM_BIT");
|
||||
GLStateManager.glPushAttrib(bit);
|
||||
GLStateManager.glEnable(plane);
|
||||
verifyIsEnabled(plane, true, "After enable inside " + bitName);
|
||||
verifyClipPlaneMask("After enable inside " + bitName);
|
||||
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyIsEnabled(GL11.GL_CLIP_PLANE1, false, "Clip1 - GL_TRANSFORM_BIT member, must restore");
|
||||
verifyClipPlaneMask("After GL_TRANSFORM_BIT pop");
|
||||
verifyIsEnabled(plane, !member, member ? bitName + " member, must restore" : "Not a " + bitName + " member, must stay toggled");
|
||||
verifyClipPlaneMask("After " + bitName + " pop");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_CLIP_PLANE1);
|
||||
GLStateManager.glDisable(plane);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,22 +227,4 @@ public class GLSM_PopAttribMaskFidelity_GLTest {
|
||||
GLStateManager.glClearDepth(1.0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void colorBufferBitPopDoesNotRestoreClipPlaneButMaskStaysFaithful() {
|
||||
try {
|
||||
verifyClipPlaneMask("Initial");
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT);
|
||||
GLStateManager.glEnable(GL11.GL_CLIP_PLANE2);
|
||||
verifyIsEnabled(GL11.GL_CLIP_PLANE2, true, "Clip2 - After enable inside GL_COLOR_BUFFER_BIT");
|
||||
verifyClipPlaneMask("After enable inside GL_COLOR_BUFFER_BIT");
|
||||
|
||||
GLStateManager.glPopAttrib();
|
||||
verifyIsEnabled(GL11.GL_CLIP_PLANE2, true, "Clip2 - Not a GL_COLOR_BUFFER_BIT member, must stay toggled");
|
||||
verifyClipPlaneMask("After GL_COLOR_BUFFER_BIT pop");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_CLIP_PLANE2);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
@@ -330,8 +330,10 @@ public class GLSM_SharedDrawableContext_GLTest {
|
||||
GLStateManager.glBindVertexArray(bound);
|
||||
GLStateManager.glCallList(foreignList);
|
||||
assertEquals(bound, GLStateManager.getBoundVAO());
|
||||
assertEquals(bound, GL11.glGetInteger(GL30.GL_VERTEX_ARRAY_BINDING));
|
||||
GLStateManager.glCallList(localList);
|
||||
assertEquals(local, GLStateManager.getBoundVAO());
|
||||
assertEquals(local, GL11.glGetInteger(GL30.GL_VERTEX_ARRAY_BINDING));
|
||||
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glDeleteVertexArrays(bound);
|
||||
|
||||
+3
-21
@@ -5,17 +5,12 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||
import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Set;
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -38,22 +33,6 @@ public class GLSM_SplashCachingCoverage_UnitTest {
|
||||
GLStateManager.glCullFace(GL11.GL_BACK);
|
||||
}
|
||||
|
||||
static Stream<Arguments> stateSetDuringSplash() {
|
||||
return Stream.of(
|
||||
Arguments.of("depth func", (Runnable) () -> GLStateManager.glDepthFunc(GL11.GL_LEQUAL), GL11.GL_LEQUAL,
|
||||
(IntSupplier) () -> GLStateManager.getDepthState().getFunc()),
|
||||
Arguments.of("cull face", (Runnable) () -> GLStateManager.glCullFace(GL11.GL_FRONT), GL11.GL_FRONT,
|
||||
(IntSupplier) () -> GLStateManager.getPolygonState().getCullFaceMode()));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("stateSetDuringSplash")
|
||||
void stateSetDuringSplashLandsInCache(String name, Runnable set, int expected, IntSupplier cached) {
|
||||
set.run();
|
||||
|
||||
assertEquals(expected, cached.getAsInt(), "cache write dropped while caching=false");
|
||||
}
|
||||
|
||||
@Test
|
||||
void popAttribDoesNotRevertDepthFuncSetDuringSplash() throws IllegalAccessException {
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
@@ -65,6 +44,7 @@ public class GLSM_SplashCachingCoverage_UnitTest {
|
||||
GLStateManager.glPopAttrib();
|
||||
|
||||
assertEquals(GL11.GL_LEQUAL, GLStateManager.getDepthState().getFunc(), "popAttrib reverted to the stale default");
|
||||
assertEquals(GL11.GL_LEQUAL, GL11.glGetInteger(GL11.GL_DEPTH_FUNC));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -99,6 +79,8 @@ public class GLSM_SplashCachingCoverage_UnitTest {
|
||||
void replayPushesCachedStateWithoutDisturbingIt() {
|
||||
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
|
||||
GLStateManager.glCullFace(GL11.GL_FRONT);
|
||||
assertEquals(GL11.GL_LEQUAL, GLStateManager.getDepthState().getFunc(), "cache write dropped while caching=false");
|
||||
assertEquals(GL11.GL_FRONT, GLStateManager.getPolygonState().getCullFaceMode(), "cache write dropped while caching=false");
|
||||
|
||||
GLStateManager.replayStateToBackend();
|
||||
|
||||
|
||||
+41
@@ -13,6 +13,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@GLCompatTest
|
||||
public class GLSM_StateQueryRoundTrip_UnitTest {
|
||||
|
||||
private static IntBuffer driverInts(int pname) {
|
||||
final IntBuffer buf = BufferUtils.createIntBuffer(16);
|
||||
GL11.glGetInteger(pname, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
@Test
|
||||
void activeTextureRoundTrips() {
|
||||
try {
|
||||
@@ -31,9 +37,11 @@ public class GLSM_StateQueryRoundTrip_UnitTest {
|
||||
|
||||
GLStateManager.glActiveTexture(0);
|
||||
assertEquals(GL13.GL_TEXTURE2, GLStateManager.glGetInteger(GL13.GL_ACTIVE_TEXTURE));
|
||||
assertEquals(GL13.GL_TEXTURE2, GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE));
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + GLStateManager.MAX_TEXTURE_UNITS);
|
||||
assertEquals(GL13.GL_TEXTURE2, GLStateManager.glGetInteger(GL13.GL_ACTIVE_TEXTURE));
|
||||
assertEquals(GL13.GL_TEXTURE2, GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE));
|
||||
} finally {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
}
|
||||
@@ -44,8 +52,10 @@ public class GLSM_StateQueryRoundTrip_UnitTest {
|
||||
try {
|
||||
GLStateManager.glDepthMask(false);
|
||||
assertEquals(GL11.GL_FALSE, GLStateManager.glGetInteger(GL11.GL_DEPTH_WRITEMASK));
|
||||
assertEquals(GL11.GL_FALSE, GL11.glGetInteger(GL11.GL_DEPTH_WRITEMASK));
|
||||
GLStateManager.glDepthMask(true);
|
||||
assertEquals(GL11.GL_TRUE, GLStateManager.glGetInteger(GL11.GL_DEPTH_WRITEMASK));
|
||||
assertEquals(GL11.GL_TRUE, GL11.glGetInteger(GL11.GL_DEPTH_WRITEMASK));
|
||||
} finally {
|
||||
GLStateManager.glDepthMask(true);
|
||||
}
|
||||
@@ -72,6 +82,10 @@ public class GLSM_StateQueryRoundTrip_UnitTest {
|
||||
GLStateManager.glGetInteger(GL11.GL_POLYGON_MODE, params);
|
||||
assertEquals(GL11.GL_LINE, params.get(0), "front mode");
|
||||
assertEquals(GL11.GL_LINE, params.get(1), "back mode");
|
||||
|
||||
final IntBuffer driver = driverInts(GL11.GL_POLYGON_MODE);
|
||||
assertEquals(GL11.GL_LINE, driver.get(0), "driver front mode");
|
||||
assertEquals(GL11.GL_LINE, driver.get(1), "driver back mode");
|
||||
} finally {
|
||||
GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
|
||||
}
|
||||
@@ -89,6 +103,11 @@ public class GLSM_StateQueryRoundTrip_UnitTest {
|
||||
assertEquals(2, GLStateManager.glGetInteger(GL11.GL_UNPACK_SKIP_PIXELS));
|
||||
assertEquals(3, GLStateManager.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS));
|
||||
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
|
||||
|
||||
assertEquals(64, GL11.glGetInteger(GL11.GL_UNPACK_ROW_LENGTH));
|
||||
assertEquals(2, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_PIXELS));
|
||||
assertEquals(3, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS));
|
||||
assertEquals(1, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
|
||||
} finally {
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0);
|
||||
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0);
|
||||
@@ -107,6 +126,12 @@ public class GLSM_StateQueryRoundTrip_UnitTest {
|
||||
assertEquals(5, params[1]);
|
||||
assertEquals(17, params[2]);
|
||||
assertEquals(23, params[3]);
|
||||
|
||||
final IntBuffer driver = driverInts(GL11.GL_VIEWPORT);
|
||||
assertEquals(3, driver.get(0));
|
||||
assertEquals(5, driver.get(1));
|
||||
assertEquals(17, driver.get(2));
|
||||
assertEquals(23, driver.get(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -118,6 +143,12 @@ public class GLSM_StateQueryRoundTrip_UnitTest {
|
||||
final int unpackAlignment = GLStateManager.glGetInteger(GL11.GL_UNPACK_ALIGNMENT);
|
||||
final int[] viewport = new int[4];
|
||||
GLStateManager.glGetInteger(GL11.GL_VIEWPORT, viewport);
|
||||
final int driverActiveTexture = GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE);
|
||||
final int driverDepthWriteMask = GL11.glGetInteger(GL11.GL_DEPTH_WRITEMASK);
|
||||
final int driverCullMode = GL11.glGetInteger(GL11.GL_CULL_FACE_MODE);
|
||||
final int driverPolygonMode = GL11.glGetInteger(GL11.GL_POLYGON_MODE);
|
||||
final int driverUnpackAlignment = GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT);
|
||||
final IntBuffer driverViewport = driverInts(GL11.GL_VIEWPORT);
|
||||
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + 4);
|
||||
GLStateManager.glDepthMask(false);
|
||||
@@ -144,6 +175,16 @@ public class GLSM_StateQueryRoundTrip_UnitTest {
|
||||
assertEquals(viewport[1], restored[1]);
|
||||
assertEquals(viewport[2], restored[2]);
|
||||
assertEquals(viewport[3], restored[3]);
|
||||
|
||||
assertEquals(driverActiveTexture, GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE));
|
||||
assertEquals(driverDepthWriteMask, GL11.glGetInteger(GL11.GL_DEPTH_WRITEMASK));
|
||||
assertEquals(driverCullMode, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE));
|
||||
assertEquals(driverPolygonMode, GL11.glGetInteger(GL11.GL_POLYGON_MODE));
|
||||
assertEquals(driverUnpackAlignment, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
|
||||
final IntBuffer driverRestored = driverInts(GL11.GL_VIEWPORT);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
assertEquals(driverViewport.get(i), driverRestored.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -464,6 +464,7 @@ public class GLSM_StateSet_GLTest {
|
||||
GLStateManager.popStateTo(d);
|
||||
assertEquals(d, GLStateManager.getAttribDepth());
|
||||
assertEquals(GL11.GL_ONE, GLStateManager.getBlendState().getSrcRgb(), "popStateTo repairs the unbalanced non-member push and carries the change through to the BLEND member pop, restoring the pre-bracket baseline");
|
||||
assertEquals(GL11.GL_ONE, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -92,6 +92,7 @@ public class GLSM_TextureInfoCache_UnitTest {
|
||||
int first = GLStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_INTERNAL_FORMAT);
|
||||
assertFalse(info.needsInternalFormatResolve());
|
||||
int second = GLStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_INTERNAL_FORMAT);
|
||||
assertEquals(GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_INTERNAL_FORMAT), second);
|
||||
assertEquals(first, second);
|
||||
} finally {
|
||||
GL11.glDeleteTextures(texId);
|
||||
|
||||
+1
-10
@@ -37,16 +37,6 @@ public class GLSM_TextureUploadGeneration_GLTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void anUploadLeavesANonZeroGeneration() {
|
||||
final int texId = allocate(4);
|
||||
try {
|
||||
assertNotEquals(0, TextureInfoCache.INSTANCE.getInfo(texId).getUploadGeneration());
|
||||
} finally {
|
||||
GLStateManager.glDeleteTextures(texId);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void recreatingATextureAtAReusedIdYieldsAFreshInfo() {
|
||||
final int texId = allocate(4);
|
||||
@@ -72,6 +62,7 @@ public class GLSM_TextureUploadGeneration_GLTest {
|
||||
try {
|
||||
final TextureInfo info = TextureInfoCache.INSTANCE.getInfo(texId);
|
||||
final int afterAlloc = info.getUploadGeneration();
|
||||
assertNotEquals(0, afterAlloc);
|
||||
|
||||
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, BufferUtils.createByteBuffer(2 * 2 * 4));
|
||||
assertNotEquals(afterAlloc, info.getUploadGeneration(), "content changed, so anything caching the pixels is stale");
|
||||
|
||||
@@ -6,11 +6,15 @@ import com.gtnewhorizons.angelica.util.GLSMUtil;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
|
||||
|
||||
@GLCompatTest
|
||||
@@ -18,7 +22,7 @@ public class GLSM_VAO_UnitTest {
|
||||
|
||||
@AfterEach
|
||||
void resetClientArrayState() {
|
||||
glBindVertexArray(0);
|
||||
UniversalVAO.bindVertexArray(0);
|
||||
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLSMUtil.setClientState(GL11.GL_NORMAL_ARRAY, false);
|
||||
}
|
||||
@@ -66,7 +70,7 @@ public class GLSM_VAO_UnitTest {
|
||||
int vao = UniversalVAO.genVertexArrays();
|
||||
Assertions.assertTrue(vao > 0, "GL_VERTEX_ARRAY_BINDING - VAO Created");
|
||||
|
||||
glBindVertexArray(vao);
|
||||
UniversalVAO.bindVertexArray(vao);
|
||||
verifyState(GL30.GL_VERTEX_ARRAY_BINDING, vao, "GL_VERTEX_ARRAY_BINDING - Bound");
|
||||
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
@@ -91,7 +95,7 @@ public class GLSM_VAO_UnitTest {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
// Check if everything got reset to default
|
||||
glBindVertexArray(0);
|
||||
UniversalVAO.bindVertexArray(0);
|
||||
verifyState(GL30.GL_VERTEX_ARRAY_BINDING, GLStateManager.ctx().defaultVAO, "GL_VERTEX_ARRAY_BINDING - Unbound");
|
||||
verifyState(GL15.GL_ARRAY_BUFFER_BINDING, 0, "GL_ARRAY_BUFFER_BINDING - Unbound");
|
||||
|
||||
@@ -103,18 +107,18 @@ public class GLSM_VAO_UnitTest {
|
||||
verifyState(GL11.GL_NORMAL_ARRAY, true, "GL_NORMAL_ARRAY - Reset");
|
||||
|
||||
// Check if the VAO states are still valid after re-binding it
|
||||
glBindVertexArray(vao);
|
||||
UniversalVAO.bindVertexArray(vao);
|
||||
|
||||
verifyState(GL11.GL_VERTEX_ARRAY, vaoVertexEnabled, "GL_VERTEX_ARRAY - Preserved");
|
||||
verifyState(GL11.GL_VERTEX_ARRAY_SIZE, vaoVertexSize, "GL_VERTEX_ARRAY_SIZE - Preserved");
|
||||
verifyState(GL11.GL_VERTEX_ARRAY_TYPE, vaoVertexType, "GL_VERTEX_ARRAY_TYPE - Preserved");
|
||||
verifyState(GL11.GL_VERTEX_ARRAY_STRIDE, vaoVertexStride, "GL_VERTEX_ARRAY_STRIDE - Preserved");
|
||||
|
||||
glBindVertexArray(0);
|
||||
UniversalVAO.bindVertexArray(0);
|
||||
|
||||
GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
UniversalVAO.bindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
GLStateManager.glPopAttrib();
|
||||
@@ -123,7 +127,7 @@ public class GLSM_VAO_UnitTest {
|
||||
verifyState(GL15.GL_ARRAY_BUFFER_BINDING, vbo, "GL_ARRAY_BUFFER_BINDING - Not affected by glPushAttrib");
|
||||
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
UniversalVAO.bindVertexArray(0);
|
||||
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
@@ -131,64 +135,67 @@ public class GLSM_VAO_UnitTest {
|
||||
|
||||
@Test
|
||||
void testGenericAttributeSupersedesFfpConventional() {
|
||||
final int vao = UniversalVAO.genVertexArrays();
|
||||
glBindVertexArray(vao);
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
withVaoAndVbo(() -> {
|
||||
GLStateManager.glVertexAttribPointer(0, 1, GL11.GL_FLOAT, false, 0, 0L);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);
|
||||
|
||||
GLStateManager.glVertexAttribPointer(0, 1, GL11.GL_FLOAT, false, 0, 0L);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);
|
||||
Assertions.assertEquals(1, VAOManager.get(0).size, "enabled generic attribute must supersede the FFP conventional array at the same location");
|
||||
Assertions.assertEquals(1, driverAttrib0Size());
|
||||
Assertions.assertTrue(VAOManager.get(0).genericPointer, "location must remain generic-sourced");
|
||||
|
||||
Assertions.assertEquals(1, VAOManager.get(0).size, "enabled generic attribute must supersede the FFP conventional array at the same location");
|
||||
Assertions.assertTrue(VAOManager.get(0).genericPointer, "location must remain generic-sourced");
|
||||
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenericAttributeWinsRegardlessOfOrder() {
|
||||
final int vao = UniversalVAO.genVertexArrays();
|
||||
glBindVertexArray(vao);
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
withVaoAndVbo(() -> {
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);
|
||||
GLStateManager.glVertexAttribPointer(0, 1, GL11.GL_FLOAT, false, 0, 0L);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);
|
||||
GLStateManager.glVertexAttribPointer(0, 1, GL11.GL_FLOAT, false, 0, 0L);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
Assertions.assertEquals(1, VAOManager.get(0).size, "generic attribute set after the FFP conventional array must still win");
|
||||
Assertions.assertEquals(1, driverAttrib0Size());
|
||||
Assertions.assertTrue(VAOManager.get(0).genericPointer, "location must remain generic-sourced");
|
||||
|
||||
Assertions.assertEquals(1, VAOManager.get(0).size, "generic attribute set after the FFP conventional array must still win");
|
||||
Assertions.assertTrue(VAOManager.get(0).genericPointer, "location must remain generic-sourced");
|
||||
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFfpConventionalAppliesWithoutGenericAttribute() {
|
||||
withVaoAndVbo(() -> {
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
|
||||
Assertions.assertEquals(3, VAOManager.get(0).size, "plain FFP conventional array must apply when no generic attribute occupies the location");
|
||||
Assertions.assertEquals(3, driverAttrib0Size());
|
||||
Assertions.assertFalse(VAOManager.get(0).genericPointer, "location must be conventional-sourced");
|
||||
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
});
|
||||
}
|
||||
|
||||
private static int driverAttrib0Size() {
|
||||
final IntBuffer out = BufferUtils.createIntBuffer(16);
|
||||
GL20.glGetVertexAttrib(0, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE, out);
|
||||
return out.get(0);
|
||||
}
|
||||
|
||||
private static void withVaoAndVbo(Runnable body) {
|
||||
final int vao = UniversalVAO.genVertexArrays();
|
||||
glBindVertexArray(vao);
|
||||
UniversalVAO.bindVertexArray(vao);
|
||||
final int vbo = GL15.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
|
||||
|
||||
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);
|
||||
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
|
||||
Assertions.assertEquals(3, VAOManager.get(0).size, "plain FFP conventional array must apply when no generic attribute occupies the location");
|
||||
Assertions.assertFalse(VAOManager.get(0).genericPointer, "location must be conventional-sourced");
|
||||
|
||||
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
try {
|
||||
body.run();
|
||||
} finally {
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
UniversalVAO.bindVertexArray(0);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testClientStates() {
|
||||
@@ -198,12 +205,4 @@ public class GLSM_VAO_UnitTest {
|
||||
verifyState(GL11.GL_COLOR_ARRAY, false, "GL_COLOR_ARRAY Default State");
|
||||
verifyState(GL11.GL_INDEX_ARRAY, false, "GL_INDEX_ARRAY Default State");
|
||||
}
|
||||
|
||||
private static void glBindVertexArray(int array) {
|
||||
final GLContextState glCtx = GLStateManager.ctx();
|
||||
if (array == 0) array = glCtx.defaultVAO;
|
||||
glCtx.boundVAO = array;
|
||||
glCtx.vaos.onBindVertexArrayPre(array);
|
||||
UniversalVAO.bindVertexArray(array);
|
||||
}
|
||||
}
|
||||
+5
@@ -4,7 +4,9 @@ import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyIsEnabled;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -23,6 +25,7 @@ public class GLSM_WorkerContextStacks_GLTest {
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_CULL_FACE), "cull face restored on the worker context");
|
||||
verifyIsEnabled(GL11.GL_CULL_FACE, false, "driver cull face restored on the worker context");
|
||||
} finally {
|
||||
GLStateManager.glDisable(GL11.GL_CULL_FACE);
|
||||
GLStateManager.exitWorkerContext();
|
||||
@@ -42,7 +45,9 @@ public class GLSM_WorkerContextStacks_GLTest {
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_BLEND), "blend enable restored on the worker context");
|
||||
verifyIsEnabled(GL11.GL_BLEND, false, "driver blend enable restored on the worker context");
|
||||
assertEquals(GL11.GL_ONE, GLStateManager.getBlendState().getSrcRgb(), "blend src rgb restored on the worker context");
|
||||
assertEquals(GL11.GL_ONE, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb restored on the worker context");
|
||||
} finally {
|
||||
GLStateManager.disableBlend();
|
||||
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
@@ -12,7 +14,6 @@ class ReservedWordRenameTest {
|
||||
|
||||
private static final List<Pattern> LEGACY_ALWAYS = List.of(Pattern.compile("\\bsample\\b"), Pattern.compile("\\bnew\\b"));
|
||||
private static final Pattern LEGACY_400 = Pattern.compile("\\bsampler\\b(?!\\d)");
|
||||
private static final String[] WORDS = { "sample", "sampler", "new" };
|
||||
|
||||
private static String legacyRename(String source, int targetVersion) {
|
||||
String out = source;
|
||||
@@ -30,17 +31,17 @@ class ReservedWordRenameTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void edgeCasesMatchLegacy() {
|
||||
final String[] cases = {
|
||||
"", "sample", "new", "sampler", " sample ", "sample;", "(sample)", "_sample", "sample_", "sample2", "2sample",
|
||||
"samples", "resample", "sampler2D", "samplerX", "sampler_", "sampler;", "uniform sampler s;", "sampler2DShadow",
|
||||
"new_", "renew", "news", "new;new new", "sample sample sample", "samplesampler", "sampler sample new",
|
||||
"ésample", "sampleé", "ßnewß", "中sampler", "sampler中", "samplé",
|
||||
"// sample in a comment\nfloat sample = 1.0;\n", "#define new sample\n", "vec4 c = texture(sampler, uv) * sample;",
|
||||
"float x = sample.x + new_value + newValue + sample1;", "sample\nnew\nsampler\n", "\tsample\t", "sample-new+sampler*",
|
||||
};
|
||||
for (String c : cases) assertSameAsLegacy(c);
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"", "sample", "new", "sampler", " sample ", "sample;", "(sample)", "_sample", "sample_", "sample2", "2sample",
|
||||
"samples", "resample", "sampler2D", "samplerX", "sampler_", "sampler;", "uniform sampler s;", "sampler2DShadow",
|
||||
"new_", "renew", "news", "new;new new", "sample sample sample", "samplesampler", "sampler sample new",
|
||||
"\u00e9sample", "sample\u00e9", "\u00dfnew\u00df", "\u4e2dsampler", "sampler\u4e2d", "sampl\u00e9",
|
||||
"// sample in a comment\nfloat sample = 1.0;\n", "#define new sample\n", "vec4 c = texture(sampler, uv) * sample;",
|
||||
"float x = sample.x + new_value + newValue + sample1;", "sample\nnew\nsampler\n", "\tsample\t", "sample-new+sampler*",
|
||||
})
|
||||
void edgeCasesMatchLegacy(String source) {
|
||||
assertSameAsLegacy(source);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -55,10 +56,9 @@ class ReservedWordRenameTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void literalWordsAreTheOnlyCandidates() {
|
||||
for (String w : WORDS) {
|
||||
assertEquals("angelica_renamed_" + w, GlslTransformUtils.renameReservedWords(w, 460));
|
||||
}
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "sample", "sampler", "new" })
|
||||
void literalWordsAreTheOnlyCandidates(String word) {
|
||||
assertEquals("angelica_renamed_" + word, GlslTransformUtils.renameReservedWords(word, 460));
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,47 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.taumc.glsl.grammar.GLSLParser;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ReservedWordRestoreTest {
|
||||
|
||||
private static void assertTreeRestoreMatchesStringRestore(String label, String renamedSource) {
|
||||
static Stream<Arguments> restores() {
|
||||
return Stream.of(
|
||||
Arguments.of("all three renames restore identically",
|
||||
"float angelica_renamed_sample = 1.0;\n"
|
||||
+ "uniform sampler2D angelica_renamed_sampler;\n"
|
||||
+ "vec4 angelica_renamed_texture = vec4(0.0);\n"
|
||||
+ "void main() {\n"
|
||||
+ " vec4 c = angelica_renamed_texture + vec4(angelica_renamed_sample);\n"
|
||||
+ "}\n",
|
||||
new String[0], new String[0]),
|
||||
Arguments.of("new is reserved in every GLSL version and must stay renamed",
|
||||
"float angelica_renamed_new = 2.0;\n"
|
||||
+ "void main() { float x = angelica_renamed_new; }\n",
|
||||
new String[] { "angelica_renamed_new" }, new String[0]),
|
||||
Arguments.of("suffixed rename must strip only the prefix",
|
||||
"uniform sampler2D tex;\n"
|
||||
+ "void main() {\n"
|
||||
+ " vec4 c = angelica_renamed_texture2D(tex, vec2(0.5));\n"
|
||||
+ "}\n",
|
||||
new String[] { "texture2D" }, new String[] { "angelica_renamed_" }),
|
||||
Arguments.of("untouched source prints identically",
|
||||
"layout(location = 0) out vec4 fragColor;\n"
|
||||
+ "void main() { fragColor = vec4(1.0); }\n",
|
||||
new String[0], new String[0]));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource
|
||||
void restores(String label, String renamedSource, String[] present, String[] absent) {
|
||||
final GLSLParser.Translation_unitContext treeA = GlslTransformUtils.parseFullQuiet(renamedSource);
|
||||
final String legacy = GlslTransformUtils.restoreReservedWords(GlslTransformUtils.getFormattedShader(treeA, ""));
|
||||
|
||||
@@ -17,51 +49,8 @@ class ReservedWordRestoreTest {
|
||||
GlslTransformUtils.restoreReservedWordsInTree(treeB);
|
||||
final String inTree = GlslTransformUtils.getFormattedShader(treeB, "");
|
||||
|
||||
assertEquals(legacy, inTree, "in-tree restore diverged from string restore for " + label);
|
||||
}
|
||||
|
||||
@Test
|
||||
void allThreeRenamesRestoreIdentically() {
|
||||
final String src = "float angelica_renamed_sample = 1.0;\n"
|
||||
+ "uniform sampler2D angelica_renamed_sampler;\n"
|
||||
+ "vec4 angelica_renamed_texture = vec4(0.0);\n"
|
||||
+ "void main() {\n"
|
||||
+ " vec4 c = angelica_renamed_texture + vec4(angelica_renamed_sample);\n"
|
||||
+ "}\n";
|
||||
assertTreeRestoreMatchesStringRestore("all-three", src);
|
||||
}
|
||||
|
||||
@Test
|
||||
void renamedNewStaysRenamed() {
|
||||
final String src = "float angelica_renamed_new = 2.0;\n"
|
||||
+ "void main() { float x = angelica_renamed_new; }\n";
|
||||
assertTreeRestoreMatchesStringRestore("new-identifier", src);
|
||||
|
||||
final GLSLParser.Translation_unitContext tree = GlslTransformUtils.parseFullQuiet(src);
|
||||
GlslTransformUtils.restoreReservedWordsInTree(tree);
|
||||
final String out = GlslTransformUtils.getFormattedShader(tree, "");
|
||||
assertTrue(out.contains("angelica_renamed_new"), "new is reserved in every GLSL version and must stay renamed");
|
||||
}
|
||||
|
||||
@Test
|
||||
void renamedTextureFunctionCallRestores() {
|
||||
final String src = "uniform sampler2D tex;\n"
|
||||
+ "void main() {\n"
|
||||
+ " vec4 c = angelica_renamed_texture2D(tex, vec2(0.5));\n"
|
||||
+ "}\n";
|
||||
assertTreeRestoreMatchesStringRestore("texture2D-call", src);
|
||||
|
||||
final GLSLParser.Translation_unitContext tree = GlslTransformUtils.parseFullQuiet(src);
|
||||
GlslTransformUtils.restoreReservedWordsInTree(tree);
|
||||
final String out = GlslTransformUtils.getFormattedShader(tree, "");
|
||||
assertTrue(out.contains("texture2D"), "suffixed rename must strip only the prefix");
|
||||
assertFalse(out.contains("angelica_renamed_"), "no renamed remnants expected");
|
||||
}
|
||||
|
||||
@Test
|
||||
void untouchedSourcePrintsIdentically() {
|
||||
final String src = "layout(location = 0) out vec4 fragColor;\n"
|
||||
+ "void main() { fragColor = vec4(1.0); }\n";
|
||||
assertTreeRestoreMatchesStringRestore("no-renames", src);
|
||||
assertEquals(legacy, inTree, "in-tree restore diverged from string restore");
|
||||
for (String needle : present) assertTrue(inTree.contains(needle), needle);
|
||||
for (String needle : absent) assertFalse(inTree.contains(needle), needle);
|
||||
}
|
||||
}
|
||||
+13
-27
@@ -25,16 +25,19 @@ class VertexAttribDefaultBridgeTest {
|
||||
return new float[] { buf.get(0), buf.get(1), buf.get(2), buf.get(3) };
|
||||
}
|
||||
|
||||
private void assertAttrib(int index, String label, float... expected) {
|
||||
final float[] actual = getVertexAttrib4f(index);
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
assertEquals(expected[i], actual[i], EPSILON, label + "[" + i + "]");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGlColor4fSetsVertexAttrib() {
|
||||
GLStateManager.glColor4f(0.5f, 0.25f, 0.75f, 0.8f);
|
||||
GLStateManager.flushDeferredVertexAttribs(GLStateManager.ctx(), false, false, false, false);
|
||||
|
||||
float[] color = getVertexAttrib4f(COLOR_ATTRIB);
|
||||
assertEquals(0.5f, color[0], EPSILON, "color.r");
|
||||
assertEquals(0.25f, color[1], EPSILON, "color.g");
|
||||
assertEquals(0.75f, color[2], EPSILON, "color.b");
|
||||
assertEquals(0.8f, color[3], EPSILON, "color.a");
|
||||
assertAttrib(COLOR_ATTRIB, "color", 0.5f, 0.25f, 0.75f, 0.8f);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -42,10 +45,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
GLStateManager.glNormal3f(0.0f, 1.0f, 0.0f);
|
||||
GLStateManager.flushDeferredVertexAttribs(GLStateManager.ctx(), false, false, false, false);
|
||||
|
||||
float[] normal = getVertexAttrib4f(NORMAL_ATTRIB);
|
||||
assertEquals(0.0f, normal[0], EPSILON, "normal.x");
|
||||
assertEquals(1.0f, normal[1], EPSILON, "normal.y");
|
||||
assertEquals(0.0f, normal[2], EPSILON, "normal.z");
|
||||
assertAttrib(NORMAL_ATTRIB, "normal", 0.0f, 1.0f, 0.0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -53,11 +53,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
GLStateManager.setLightmapTextureCoords(GL13.GL_TEXTURE1, 240.0f, 128.0f);
|
||||
GLStateManager.flushDeferredVertexAttribs(GLStateManager.ctx(), false, false, false, false);
|
||||
|
||||
float[] lm = getVertexAttrib4f(SECONDARY_UV_ATTRIB);
|
||||
assertEquals(240.0f, lm[0], EPSILON, "lightmap.s");
|
||||
assertEquals(128.0f, lm[1], EPSILON, "lightmap.t");
|
||||
assertEquals(0.0f, lm[2], EPSILON, "lightmap.r");
|
||||
assertEquals(1.0f, lm[3], EPSILON, "lightmap.q");
|
||||
assertAttrib(SECONDARY_UV_ATTRIB, "lightmap", 240.0f, 128.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -66,9 +62,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
GLStateManager.glTexCoord2f(0.5f, 0.75f);
|
||||
GLStateManager.flushDeferredVertexAttribs(GLStateManager.ctx(), false, false, false, false);
|
||||
|
||||
float[] uv = getVertexAttrib4f(PRIMARY_UV_ATTRIB);
|
||||
assertEquals(0.5f, uv[0], EPSILON, "texcoord.s");
|
||||
assertEquals(0.75f, uv[1], EPSILON, "texcoord.t");
|
||||
assertAttrib(PRIMARY_UV_ATTRIB, "texcoord", 0.5f, 0.75f);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -79,11 +73,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
GLStateManager.glColor4f(0.3f, 0.6f, 0.9f, 1.0f);
|
||||
GLStateManager.flushDeferredVertexAttribs(GLStateManager.ctx(), false, false, false, false);
|
||||
|
||||
float[] color = getVertexAttrib4f(COLOR_ATTRIB);
|
||||
assertEquals(0.3f, color[0], EPSILON, "color.r after dedup");
|
||||
assertEquals(0.6f, color[1], EPSILON, "color.g after dedup");
|
||||
assertEquals(0.9f, color[2], EPSILON, "color.b after dedup");
|
||||
assertEquals(1.0f, color[3], EPSILON, "color.a after dedup");
|
||||
assertAttrib(COLOR_ATTRIB, "color after dedup", 0.3f, 0.6f, 0.9f, 1.0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -97,11 +87,7 @@ class VertexAttribDefaultBridgeTest {
|
||||
// Color during COMPILE should NOT reach changeColor (early return)
|
||||
GLStateManager.glColor4f(0.9f, 0.8f, 0.7f, 0.6f);
|
||||
|
||||
float[] color = getVertexAttrib4f(COLOR_ATTRIB);
|
||||
assertEquals(0.1f, color[0], EPSILON, "color.r unchanged during COMPILE");
|
||||
assertEquals(0.2f, color[1], EPSILON, "color.g unchanged during COMPILE");
|
||||
assertEquals(0.3f, color[2], EPSILON, "color.b unchanged during COMPILE");
|
||||
assertEquals(1.0f, color[3], EPSILON, "color.a unchanged during COMPILE");
|
||||
assertAttrib(COLOR_ATTRIB, "color unchanged during COMPILE", 0.1f, 0.2f, 0.3f, 1.0f);
|
||||
|
||||
GLStateManager.glEndList();
|
||||
GLStateManager.glDeleteLists(listId, 1);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.gtnewhorizons.angelica.glsm.backend;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -17,60 +17,38 @@ class BackendPolicyTest {
|
||||
assertFalse(VSyncMode.OFF.tearFree());
|
||||
}
|
||||
|
||||
private static final Predicate<VSyncMode> NO_MAILBOX = mode -> mode != VSyncMode.MAILBOX;
|
||||
private static final Predicate<VSyncMode> EVERYTHING = mode -> true;
|
||||
|
||||
@Test
|
||||
void mailboxIsTakenWhenTheBackendOffersIt() {
|
||||
assertEquals(VSyncMode.MAILBOX, RenderBackend.resolveTearFreeMode(null, EVERYTHING));
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@CsvSource({
|
||||
"vsync is the default even when the backend offers mailbox, , true, ON",
|
||||
"vsync is the default when mailbox is unsupported, , false, ON",
|
||||
"an unsupported mailbox preference falls back to vsync, MAILBOX, false, ON",
|
||||
"an explicit supported vsync preference wins, ON, true, ON",
|
||||
"an explicit supported mailbox preference wins, MAILBOX, true, MAILBOX",
|
||||
"off is never a tear-free preference with mailbox, OFF, true, ON",
|
||||
"off is never a tear-free preference without mailbox, OFF, false, ON",
|
||||
})
|
||||
void tearFreeModeResolution(String label, VSyncMode preference, boolean mailboxSupported, VSyncMode expected) {
|
||||
assertEquals(expected, RenderBackend.resolveTearFreeMode(preference, mode -> mailboxSupported || mode != VSyncMode.MAILBOX));
|
||||
}
|
||||
|
||||
@Test
|
||||
void vsyncIsTheFallbackWhenMailboxIsUnsupported() {
|
||||
assertEquals(VSyncMode.ON, RenderBackend.resolveTearFreeMode(null, NO_MAILBOX));
|
||||
assertEquals(VSyncMode.ON, RenderBackend.resolveTearFreeMode(VSyncMode.MAILBOX, NO_MAILBOX));
|
||||
@ParameterizedTest(name = "{0}/{1} or {2} Hz -> {3} ns")
|
||||
@CsvSource({
|
||||
"60000, 1001, 0.0, 16683333",
|
||||
"144, 1, 0.0, 6944444",
|
||||
"0, 0, 59.94, 16683350",
|
||||
"0, 0, 60.0, 16666667",
|
||||
"0, 0, 0.0, 0",
|
||||
"-1, -1, -1.0, 0",
|
||||
"144, 0, 0.0, 0",
|
||||
})
|
||||
void periodPrefersTheExactRationalThenTheFloatRate(int numerator, int denominator, float hz, long expected) {
|
||||
assertEquals(expected, RenderBackend.periodFromRational(numerator, denominator, hz));
|
||||
}
|
||||
|
||||
@Test
|
||||
void anExplicitSupportedPreferenceWins() {
|
||||
assertEquals(VSyncMode.ON, RenderBackend.resolveTearFreeMode(VSyncMode.ON, EVERYTHING));
|
||||
assertEquals(VSyncMode.MAILBOX, RenderBackend.resolveTearFreeMode(VSyncMode.MAILBOX, EVERYTHING));
|
||||
}
|
||||
|
||||
@Test
|
||||
void offIsNeverATearFreePreference() {
|
||||
assertEquals(VSyncMode.MAILBOX, RenderBackend.resolveTearFreeMode(VSyncMode.OFF, EVERYTHING));
|
||||
assertEquals(VSyncMode.ON, RenderBackend.resolveTearFreeMode(VSyncMode.OFF, NO_MAILBOX));
|
||||
}
|
||||
|
||||
@Test
|
||||
void periodPrefersTheExactRational() {
|
||||
assertEquals(16_683_333L, RenderBackend.periodFromRational(60000, 1001, 0.0f));
|
||||
assertEquals(6_944_444L, RenderBackend.periodFromRational(144, 1, 0.0f));
|
||||
}
|
||||
|
||||
@Test
|
||||
void periodFallsBackToTheFloatRate() {
|
||||
assertEquals(16_683_350L, RenderBackend.periodFromRational(0, 0, 59.94f));
|
||||
assertEquals(16_666_667L, RenderBackend.periodFromRational(0, 0, 60.0f));
|
||||
}
|
||||
|
||||
@Test
|
||||
void periodIsZeroWhenUnknown() {
|
||||
assertEquals(0L, RenderBackend.periodFromRational(0, 0, 0.0f));
|
||||
assertEquals(0L, RenderBackend.periodFromRational(-1, -1, -1.0f));
|
||||
assertEquals(0L, RenderBackend.periodFromRational(144, 0, 0.0f));
|
||||
}
|
||||
|
||||
@Test
|
||||
void integerNtscRatesAreAdjusted() {
|
||||
assertEquals(16_683_333L, RenderBackend.periodFromIntegerHz(59));
|
||||
assertEquals(6_951_389L, RenderBackend.periodFromIntegerHz(143));
|
||||
assertEquals(16_666_667L, RenderBackend.periodFromIntegerHz(60));
|
||||
assertEquals(6_944_444L, RenderBackend.periodFromIntegerHz(144));
|
||||
assertEquals(0L, RenderBackend.periodFromIntegerHz(0));
|
||||
assertEquals(0L, RenderBackend.periodFromIntegerHz(-60));
|
||||
assertEquals(60, RenderBackend.hzFromPeriod(RenderBackend.periodFromIntegerHz(59)));
|
||||
@ParameterizedTest(name = "{0} Hz -> {1} ns")
|
||||
@CsvSource({"59, 16683333", "143, 6951389", "60, 16666667", "144, 6944444", "0, 0", "-60, 0"})
|
||||
void integerNtscRatesAreAdjusted(int hz, long expected) {
|
||||
assertEquals(expected, RenderBackend.periodFromIntegerHz(hz));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -79,6 +57,7 @@ class BackendPolicyTest {
|
||||
assertEquals(144, RenderBackend.hzFromPeriod(6_944_444L));
|
||||
assertEquals(0, RenderBackend.hzFromPeriod(0L));
|
||||
assertEquals(0, RenderBackend.hzFromPeriod(-1L));
|
||||
assertEquals(60, RenderBackend.hzFromPeriod(RenderBackend.periodFromIntegerHz(59)));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+38
-39
@@ -4,7 +4,6 @@ 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;
|
||||
@@ -12,54 +11,54 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class BackendStartGuardTest {
|
||||
|
||||
private static File marker(Path dir) {
|
||||
return dir.resolve("angelica").resolve("sdlgpu-starting").toFile();
|
||||
@TempDir
|
||||
Path dir;
|
||||
|
||||
private boolean boot() {
|
||||
return boot(1);
|
||||
}
|
||||
|
||||
private boolean boot(int revision) {
|
||||
return BackendStartGuard.begin(dir.toFile(), true, false, revision, "");
|
||||
}
|
||||
|
||||
private File marker() {
|
||||
return dir.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());
|
||||
void bootsThatReachTheBackendNeverCountAsStrikes() {
|
||||
for (int i = 0; i < BackendStartGuard.STRIKE_LIMIT; i++) {
|
||||
assertFalse(boot());
|
||||
BackendStartGuard.arm();
|
||||
BackendStartGuard.disarm();
|
||||
}
|
||||
assertFalse(boot());
|
||||
}
|
||||
|
||||
@Test
|
||||
void leftoverMarkerTripsOnceAndIsRemoved(@TempDir Path dir) {
|
||||
final File marker = marker(dir);
|
||||
assertFalse(BackendStartGuard.begin(marker, true, false));
|
||||
assertTrue(BackendStartGuard.begin(marker, true, false));
|
||||
void thirdDeathInTheDeviceWindowResets() {
|
||||
for (int i = 0; i < BackendStartGuard.STRIKE_LIMIT; i++) {
|
||||
assertFalse(boot());
|
||||
BackendStartGuard.arm();
|
||||
}
|
||||
assertTrue(boot());
|
||||
assertTrue(BackendStartGuard.tripped());
|
||||
assertFalse(marker.exists());
|
||||
BackendStartGuard.finish();
|
||||
assertFalse(marker.exists());
|
||||
assertFalse(BackendStartGuard.begin(marker, true, false));
|
||||
assertFalse(BackendStartGuard.tripped());
|
||||
BackendStartGuard.finish();
|
||||
assertFalse(marker().exists());
|
||||
}
|
||||
|
||||
@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());
|
||||
}
|
||||
void latchStopsArmingUntilTheRequirementsRevisionChanges() {
|
||||
assertFalse(boot());
|
||||
BackendStartGuard.arm();
|
||||
BackendStartGuard.disarm();
|
||||
BackendStartGuard.latch();
|
||||
assertFalse(boot());
|
||||
BackendStartGuard.arm();
|
||||
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());
|
||||
assertFalse(boot(2));
|
||||
BackendStartGuard.arm();
|
||||
assertTrue(marker().isFile());
|
||||
}
|
||||
}
|
||||
@@ -124,12 +124,7 @@ class FFPCombineShaderTest {
|
||||
|
||||
@Test
|
||||
void testSingleUnitCombineCompiles() {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, GL11.GL_MODULATE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_MODULATE);
|
||||
enableCombineUnit(GL13.GL_TEXTURE0, GL11.GL_MODULATE, GL11.GL_MODULATE);
|
||||
|
||||
FragmentKey fk = FragmentKey.fromState();
|
||||
String fragSource = FragmentShaderGenerator.generate(fk);
|
||||
@@ -181,12 +176,7 @@ class FFPCombineShaderTest {
|
||||
@ParameterizedTest(name = "COMBINE function {0}")
|
||||
@MethodSource("allCombineFunctions")
|
||||
void testAllCombineFunctionsCompile(String name, int glFunc, int numArgs) {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, glFunc);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_REPLACE);
|
||||
enableCombineUnit(GL13.GL_TEXTURE0, glFunc, GL11.GL_REPLACE);
|
||||
|
||||
// Set up appropriate sources
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_SOURCE0_RGB, GL11.GL_TEXTURE);
|
||||
@@ -215,12 +205,7 @@ class FFPCombineShaderTest {
|
||||
|
||||
@Test
|
||||
void testCombineScopeDeclaresColorOutsideBlock() {
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, GL11.GL_MODULATE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_MODULATE);
|
||||
enableCombineUnit(GL13.GL_TEXTURE0, GL11.GL_MODULATE, GL11.GL_MODULATE);
|
||||
|
||||
FragmentKey fk = FragmentKey.fromState();
|
||||
String src = FragmentShaderGenerator.generate(fk);
|
||||
@@ -270,14 +255,18 @@ class FFPCombineShaderTest {
|
||||
* Unit 2: COMBINE, RGB=ADD, SRC0=TEXTURE/SRC_COLOR, SRC1=PREVIOUS/SRC_COLOR
|
||||
* Unit 3: MODULATE (simple)
|
||||
*/
|
||||
private void setupXaeroState() {
|
||||
// Unit 0: COMBINE / SUBTRACT
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
|
||||
private void enableCombineUnit(int unit, int rgbFunc, int alphaFunc) {
|
||||
GLStateManager.glActiveTexture(unit);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, GL13.GL_SUBTRACT);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_REPLACE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, rgbFunc);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, alphaFunc);
|
||||
}
|
||||
|
||||
private void setupXaeroState() {
|
||||
// Unit 0: COMBINE / SUBTRACT
|
||||
enableCombineUnit(GL13.GL_TEXTURE0, GL13.GL_SUBTRACT, GL11.GL_REPLACE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_SOURCE0_RGB, GL11.GL_TEXTURE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_OPERAND0_RGB, GL11.GL_SRC_COLOR);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_SOURCE1_RGB, GL13.GL_PRIMARY_COLOR);
|
||||
@@ -288,12 +277,7 @@ class FFPCombineShaderTest {
|
||||
// Unit 1: disabled (skip — default is disabled)
|
||||
|
||||
// Unit 2: COMBINE / ADD
|
||||
GLStateManager.glActiveTexture(GL13.GL_TEXTURE2);
|
||||
GLStateManager.enableTexture();
|
||||
bindUnitTexture();
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, GL11.GL_ADD);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_ALPHA, GL11.GL_REPLACE);
|
||||
enableCombineUnit(GL13.GL_TEXTURE2, GL11.GL_ADD, GL11.GL_REPLACE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_SOURCE0_RGB, GL11.GL_TEXTURE);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_OPERAND0_RGB, GL11.GL_SRC_COLOR);
|
||||
GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_SOURCE1_RGB, GL13.GL_PREVIOUS);
|
||||
|
||||
@@ -94,6 +94,7 @@ class FfpUniformBlockDedupTest {
|
||||
|
||||
second.upload(GLStateManager.ctx());
|
||||
assertEquals(1, second.blockWrites, "a never-bound instance must write regardless of content");
|
||||
assertEquals(second.getRing().getBufferId(), GL30.glGetInteger(GL31.GL_UNIFORM_BUFFER_BINDING, FFPUniformBlock.BINDING_POINT));
|
||||
} finally {
|
||||
first.destroy();
|
||||
second.destroy();
|
||||
@@ -101,6 +102,24 @@ class FfpUniformBlockDedupTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void unchangedContentStillWritesAfterFrameEnd() {
|
||||
resetState();
|
||||
final Uniforms uniforms = new Uniforms();
|
||||
try {
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
final int firstOffset = boundRangeStart();
|
||||
uniforms.endFrame();
|
||||
|
||||
uniforms.upload(GLStateManager.ctx());
|
||||
assertEquals(1, uniforms.blockWrites, "the frame-end fence owns the old range, so the next frame must not reuse it");
|
||||
assertNotEquals(firstOffset, boundRangeStart());
|
||||
} finally {
|
||||
uniforms.destroy();
|
||||
resetState();
|
||||
}
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
List<DynamicTest> everyStagingCategoryForcesAWrite() {
|
||||
final List<DynamicTest> tests = new ArrayList<>();
|
||||
|
||||
+23
-55
@@ -85,7 +85,7 @@ class InstancedFFPDrawGLTest {
|
||||
GLSMHooks.pendingProgramSelection = null;
|
||||
GLSMHooks.blendHandler = null;
|
||||
log.clear();
|
||||
FfpFixture.IntegerInstances.delete();
|
||||
FfpFixture.EntityIdInstances.delete();
|
||||
FfpFixture.resetFfpState();
|
||||
CubeParityFixture.disableDirectionalLight();
|
||||
CubeParityFixture.deleteResources();
|
||||
@@ -147,9 +147,7 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.glViewport(0, 0, PARITY_SIZE, PARITY_SIZE);
|
||||
CubeParityFixture.setupScene(lightmap, lit);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
FfpFixture.activateFfp();
|
||||
|
||||
final CubeParityFixture.Spec[] specs = CubeParityFixture.specs();
|
||||
|
||||
@@ -172,9 +170,7 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.glViewport(0, 0, PARITY_SIZE, PARITY_SIZE);
|
||||
ParticleParityFixture.setupScene(true);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
FfpFixture.activateFfp();
|
||||
|
||||
final ParticleParityFixture.Particle[] particles = ParticleParityFixture.particles();
|
||||
|
||||
@@ -196,14 +192,14 @@ class InstancedFFPDrawGLTest {
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.disableCull();
|
||||
|
||||
FfpFixture.IntegerInstances.build(0.66f);
|
||||
FfpFixture.EntityIdInstances.build(0.66f);
|
||||
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
FfpFixture.IntegerInstances.draw();
|
||||
FfpFixture.EntityIdInstances.draw();
|
||||
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "instanced ivec4 draw must not raise a GL error");
|
||||
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "instanced entity id draw must not raise a GL error");
|
||||
|
||||
assertPixel(51, 50, 255, 0, 0, "entity -1 must render red");
|
||||
assertPixel(150, 50, 0, 255, 0, "entity 7 must render green");
|
||||
@@ -237,20 +233,7 @@ class InstancedFFPDrawGLTest {
|
||||
|
||||
@Test
|
||||
void twoInstancesDrawWithDistinctTransformsAndColors() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glViewport(0, 0, 800, 600);
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.disableCull();
|
||||
|
||||
buildTemplate();
|
||||
uploadInstances(0xFF0000FF, 0, 0xFF00FF00, 0);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
prepareTwoInstances(0xFF0000FF, 0, 0xFF00FF00, 0);
|
||||
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
@@ -267,20 +250,7 @@ class InstancedFFPDrawGLTest {
|
||||
|
||||
@Test
|
||||
void instancedAlphaTestDiscardsLowAlphaInstance() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glViewport(0, 0, 800, 600);
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.disableCull();
|
||||
|
||||
buildTemplate();
|
||||
uploadInstances(0xFF0000FF, 0, 0x0D0000FF, 0);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
prepareTwoInstances(0xFF0000FF, 0, 0x0D0000FF, 0);
|
||||
GLStateManager.enableAlphaTest();
|
||||
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1f);
|
||||
|
||||
@@ -311,9 +281,7 @@ class InstancedFFPDrawGLTest {
|
||||
buildTemplate();
|
||||
uploadInstances(0xFF0000FF, 0, 0xFF00FF00, 0);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
FfpFixture.activateFfp();
|
||||
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
|
||||
@@ -340,20 +308,7 @@ class InstancedFFPDrawGLTest {
|
||||
|
||||
@Test
|
||||
void perInstanceOverlayMixesOnlyTintedInstances() {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glViewport(0, 0, 800, 600);
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.disableCull();
|
||||
|
||||
buildTemplate();
|
||||
uploadInstances(0xFFFFFFFF, 0, 0xFFFFFFFF, 0xFF0000FF);
|
||||
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
ShaderManager.enable();
|
||||
sm.activate();
|
||||
prepareTwoInstances(0xFFFFFFFF, 0, 0xFFFFFFFF, 0xFF0000FF);
|
||||
|
||||
GLStateManager.glClearColor(0f, 0f, 0f, 1f);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
@@ -370,6 +325,19 @@ class InstancedFFPDrawGLTest {
|
||||
assertTrue(right[0] > 200 && right[1] < 50 && right[2] < 50, "right instance is mixed to its own opaque red overlay, got " + right[0] + "," + right[1] + "," + right[2]);
|
||||
}
|
||||
|
||||
private void prepareTwoInstances(int leftColor, int leftOverlay, int rightColor, int rightOverlay) {
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
|
||||
GLStateManager.glLoadIdentity();
|
||||
GLStateManager.glViewport(0, 0, 800, 600);
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.disableCull();
|
||||
buildTemplate();
|
||||
uploadInstances(leftColor, leftOverlay, rightColor, rightOverlay);
|
||||
FfpFixture.activateFfp();
|
||||
}
|
||||
|
||||
private void uploadInstances(int leftColor, int leftOverlay, int rightColor, int rightOverlay) {
|
||||
final ByteBuffer instances = BufferUtils.createByteBuffer(2 * INSTANCE_STRIDE);
|
||||
putInstance(instances, -0.5f, leftColor, leftOverlay);
|
||||
|
||||
@@ -17,8 +17,7 @@ class LogicOpGLTest {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
FfpFixture.resetFfpState();
|
||||
ShaderManager.enable();
|
||||
ShaderManager.getInstance().activate();
|
||||
FfpFixture.activateFfp();
|
||||
GLStateManager.glViewport(0, 0, SIZE, SIZE);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
|
||||
@@ -3,7 +3,8 @@ package com.gtnewhorizons.angelica.glsm.ffp;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@@ -15,18 +16,13 @@ class UniformsRescaleTest {
|
||||
return Uniforms.rescaleFactor(normalMatrix, new Vector3f());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rescaleFactorUsesTheInverseModelviewBottomRow() {
|
||||
assertEquals(8.0f, rescaleFactorFor(4.0f, 2.0f, 8.0f), 1.0e-5f, "wrong normal matrix column");
|
||||
}
|
||||
|
||||
@Test
|
||||
void uniformScaleIsRowIndependent() {
|
||||
assertEquals(2.0f, rescaleFactorFor(2.0f, 2.0f, 2.0f), 1.0e-5f);
|
||||
}
|
||||
|
||||
@Test
|
||||
void singularMatrixFallsBackToUnitScale() {
|
||||
assertEquals(1.0f, rescaleFactorFor(1.0f, 1.0f, 0.0f), 1.0e-5f, "a degenerate normal matrix must not divide by zero");
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@CsvSource({
|
||||
"wrong normal matrix column, 4, 2, 8, 8",
|
||||
"uniform scale, 2, 2, 2, 2",
|
||||
"a degenerate normal matrix must not divide by zero, 1, 1, 0, 1",
|
||||
})
|
||||
void rescaleFactorUsesTheInverseModelviewBottomRow(String label, float sx, float sy, float sz, float expected) {
|
||||
assertEquals(expected, rescaleFactorFor(sx, sy, sz), 1.0e-5f, label);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class VAOManagerEboIndexTest {
|
||||
@@ -127,64 +126,4 @@ public class VAOManagerEboIndexTest {
|
||||
}
|
||||
assertTrue(sharedEboDeletes > 20, "op stream never freed a multi-owner EBO (hits: " + sharedEboDeletes + ")");
|
||||
}
|
||||
|
||||
@Test
|
||||
void sharedEboClearsEveryOwner() {
|
||||
final int ebo = 77;
|
||||
bindVao(1);
|
||||
bindEbo(ebo);
|
||||
bindVao(2);
|
||||
bindEbo(ebo);
|
||||
bindVao(3);
|
||||
bindEbo(ebo);
|
||||
bindVao(DEFAULT_VAO);
|
||||
|
||||
deleteBuffer(ebo);
|
||||
|
||||
assertEquals(0, vaos.vaoMap.get(1).ebo, "vao 1 kept a freed shared EBO");
|
||||
assertEquals(0, vaos.vaoMap.get(2).ebo, "vao 2 kept a freed shared EBO");
|
||||
assertEquals(0, vaos.vaoMap.get(3).ebo, "vao 3 kept a freed shared EBO");
|
||||
assertFalse(vaos.eboOwners.containsKey(ebo), "index kept owners for a freed EBO");
|
||||
}
|
||||
|
||||
@Test
|
||||
void sharedEboEntryDropsOnlyAfterItsLastOwnerGoes() {
|
||||
final int ebo = 88;
|
||||
bindVao(1);
|
||||
bindEbo(ebo);
|
||||
bindVao(2);
|
||||
bindEbo(ebo);
|
||||
bindVao(DEFAULT_VAO);
|
||||
|
||||
deleteVao(1);
|
||||
assertTrue(vaos.eboOwners.containsKey(ebo), "index dropped a still-live owner");
|
||||
deleteVao(2);
|
||||
assertFalse(vaos.eboOwners.containsKey(ebo), "index kept an entry after its last owner went away");
|
||||
}
|
||||
|
||||
@Test
|
||||
void deletingTheBoundVaoDoesNotOrphanAnIndexEntry() {
|
||||
final int ebo = 99;
|
||||
bindVao(1);
|
||||
bindEbo(ebo);
|
||||
|
||||
deleteVao(1);
|
||||
bindVao(DEFAULT_VAO);
|
||||
|
||||
assertFalse(vaos.eboOwners.containsKey(ebo), "deleting the bound VAO left an unreachable owner in the index");
|
||||
assertEquals(0, vaos.eboOwners.size(), "index leaked an entry for a deleted VAO");
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexEmptiesOutWhenEverythingIsDeleted() {
|
||||
for (int vao = 1; vao <= 64; vao++) {
|
||||
bindVao(vao);
|
||||
bindEbo(1000 + vao);
|
||||
}
|
||||
bindVao(DEFAULT_VAO);
|
||||
for (int vao = 1; vao <= 64; vao++) {
|
||||
deleteVao(vao);
|
||||
}
|
||||
assertEquals(0, vaos.eboOwners.size(), "index leaked entries after every VAO was deleted");
|
||||
}
|
||||
}
|
||||
@@ -35,13 +35,11 @@ class WideLineClipGLTest {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
FfpFixture.routeVertexFormats(true);
|
||||
savedEmulation = GLStateManager.wideLineEmulationEnabled;
|
||||
GLStateManager.wideLineEmulationEnabled = true;
|
||||
|
||||
FfpFixture.resetFfpState();
|
||||
ShaderManager.enable();
|
||||
ShaderManager.getInstance().activate();
|
||||
FfpFixture.activateFfp();
|
||||
GLStateManager.glViewport(0, 0, SIZE, SIZE);
|
||||
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
|
||||
GLStateManager.glLoadIdentity();
|
||||
@@ -54,7 +52,6 @@ class WideLineClipGLTest {
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
FfpFixture.routeVertexFormats(false);
|
||||
GLStateManager.wideLineEmulationEnabled = savedEmulation;
|
||||
GLStateManager.glLineWidth(1.0f);
|
||||
if (vbo != 0) { GLStateManager.glDeleteBuffers(vbo); vbo = 0; }
|
||||
|
||||
+21
-89
@@ -139,6 +139,10 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
assertNotNull(cmd);
|
||||
assertEquals(6, cmd.getIndexCount());
|
||||
assertEquals(GL11.GL_UNSIGNED_INT, cmd.getIndexType(), "batching always widens indices to UINT for safe base-vertex shifting");
|
||||
|
||||
final ByteBuffer ib = readBufferBytes(GL15.GL_ELEMENT_ARRAY_BUFFER, firstBatch(list).getSharedEBO(), 6 * 4);
|
||||
assertUintIndices(ib, 0, 1, 2, 2, 3, 0);
|
||||
MemoryUtilities.memFree(ib);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -156,13 +160,7 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
final int vbo = defaultQuadVbo();
|
||||
final int vao = setupPosColorVao(vbo);
|
||||
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
final int list = recordSingleIndexedDraw(vao, 0, GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT);
|
||||
|
||||
assertTrue(allBatchedCmds(list).isEmpty(), "no EBO bound → no batched command attached");
|
||||
assertTrue(batchesOf(list).isEmpty(), "no batches either");
|
||||
@@ -357,51 +355,6 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
MemoryUtilities.memFree(ib);
|
||||
}
|
||||
|
||||
@Test
|
||||
void multiDrawDifferentFormats_splitBatches() {
|
||||
// Draw A: pos+color (stride=12). Draw B: pos-only (stride=8). Different layout → two batches.
|
||||
final int vboA = defaultQuadVbo();
|
||||
final int vaoA = setupPosColorVao(vboA);
|
||||
|
||||
final int vboB = newBuffer();
|
||||
final ByteBuffer bData = MemoryUtilities.memAlloc(4 * 8).order(ByteOrder.nativeOrder());
|
||||
for (int i = 0; i < 4; i++) {
|
||||
bData.putFloat(DEFAULT_QUAD_POSITIONS[i * 2]);
|
||||
bData.putFloat(DEFAULT_QUAD_POSITIONS[i * 2 + 1]);
|
||||
}
|
||||
bData.flip();
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboB);
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, bData, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
MemoryUtilities.memFree(bData);
|
||||
|
||||
final int vaoB = newVao();
|
||||
GLStateManager.glBindVertexArray(vaoB);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboB);
|
||||
GLStateManager.glVertexAttribPointer(0, 2, GL11.GL_FLOAT, false, 8, 0);
|
||||
GLStateManager.glEnableVertexAttribArray(0);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
|
||||
final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2);
|
||||
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glBindVertexArray(vaoA);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
GLStateManager.glBindVertexArray(vaoB);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
|
||||
final List<BatchedIndexedDrawCmd> cmds = allBatchedCmds(list);
|
||||
assertEquals(2, cmds.size());
|
||||
assertEquals(2, batchesOf(list).size(), "incompatible layouts → two batches");
|
||||
assertNotSame(batchOf(cmds.get(0)), batchOf(cmds.get(1)), "separate layouts must get distinct batches");
|
||||
}
|
||||
|
||||
@Test
|
||||
void sameSizesDifferentLocations_splitIntoSeparateBatches() {
|
||||
// Both VAOs: pos 2f + color 4ub (same sizes/types/normalized). Only the enabled
|
||||
@@ -424,16 +377,7 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
|
||||
final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2);
|
||||
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
GLStateManager.glBindVertexArray(vaoA);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
GLStateManager.glBindVertexArray(vaoB);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
final int list = recordTwoVaoDraws(vaoA, vaoB, ebo);
|
||||
|
||||
final List<BatchedIndexedDrawCmd> cmds = allBatchedCmds(list);
|
||||
assertEquals(2, cmds.size());
|
||||
@@ -472,30 +416,14 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
|
||||
// Record (A, B) and (B, A) as independent lists; each list's batch order
|
||||
// must match its own recording order.
|
||||
final int listAB = newList();
|
||||
GLStateManager.glNewList(listAB, GL11.GL_COMPILE);
|
||||
GLStateManager.glBindVertexArray(vaoA);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
GLStateManager.glBindVertexArray(vaoB);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
final int listAB = recordTwoVaoDraws(vaoA, vaoB, ebo);
|
||||
|
||||
final int listBA = newList();
|
||||
GLStateManager.glNewList(listBA, GL11.GL_COMPILE);
|
||||
GLStateManager.glBindVertexArray(vaoB);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
GLStateManager.glBindVertexArray(vaoA);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
final int listBA = recordTwoVaoDraws(vaoB, vaoA, ebo);
|
||||
|
||||
final List<BatchedIndexedDrawCmd> ab = allBatchedCmds(listAB);
|
||||
final List<BatchedIndexedDrawCmd> ba = allBatchedCmds(listBA);
|
||||
assertEquals(2, ab.size());
|
||||
assertEquals(2, batchesOf(listAB).size(), "incompatible layouts -> two batches");
|
||||
// Placeholders sit in command-stream order (A-first vs B-first).
|
||||
// Batches must be ordered so that placeholder[0] points at batch[0].
|
||||
assertSame(batchesOf(listAB).get(0), batchOf(ab.get(0)),
|
||||
@@ -514,44 +442,48 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
|
||||
final ByteBuffer idx = BufferUtils.createByteBuffer(3);
|
||||
idx.put(new byte[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
}, GL11.GL_TRIANGLES, 3, new int[]{0, 1, 2}),
|
||||
arguments("short indices", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final ShortBuffer idx = BufferUtils.createShortBuffer(3);
|
||||
idx.put(new short[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
}, GL11.GL_TRIANGLES, 3, new int[]{0, 1, 2}),
|
||||
arguments("int indices", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(3);
|
||||
idx.put(new int[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
}, GL11.GL_TRIANGLES, 3, new int[]{0, 1, 2}),
|
||||
arguments("explicit count+type", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final ByteBuffer idx = BufferUtils.createByteBuffer(3 * 2).order(ByteOrder.nativeOrder());
|
||||
idx.putShort((short) 0).putShort((short) 1).putShort((short) 2).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, idx);
|
||||
}, GL11.GL_TRIANGLES, 3),
|
||||
}, GL11.GL_TRIANGLES, 3, new int[]{0, 1, 2}),
|
||||
arguments("quads triangulate at record", GL11.GL_COMPILE, (Runnable) () -> {
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(4);
|
||||
idx.put(new int[]{0, 1, 2, 3}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_QUADS, idx);
|
||||
}, GL11.GL_TRIANGLES, 6),
|
||||
}, GL11.GL_TRIANGLES, 6, new int[]{0, 1, 3, 1, 2, 3}),
|
||||
arguments("compile-and-execute fires live and bakes", GL11.GL_COMPILE_AND_EXECUTE, (Runnable) () -> {
|
||||
final IntBuffer idx = BufferUtils.createIntBuffer(3);
|
||||
idx.put(new int[]{0, 1, 2}).flip();
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, idx);
|
||||
}, GL11.GL_TRIANGLES, 3)
|
||||
}, GL11.GL_TRIANGLES, 3, new int[]{0, 1, 2})
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("clientIndexedDrawCases")
|
||||
void clientIndexedDraw_capturedAndWidenedToUnsignedInt(String name, int listMode, Runnable draw, int expectedMode, int expectedCount) {
|
||||
void clientIndexedDraw_capturedAndWidenedToUnsignedInt(String name, int listMode, Runnable draw, int expectedMode, int expectedCount, int[] expectedIndices) {
|
||||
final int list = recordClientIndexDraw(listMode, draw);
|
||||
final BatchedIndexedDrawCmd cmd = firstBatchedCmd(list);
|
||||
assertNotNull(cmd);
|
||||
assertEquals(expectedMode, cmd.getDrawMode());
|
||||
assertEquals(expectedCount, cmd.getIndexCount());
|
||||
assertEquals(GL11.GL_UNSIGNED_INT, cmd.getIndexType(), "client indices always widen to UNSIGNED_INT");
|
||||
|
||||
final ByteBuffer ib = readBufferBytes(GL15.GL_ELEMENT_ARRAY_BUFFER, firstBatch(list).getSharedEBO(), expectedCount * 4);
|
||||
assertUintIndices(ib, expectedIndices);
|
||||
MemoryUtilities.memFree(ib);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
@@ -79,6 +79,7 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
|
||||
GLStateManager.glBindVertexArray(vaoOf(batch));
|
||||
assertEquals(batch.getSharedEBO(), GLStateManager.getBoundEBO(),
|
||||
"batched VAO's EBO binding corrupted");
|
||||
assertEquals(batch.getSharedEBO(), GL11.glGetInteger(GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING));
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
}
|
||||
|
||||
|
||||
+13
@@ -148,6 +148,19 @@ public abstract class DisplayListTestFixture {
|
||||
return list;
|
||||
}
|
||||
|
||||
protected final int recordTwoVaoDraws(int firstVao, int secondVao, int ebo) {
|
||||
final int list = newList();
|
||||
GLStateManager.glNewList(list, GL11.GL_COMPILE);
|
||||
for (int vao : new int[] { firstVao, secondVao }) {
|
||||
GLStateManager.glBindVertexArray(vao);
|
||||
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||
GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);
|
||||
}
|
||||
GLStateManager.glBindVertexArray(0);
|
||||
GLStateManager.glEndList();
|
||||
return list;
|
||||
}
|
||||
|
||||
protected static CompiledDisplayList compiledOf(int listId) {
|
||||
final CompiledDisplayList compiled = DisplayListManager.getDisplayList(listId);
|
||||
assertNotNull(compiled, "compiled display list must exist for id " + listId);
|
||||
|
||||
+143
-229
@@ -119,6 +119,7 @@ class GLSMRedirectorTest {
|
||||
assertEquals(expectedName, result.getName());
|
||||
assertEquals(desc, result.getDesc());
|
||||
assertEquals(Opcodes.H_INVOKESTATIC, result.getTag());
|
||||
assertDoesNotThrow(() -> cn.accept(new ClassWriter(ClassWriter.COMPUTE_MAXS)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -158,21 +159,6 @@ class GLSMRedirectorTest {
|
||||
assertTrue(unmappedSeen().contains("org/lwjgl/opengl/ARBSparseTexture.glTexPageCommitmentARB()V"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void debugLabelIsRedirectedNotReported() {
|
||||
assertRedirected("org/lwjgl/opengl/KHRDebug", "glPushDebugGroup", "(IILjava/lang/CharSequence;)V", "glPushDebugGroup");
|
||||
assertRedirected("org/lwjgl/opengl/GL43C", "glObjectLabel", "(IILjava/lang/CharSequence;)V", "glObjectLabel");
|
||||
assertTrue(debugSeen().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonAwareDebugCallbackIsRedirectedNotPassedThrough() {
|
||||
assertRedirected("org/lwjgl/opengl/GL43", "glDebugMessageCallback", "(Lorg/lwjgl/opengl/KHRDebugCallback;)V", "glDebugMessageCallback");
|
||||
assertRedirected("org/lwjgl/opengl/KHRDebug", "glDebugMessageCallback", "(Lorg/lwjgl/opengl/KHRDebugCallback;)V", "glDebugMessageCallback");
|
||||
assertTrue(debugSeen().isEmpty());
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void debugOwnerAndWrongDescriptorArePassthrough() {
|
||||
makeAndTransform("org/lwjgl/opengl/AMDDebugOutput", "glDebugMessageEnableAMD", "(IILjava/nio/IntBuffer;Z)V");
|
||||
@@ -221,42 +207,6 @@ class GLSMRedirectorTest {
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{1}")
|
||||
@CsvSource(delimiter = '|', value = {
|
||||
"org/lwjgl/opengl/EXTFramebufferObject | glBindFramebufferEXT | (II)V | glBindFramebuffer",
|
||||
"org/lwjgl/opengl/ARBFramebufferObject | glGenerateMipmap | (I)V | glGenerateMipmap",
|
||||
"org/lwjgl/opengl/EXTFramebufferObject | glGenerateMipmapEXT | (I)V | glGenerateMipmap",
|
||||
"org/lwjgl/opengl/GL11 | glReadPixels | (IIIIIILjava/nio/IntBuffer;)V | glReadPixels",
|
||||
"org/lwjgl/opengl/ARBOcclusionQuery | glGenQueriesARB | (Ljava/nio/IntBuffer;)V | glGenQueries"
|
||||
})
|
||||
void aliasIsRedirected(String owner, String name, String desc, String expectedName) {
|
||||
assertEquals(desc, assertRedirected(owner, name, desc, expectedName).desc);
|
||||
}
|
||||
|
||||
@Test
|
||||
void pixelPackBufferReadbackOverloadsHaveDeclaredTargets() throws Exception {
|
||||
assertRedirectedToDeclaredTarget(new String[][]{
|
||||
{"org/lwjgl/opengl/GL11", "glReadPixels", "(IIIIIIJ)V"},
|
||||
{"org/lwjgl/opengl/GL11C", "glReadPixels", "(IIIIIIJ)V"},
|
||||
{"org/lwjgl/opengl/GL11", "glGetTexImage", "(IIIIJ)V"},
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void utilCheckGLErrorIsRedirected() throws Exception {
|
||||
assertRedirectedToDeclaredTarget(new String[][]{{"org/lwjgl/opengl/Util", "checkGLError", "()V"}});
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexedGettersHaveDeclaredTargets() throws Exception {
|
||||
assertRedirectedToDeclaredTarget(new String[][]{
|
||||
{"org/lwjgl/opengl/GL30", "glGetInteger", "(II)I"},
|
||||
{"org/lwjgl/opengl/GL31C", "glGetIntegeri", "(II)I"},
|
||||
{"org/lwjgl/opengl/GL30", "glGetIntegeri_v", "(IILjava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/GL30", "glGetBooleani_v", "(IILjava/nio/ByteBuffer;)V"},
|
||||
});
|
||||
}
|
||||
|
||||
private MethodInsnNode assertRedirected(String owner, String name, String desc, String expectedName) {
|
||||
final ClassNode cn = makeClassWithMethodCall(owner, name, desc);
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
@@ -277,80 +227,134 @@ class GLSMRedirectorTest {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void assertRedirectedToDeclaredTarget(String[][] cases) throws ClassNotFoundException {
|
||||
@ParameterizedTest(name = "{0}.{1}{2}")
|
||||
@CsvSource(delimiter = '|', value = {
|
||||
"org/lwjgl/opengl/EXTFramebufferObject | glBindFramebufferEXT | (II)V | glBindFramebuffer",
|
||||
"org/lwjgl/opengl/EXTFramebufferObject | glFramebufferTexture3DEXT | (IIIIII)V | glFramebufferTexture3D",
|
||||
"org/lwjgl/opengl/ARBFramebufferObject | glGenerateMipmap | (I)V | glGenerateMipmap",
|
||||
"org/lwjgl/opengl/EXTFramebufferObject | glGenerateMipmapEXT | (I)V | glGenerateMipmap",
|
||||
"org/lwjgl/opengl/GL11 | glReadPixels | (IIIIIILjava/nio/IntBuffer;)V | glReadPixels",
|
||||
"org/lwjgl/opengl/ARBOcclusionQuery | glGenQueriesARB | (Ljava/nio/IntBuffer;)V | glGenQueries",
|
||||
"org/lwjgl/opengl/KHRDebug | glPushDebugGroup | (IILjava/lang/CharSequence;)V | glPushDebugGroup",
|
||||
"org/lwjgl/opengl/GL43C | glObjectLabel | (IILjava/lang/CharSequence;)V | glObjectLabel",
|
||||
"org/lwjgl/opengl/GL43 | glDebugMessageCallback | (Lorg/lwjgl/opengl/KHRDebugCallback;)V | glDebugMessageCallback",
|
||||
"org/lwjgl/opengl/KHRDebug | glDebugMessageCallback | (Lorg/lwjgl/opengl/KHRDebugCallback;)V | glDebugMessageCallback",
|
||||
"org/lwjgl/opengl/GL32C | glFenceSync | (II)J | glFenceSync",
|
||||
"org/lwjgl/opengl/GL32C | glDeleteSync | (J)V | glDeleteSync",
|
||||
"org/lwjgl/opengl/GL15C | nglMapBuffer | (II)J | nglMapBuffer",
|
||||
"org/lwjgl/opengl/GL33C | glQueryCounter | (II)V | glQueryCounter",
|
||||
"org/lwjgl/opengl/GL33C | glGetQueryObjectui64 | (II)J | glGetQueryObjectui64",
|
||||
"org/lwjgl/opengl/ARBTimerQuery | glGetQueryObjectui64 | (II)J | glGetQueryObjectui64",
|
||||
"org/lwjgl/opengl/EXTTimerQuery | glGetQueryObjectuEXT | (II)J | glGetQueryObjectui64",
|
||||
"org/lwjgl/opengl/EXTTimerQuery | glGetQueryObjectui64EXT | (II)J | glGetQueryObjectui64",
|
||||
"org/lwjgl/opengl/GL30C | glBindFragDataLocation | (IILjava/lang/CharSequence;)V | glBindFragDataLocation",
|
||||
"org/lwjgl/opengl/GL11C | glGetIntegerv | (I[I)V | glGetInteger",
|
||||
"org/lwjgl/opengl/GL20C | glUniform4fv | (I[F)V | glUniform4",
|
||||
"org/lwjgl/opengl/GL20C | glUniform4fv | (ILjava/nio/FloatBuffer;)V | glUniform4",
|
||||
"org/lwjgl/opengl/EXTGpuShader4 | glVertexAttribIPointerEXT | (IIIIJ)V | glVertexAttribIPointer",
|
||||
"org/lwjgl/opengl/EXTGPUShader4 | glVertexAttribIPointerEXT | (IIIIJ)V | glVertexAttribIPointer",
|
||||
"org/lwjgl/opengl/ARBUniformBufferObject | glGetUniformBlockIndex | (ILjava/lang/CharSequence;)I | glGetUniformBlockIndex",
|
||||
"org/lwjgl/opengl/GL30C | glBindBufferBase | (III)V | glBindBufferBase",
|
||||
"org/lwjgl/opengl/GL31C | glUniformBlockBinding | (III)V | glUniformBlockBinding",
|
||||
"org/lwjgl/opengl/GL32C | glDrawElementsBaseVertex | (IIIJI)V | glDrawElementsBaseVertex",
|
||||
"org/lwjgl/opengl/GL43C | glMultiDrawElementsIndirect | (IIJII)V | glMultiDrawElementsIndirect",
|
||||
"org/lwjgl/opengl/GL20C | glUniform1fv | (ILjava/nio/FloatBuffer;)V | glUniform1",
|
||||
"org/lwjgl/opengl/GL11 | glLoadMatrixf | (Ljava/nio/FloatBuffer;)V | glLoadMatrix",
|
||||
"org/lwjgl/opengl/ARBVertexArrayObject | glGenVertexArrays | ()I | glGenVertexArrays",
|
||||
"org/lwjgl/opengl/APPLEVertexArrayObject | glGenVertexArraysAPPLE | ()I | glGenVertexArrays",
|
||||
"org/lwjgl/opengl/ARBGeometryShader4 | glFramebufferTextureFaceARB | (IIIII)V | glFramebufferTextureFace",
|
||||
"org/lwjgl/opengl/EXTGeometryShader4 | glFramebufferTextureEXT | (IIII)V | glFramebufferTexture",
|
||||
"org/lwjgl/opengl/EXTTextureArray | glFramebufferTextureLayerEXT | (IIIII)V | glFramebufferTextureLayer",
|
||||
"org/lwjgl/opengl/EXTShaderImageLoadStore | glBindImageTextureEXT | (IIIZIII)V | glBindImageTexture",
|
||||
"org/lwjgl/opengl/EXTFramebufferObject | glDeleteFramebuffersEXT | (Ljava/nio/IntBuffer;)V | glDeleteFramebuffers",
|
||||
})
|
||||
void aliasIsRedirected(String owner, String name, String desc, String expectedName) throws ClassNotFoundException {
|
||||
assertEquals(desc, assertRedirected(owner, name, desc, expectedName).desc);
|
||||
assertTrue(glsmDeclares(expectedName, desc), () -> "GLStateManager lacks " + expectedName + desc);
|
||||
assertTrue(debugSeen().isEmpty());
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}.{1}{2}")
|
||||
@CsvSource(delimiter = '|', value = {
|
||||
"org/lwjgl/opengl/GL11 | glPopMatrix | ()V",
|
||||
"org/lwjgl/opengl/GL11 | glReadPixels | (IIIIIIJ)V",
|
||||
"org/lwjgl/opengl/GL11C | glReadPixels | (IIIIIIJ)V",
|
||||
"org/lwjgl/opengl/GL11 | glGetTexImage | (IIIIJ)V",
|
||||
"org/lwjgl/opengl/Util | checkGLError | ()V",
|
||||
"org/lwjgl/opengl/GL30 | glGetInteger | (II)I",
|
||||
"org/lwjgl/opengl/GL31C | glGetIntegeri | (II)I",
|
||||
"org/lwjgl/opengl/GL30 | glGetIntegeri_v | (IILjava/nio/IntBuffer;)V",
|
||||
"org/lwjgl/opengl/GL30 | glGetBooleani_v | (IILjava/nio/ByteBuffer;)V",
|
||||
"org/lwjgl/opengl/GL33 | glGenSamplers | ()I",
|
||||
"org/lwjgl/opengl/GL33 | glGenSamplers | (Ljava/nio/IntBuffer;)V",
|
||||
"org/lwjgl/opengl/GL33C | glGenSamplers | ([I)V",
|
||||
"org/lwjgl/opengl/GL33 | glGenSamplers | ([I)V",
|
||||
"org/lwjgl/opengl/GL33 | glDeleteSamplers | (I)V",
|
||||
"org/lwjgl/opengl/GL33 | glDeleteSamplers | (Ljava/nio/IntBuffer;)V",
|
||||
"org/lwjgl/opengl/GL33C | glDeleteSamplers | ([I)V",
|
||||
"org/lwjgl/opengl/GL33 | glDeleteSamplers | ([I)V",
|
||||
"org/lwjgl/opengl/GL44 | glBindSamplers | (IILjava/nio/IntBuffer;)V",
|
||||
"org/lwjgl/opengl/GL44C | glBindSamplers | (ILjava/nio/IntBuffer;)V",
|
||||
"org/lwjgl/opengl/GL44 | glBindSamplers | (I[I)V",
|
||||
"org/lwjgl/opengl/ARBMultiBind | glBindSamplers | (IILjava/nio/IntBuffer;)V",
|
||||
"org/lwjgl/opengl/ARBMultiBind | glBindSamplers | (ILjava/nio/IntBuffer;)V",
|
||||
"org/lwjgl/opengl/ARBMultiBind | glBindSamplers | (I[I)V",
|
||||
"org/lwjgl/opengl/ARBSamplerObjects | glGenSamplers | ()I",
|
||||
"org/lwjgl/opengl/ARBSamplerObjects | glGenSamplers | (Ljava/nio/IntBuffer;)V",
|
||||
"org/lwjgl/opengl/ARBSamplerObjects | glGenSamplers | ([I)V",
|
||||
"org/lwjgl/opengl/ARBSamplerObjects | glDeleteSamplers | (I)V",
|
||||
"org/lwjgl/opengl/ARBSamplerObjects | glDeleteSamplers | (Ljava/nio/IntBuffer;)V",
|
||||
"org/lwjgl/opengl/ARBSamplerObjects | glDeleteSamplers | ([I)V",
|
||||
"org/lwjgl/opengl/ARBSamplerObjects | glIsSampler | (I)Z",
|
||||
"org/lwjgl/opengl/ARBSamplerObjects | glBindSampler | (II)V",
|
||||
"org/lwjgl/opengl/ARBSamplerObjects | glSamplerParameteri | (III)V",
|
||||
"org/lwjgl/opengl/ARBSamplerObjects | glSamplerParameterf | (IIF)V",
|
||||
"org/lwjgl/opengl/GL43 | glGetDebugMessageLog | (ILjava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/ByteBuffer;)I",
|
||||
"org/lwjgl/opengl/GL30 | glFramebufferTexture1D | (IIIII)V",
|
||||
"org/lwjgl/opengl/GL30 | glFramebufferTexture3D | (IIIIII)V",
|
||||
"org/lwjgl/opengl/GL30 | glFramebufferTextureLayer | (IIIII)V",
|
||||
"org/lwjgl/opengl/GL30C | glDeleteFramebuffers | ([I)V",
|
||||
"org/lwjgl/opengl/GL11C | glDeleteTextures | ([I)V",
|
||||
"org/lwjgl/opengl/GL11C | glGenTextures | ([I)V",
|
||||
"org/lwjgl/opengl/GL44 | glBindTextures | (IILjava/nio/IntBuffer;)V",
|
||||
"org/lwjgl/opengl/GL44C | glBindTextures | (ILjava/nio/IntBuffer;)V",
|
||||
"org/lwjgl/opengl/GL44 | glBindTextures | (I[I)V",
|
||||
"org/lwjgl/opengl/GL44 | glBindImageTextures | (IILjava/nio/IntBuffer;)V",
|
||||
"org/lwjgl/opengl/GL44C | glBindImageTextures | (ILjava/nio/IntBuffer;)V",
|
||||
"org/lwjgl/opengl/GL44 | glBindImageTextures | (I[I)V",
|
||||
"org/lwjgl/opengl/GL45 | glBindTextureUnit | (II)V",
|
||||
"org/lwjgl/opengl/GL45 | glCreateTextures | (I)I",
|
||||
"org/lwjgl/opengl/GL45C | glCreateTextures | (ILjava/nio/IntBuffer;)V",
|
||||
"org/lwjgl/opengl/GL45C | glCreateTextures | (I[I)V",
|
||||
"org/lwjgl/opengl/GL45 | glNamedFramebufferTexture | (IIII)V",
|
||||
"org/lwjgl/opengl/GL45 | glNamedFramebufferTextureLayer | (IIIII)V",
|
||||
"org/lwjgl/opengl/ARBDirectStateAccess | glNamedFramebufferRenderbuffer | (IIII)V",
|
||||
"org/lwjgl/opengl/EXTDirectStateAccess | glNamedFramebufferTexture1DEXT | (IIIII)V",
|
||||
"org/lwjgl/opengl/EXTDirectStateAccess | glNamedFramebufferTexture2DEXT | (IIIII)V",
|
||||
"org/lwjgl/opengl/EXTDirectStateAccess | glNamedFramebufferTexture3DEXT | (IIIIII)V",
|
||||
"org/lwjgl/opengl/EXTDirectStateAccess | glNamedFramebufferTextureEXT | (IIII)V",
|
||||
"org/lwjgl/opengl/EXTDirectStateAccess | glNamedFramebufferTextureFaceEXT | (IIIII)V",
|
||||
"org/lwjgl/opengl/EXTDirectStateAccess | glBindMultiTextureEXT | (III)V",
|
||||
"org/lwjgl/opengl/ARBShaderImageLoadStore | glBindImageTexture | (IIIZIII)V",
|
||||
})
|
||||
void redirectsToADeclaredGlsmTarget(String owner, String name, String desc) throws ClassNotFoundException {
|
||||
for (boolean aware : new boolean[]{false, true}) {
|
||||
for (String[] c : cases) {
|
||||
final String call = c[0] + "." + c[1] + c[2] + (aware ? " aware" : "");
|
||||
final ClassNode cn = makeClassWithMethodCall(c[0], c[1], c[2]);
|
||||
assertTrue(new GLSMRedirector().transformClassNode("com.example.TestClass", cn, aware), call);
|
||||
final MethodInsnNode target = firstCall(cn);
|
||||
assertEquals(GLSM, target.owner, call);
|
||||
assertEquals(c[1], target.name, call);
|
||||
assertEquals(c[2], target.desc, call);
|
||||
assertTrue(glsmDeclares(target.name, target.desc), () -> "GLStateManager lacks the target of " + call);
|
||||
}
|
||||
final String call = owner + "." + name + desc + (aware ? " aware" : "");
|
||||
final ClassNode cn = makeClassWithMethodCall(owner, name, desc);
|
||||
assertTrue(new GLSMRedirector().transformClassNode("com.example.TestClass", cn, aware), call);
|
||||
final MethodInsnNode target = firstCall(cn);
|
||||
assertEquals(GLSM, target.owner, call);
|
||||
assertEquals(name, target.name, call);
|
||||
assertEquals(desc, target.desc, call);
|
||||
assertTrue(glsmDeclares(target.name, target.desc), () -> "GLStateManager lacks the target of " + call);
|
||||
}
|
||||
assertTrue(unmappedSeen().isEmpty(), () -> "unexpected reports: " + unmappedSeen());
|
||||
}
|
||||
|
||||
@Test
|
||||
void samplerGenDeleteOverloadsHaveDeclaredTargets() throws Exception {
|
||||
assertRedirectedToDeclaredTarget(new String[][]{
|
||||
{"org/lwjgl/opengl/GL33", "glGenSamplers", "()I"},
|
||||
{"org/lwjgl/opengl/GL33", "glGenSamplers", "(Ljava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/GL33C", "glGenSamplers", "([I)V"},
|
||||
{"org/lwjgl/opengl/GL33", "glGenSamplers", "([I)V"},
|
||||
{"org/lwjgl/opengl/GL33", "glDeleteSamplers", "(I)V"},
|
||||
{"org/lwjgl/opengl/GL33", "glDeleteSamplers", "(Ljava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/GL33C", "glDeleteSamplers", "([I)V"},
|
||||
{"org/lwjgl/opengl/GL33", "glDeleteSamplers", "([I)V"},
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindSamplersHasDeclaredTargets() throws Exception {
|
||||
assertRedirectedToDeclaredTarget(new String[][]{
|
||||
{"org/lwjgl/opengl/GL44", "glBindSamplers", "(IILjava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/GL44C", "glBindSamplers", "(ILjava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/GL44", "glBindSamplers", "(I[I)V"},
|
||||
{"org/lwjgl/opengl/ARBMultiBind", "glBindSamplers", "(IILjava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/ARBMultiBind", "glBindSamplers", "(ILjava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/ARBMultiBind", "glBindSamplers", "(I[I)V"},
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void arbSamplerObjectsHasDeclaredTargets() throws Exception {
|
||||
assertRedirectedToDeclaredTarget(new String[][]{
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glGenSamplers", "()I"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glGenSamplers", "(Ljava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glGenSamplers", "([I)V"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glDeleteSamplers", "(I)V"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glDeleteSamplers", "(Ljava/nio/IntBuffer;)V"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glDeleteSamplers", "([I)V"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glIsSampler", "(I)Z"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glBindSampler", "(II)V"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glSamplerParameteri", "(III)V"},
|
||||
{"org/lwjgl/opengl/ARBSamplerObjects", "glSamplerParameterf", "(IIF)V"},
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void descKeyedSyncRedirectsOnlyLongForm() {
|
||||
assertRedirected("org/lwjgl/opengl/GL32C", "glFenceSync", "(II)J", "glFenceSync");
|
||||
assertRedirected("org/lwjgl/opengl/GL32C", "glDeleteSync", "(J)V", "glDeleteSync");
|
||||
assertRedirected("org/lwjgl/opengl/GL15C", "nglMapBuffer", "(II)J", "nglMapBuffer");
|
||||
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/GL32", "glFenceSync", "(II)Lorg/lwjgl/opengl/GLSync;");
|
||||
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
assertFalse(changed);
|
||||
assertEquals("org/lwjgl/opengl/GL32", firstCall(cn).owner);
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void glSyncObjectFormsAreNotReported() {
|
||||
final String[][] cases = {
|
||||
{"org/lwjgl/opengl/GL32", "glFenceSync", "(II)Lorg/lwjgl/opengl/GLSync;"},
|
||||
{"org/lwjgl/opengl/GL32", "glClientWaitSync", "(Lorg/lwjgl/opengl/GLSync;IJ)I"},
|
||||
{"org/lwjgl/opengl/ARBSync", "glWaitSync", "(Lorg/lwjgl/opengl/GLSync;IJ)V"},
|
||||
{"org/lwjgl/opengl/ARBSync", "glDeleteSync", "(Lorg/lwjgl/opengl/GLSync;)V"},
|
||||
@@ -362,79 +366,39 @@ class GLSMRedirectorTest {
|
||||
assertTrue(unmappedSeen().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void phase2NameKeyedGapsRedirect() {
|
||||
assertRedirected("org/lwjgl/opengl/GL33C", "glQueryCounter", "(II)V", "glQueryCounter");
|
||||
assertRedirected("org/lwjgl/opengl/GL33C", "glGetQueryObjectui64", "(II)J", "glGetQueryObjectui64");
|
||||
assertRedirected("org/lwjgl/opengl/ARBTimerQuery", "glGetQueryObjectui64", "(II)J", "glGetQueryObjectui64");
|
||||
assertRedirected("org/lwjgl/opengl/EXTTimerQuery", "glGetQueryObjectuEXT", "(II)J", "glGetQueryObjectui64");
|
||||
assertRedirected("org/lwjgl/opengl/EXTTimerQuery", "glGetQueryObjectui64EXT", "(II)J", "glGetQueryObjectui64");
|
||||
assertRedirected("org/lwjgl/opengl/GL30C", "glBindFragDataLocation", "(IILjava/lang/CharSequence;)V", "glBindFragDataLocation");
|
||||
assertRedirected("org/lwjgl/opengl/GL11C", "glGetIntegerv", "(I[I)V", "glGetInteger");
|
||||
assertRedirected("org/lwjgl/opengl/GL20C", "glUniform4fv", "(I[F)V", "glUniform4");
|
||||
assertRedirected("org/lwjgl/opengl/GL20C", "glUniform4fv", "(ILjava/nio/FloatBuffer;)V", "glUniform4");
|
||||
}
|
||||
|
||||
@Test
|
||||
void phase1CeleritasGapsRedirect() {
|
||||
assertRedirected("org/lwjgl/opengl/EXTGpuShader4", "glVertexAttribIPointerEXT", "(IIIIJ)V", "glVertexAttribIPointer");
|
||||
assertRedirected("org/lwjgl/opengl/EXTGPUShader4", "glVertexAttribIPointerEXT", "(IIIIJ)V", "glVertexAttribIPointer");
|
||||
assertRedirected("org/lwjgl/opengl/ARBUniformBufferObject", "glGetUniformBlockIndex", "(ILjava/lang/CharSequence;)I", "glGetUniformBlockIndex");
|
||||
assertRedirected("org/lwjgl/opengl/GL30C", "glBindBufferBase", "(III)V", "glBindBufferBase");
|
||||
assertRedirected("org/lwjgl/opengl/GL31C", "glUniformBlockBinding", "(III)V", "glUniformBlockBinding");
|
||||
assertRedirected("org/lwjgl/opengl/GL32C", "glDrawElementsBaseVertex", "(IIIJI)V", "glDrawElementsBaseVertex");
|
||||
assertRedirected("org/lwjgl/opengl/GL43C", "glMultiDrawElementsIndirect", "(IIJII)V", "glMultiDrawElementsIndirect");
|
||||
assertRedirected("org/lwjgl/opengl/GL20C", "glUniform1fv", "(ILjava/nio/FloatBuffer;)V", "glUniform1");
|
||||
assertRedirected("org/lwjgl/opengl/GL11", "glLoadMatrixf", "(Ljava/nio/FloatBuffer;)V", "glLoadMatrix");
|
||||
assertRedirected("org/lwjgl/opengl/ARBVertexArrayObject", "glGenVertexArrays", "()I", "glGenVertexArrays");
|
||||
assertRedirected("org/lwjgl/opengl/APPLEVertexArrayObject", "glGenVertexArraysAPPLE", "()I", "glGenVertexArrays");
|
||||
}
|
||||
|
||||
private static final String AWARE_SHIM = "com/gtnewhorizons/angelica/sdlgpu/compat/AwareGLDebugShim";
|
||||
private static final String CALLBACK_I = "Lorg/lwjgl/opengl/GLDebugMessageCallbackI;";
|
||||
|
||||
@Test
|
||||
void awareCallerStillHasPlainGlCallsRedirected() {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/GL11", "glPopMatrix", "()V");
|
||||
|
||||
assertTrue(new GLSMRedirector().transformClassNode("com.example.TestClass", cn, true));
|
||||
|
||||
assertEquals(GLSM, firstCall(cn).owner);
|
||||
assertEquals("glPopMatrix", firstCall(cn).name);
|
||||
}
|
||||
|
||||
@Test
|
||||
void awareDebugMessageCallbackTargetsTheAwareShim() {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/GL43", "glDebugMessageCallback", "(" + CALLBACK_I + "J)V");
|
||||
@ParameterizedTest(name = "{0}.{1}{2}")
|
||||
@CsvSource(delimiter = '|', value = {
|
||||
"org/lwjgl/opengl/GL43 | glDebugMessageCallback | (Lorg/lwjgl/opengl/GLDebugMessageCallbackI;J)V",
|
||||
"org/lwjgl/opengl/GLUtil | setupDebugMessageCallback | ()Lorg/lwjgl/system/Callback;",
|
||||
"org/lwjgl/opengl/GLUtil | setupDebugMessageCallback | (Ljava/io/PrintStream;)Lorg/lwjgl/system/Callback;",
|
||||
})
|
||||
void awareDebugCallbackSetupTargetsTheAwareShim(String owner, String name, String desc) {
|
||||
final ClassNode cn = makeClassWithMethodCall(owner, name, desc);
|
||||
|
||||
assertTrue(new GLSMRedirector().transformClassNode("com.example.TestClass", cn, true));
|
||||
|
||||
assertEquals(AWARE_SHIM, firstCall(cn).owner, "GLStateManager cannot carry an org.lwjgl-typed descriptor for an aware caller");
|
||||
assertEquals("glDebugMessageCallback", firstCall(cn).name);
|
||||
assertEquals(name, firstCall(cn).name);
|
||||
}
|
||||
|
||||
@Test
|
||||
void awareGLUtilSetupDebugMessageCallbackIsRedirected() {
|
||||
for (String desc : new String[]{"()Lorg/lwjgl/system/Callback;", "(Ljava/io/PrintStream;)Lorg/lwjgl/system/Callback;"}) {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/GLUtil", "setupDebugMessageCallback", desc);
|
||||
@ParameterizedTest(name = "{0}.{1}{2}")
|
||||
@CsvSource(delimiter = '|', value = {
|
||||
"org/lwjgl/opengl/GL | getCapabilities | ()Lorg/lwjgl/opengl/GLCapabilities;",
|
||||
"org/lwjgl/opengl/GL | setCapabilities | (Lorg/lwjgl/opengl/GLCapabilities;)V",
|
||||
"org/lwjgl/opengl/GL | getFunctionProvider | ()Lorg/lwjgl/system/FunctionProvider;",
|
||||
"org/lwjgl/opengl/GLCapabilities | getAddressBuffer | ()Lorg/lwjgl/PointerBuffer;",
|
||||
"org/lwjgl/opengl/GLDebugMessageCallback | create | (Lorg/lwjgl/opengl/GLDebugMessageCallbackI;)Lorg/lwjgl/opengl/GLDebugMessageCallback;",
|
||||
})
|
||||
void awareSafeLwjglTypedMembersAreNeitherRedirectedNorReported(String owner, String name, String desc) {
|
||||
final ClassNode cn = makeClassWithMethodCall(owner, name, desc);
|
||||
|
||||
assertTrue(new GLSMRedirector().transformClassNode("com.example.TestClass", cn, true), desc);
|
||||
assertFalse(new GLSMRedirector().transformClassNode("com.example.TestClass", cn, true));
|
||||
|
||||
assertEquals(AWARE_SHIM, firstCall(cn).owner, desc);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void getDebugMessageLogReachesGLStateManager() {
|
||||
final String buffers = "Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/ByteBuffer;";
|
||||
for (String desc : new String[]{"(I" + buffers + ")I"}) {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/GL43", "glGetDebugMessageLog", desc);
|
||||
|
||||
assertTrue(new GLSMRedirector().transformClassNode("com.example.TestClass", cn, false), desc);
|
||||
|
||||
assertEquals(GLSM, firstCall(cn).owner, desc);
|
||||
assertEquals(desc, firstCall(cn).desc, "descriptor must be preserved so the right overload is hit");
|
||||
}
|
||||
assertEquals(owner, firstCall(cn).owner);
|
||||
assertTrue(unmappedSeen().isEmpty(), () -> "must not be reported: " + unmappedSeen());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -446,14 +410,6 @@ class GLSMRedirectorTest {
|
||||
assertTrue(unmappedSeen().stream().anyMatch(s -> s.startsWith("aware ") && s.contains("glSomethingExotic")), () -> "expected an aware-unroutable report, got " + unmappedSeen());
|
||||
}
|
||||
|
||||
@Test
|
||||
void awareUnroutableCallFailsFastWhenRequested() {
|
||||
Reflect.setStaticFinal(SystemProperties.class, "UNMAPPED_GL", UnmappedGLMode.class, UnmappedGLMode.STRICT);
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/GL32C", "glSomethingExotic", "(" + CALLBACK_I + ")V");
|
||||
|
||||
assertThrows(IllegalStateException.class, () -> new GLSMRedirector().transformClassNode("com.example.TestClass", cn, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonAwareCallerNeverTargetsTheAwareShim() {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/GL43", "glDebugMessageCallback", "(" + CALLBACK_I + "J)V");
|
||||
@@ -463,17 +419,6 @@ class GLSMRedirectorTest {
|
||||
assertFalse(AWARE_SHIM.equals(firstCall(cn).owner));
|
||||
}
|
||||
|
||||
/** Crashed the game: it carries an LWJGL type but is only a ThreadLocal read plus a null check. */
|
||||
@Test
|
||||
void awareGetCapabilitiesIsNeitherRedirectedNorReported() {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/GL", "getCapabilities", "()Lorg/lwjgl/opengl/GLCapabilities;");
|
||||
|
||||
assertFalse(new GLSMRedirector().transformClassNode("com.example.TestClass", cn, true));
|
||||
|
||||
assertEquals("org/lwjgl/opengl/GL", firstCall(cn).owner);
|
||||
assertTrue(unmappedSeen().isEmpty(), () -> "must not be reported: " + unmappedSeen());
|
||||
}
|
||||
|
||||
@Test
|
||||
void awareGetCapabilitiesDoesNotFailFastEvenInStrictMode() {
|
||||
Reflect.setStaticFinal(SystemProperties.class, "UNMAPPED_GL", UnmappedGLMode.class, UnmappedGLMode.STRICT);
|
||||
@@ -482,22 +427,6 @@ class GLSMRedirectorTest {
|
||||
assertDoesNotThrow(() -> new GLSMRedirector().transformClassNode("com.example.TestClass", cn, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void awareSafeLwjglTypedMembersAreNotReported() {
|
||||
final String[][] safe = {
|
||||
{"org/lwjgl/opengl/GL", "setCapabilities", "(Lorg/lwjgl/opengl/GLCapabilities;)V"},
|
||||
{"org/lwjgl/opengl/GL", "getFunctionProvider", "()Lorg/lwjgl/system/FunctionProvider;"},
|
||||
{"org/lwjgl/opengl/GLCapabilities", "getAddressBuffer", "()Lorg/lwjgl/PointerBuffer;"},
|
||||
{"org/lwjgl/opengl/GLDebugMessageCallback", "create", "(Lorg/lwjgl/opengl/GLDebugMessageCallbackI;)Lorg/lwjgl/opengl/GLDebugMessageCallback;"},
|
||||
};
|
||||
for (String[] m : safe) {
|
||||
unmappedSeen().clear();
|
||||
final ClassNode cn = makeClassWithMethodCall(m[0], m[1], m[2]);
|
||||
new GLSMRedirector().transformClassNode("com.example.TestClass", cn, true);
|
||||
assertTrue(unmappedSeen().isEmpty(), () -> m[0] + "." + m[1] + " must not be reported");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void awareDispatchingMembersOnTheGLClassAreStillReported() {
|
||||
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/GL", "createCapabilities", "(Z)Lorg/lwjgl/opengl/GLCapabilities;");
|
||||
@@ -516,19 +445,4 @@ class GLSMRedirectorTest {
|
||||
assertThrows(IllegalStateException.class, () -> new GLSMRedirector().transformClassNode("com.example.TestClass", cn, true), () -> "attempt " + attempt + " must still fail");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void transformedClassProducesValidBytecode() {
|
||||
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/GL11", "glPopMatrix", "()V", false);
|
||||
final ClassNode cn = makeClassWithInvokeDynamic(impl);
|
||||
|
||||
new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
|
||||
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
assertDoesNotThrow(() -> {
|
||||
cn.accept(cw);
|
||||
final byte[] bytes = cw.toByteArray();
|
||||
assertTrue(bytes.length > 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
+222
-341
@@ -1,10 +1,14 @@
|
||||
package com.gtnewhorizons.angelica.glsm.shader;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
@@ -12,267 +16,232 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class SpirvShaderTranslatorTest {
|
||||
|
||||
@Test
|
||||
void selectionBoxVs_producesNamedLooseUniforms() {
|
||||
final String src = """
|
||||
#version 330 core
|
||||
uniform mat4 u_MVP;
|
||||
uniform vec3 u_Min;
|
||||
uniform vec3 u_Max;
|
||||
const vec3 edges[24] = vec3[24](
|
||||
vec3(0,0,0), vec3(1,0,0), vec3(1,0,0), vec3(1,0,1),
|
||||
vec3(1,0,1), vec3(0,0,1), vec3(0,0,1), vec3(0,0,0),
|
||||
vec3(0,1,0), vec3(1,1,0), vec3(1,1,0), vec3(1,1,1),
|
||||
vec3(1,1,1), vec3(0,1,1), vec3(0,1,1), vec3(0,1,0),
|
||||
vec3(0,0,0), vec3(0,1,0), vec3(1,0,0), vec3(1,1,0),
|
||||
vec3(1,0,1), vec3(1,1,1), vec3(0,0,1), vec3(0,1,1)
|
||||
);
|
||||
void main() {
|
||||
vec3 pos = mix(u_Min, u_Max, edges[gl_VertexID]);
|
||||
gl_Position = u_MVP * vec4(pos, 1.0);
|
||||
}
|
||||
""";
|
||||
static Stream<Arguments> translates() {
|
||||
return Stream.of(
|
||||
Arguments.of("selection box VS produces named loose uniforms", GL20.GL_VERTEX_SHADER, "selection_box.vert", """
|
||||
#version 330 core
|
||||
uniform mat4 u_MVP;
|
||||
uniform vec3 u_Min;
|
||||
uniform vec3 u_Max;
|
||||
const vec3 edges[24] = vec3[24](
|
||||
vec3(0,0,0), vec3(1,0,0), vec3(1,0,0), vec3(1,0,1),
|
||||
vec3(1,0,1), vec3(0,0,1), vec3(0,0,1), vec3(0,0,0),
|
||||
vec3(0,1,0), vec3(1,1,0), vec3(1,1,0), vec3(1,1,1),
|
||||
vec3(1,1,1), vec3(0,1,1), vec3(0,1,1), vec3(0,1,0),
|
||||
vec3(0,0,0), vec3(0,1,0), vec3(1,0,0), vec3(1,1,0),
|
||||
vec3(1,0,1), vec3(1,1,1), vec3(0,0,1), vec3(0,1,1)
|
||||
);
|
||||
void main() {
|
||||
vec3 pos = mix(u_Min, u_Max, edges[gl_VertexID]);
|
||||
gl_Position = u_MVP * vec4(pos, 1.0);
|
||||
}
|
||||
""",
|
||||
new String[] { "mat4 u_MVP", "vec3 u_Min", "vec3 u_Max" }, new String[0], new String[] { "_RESERVED_IDENTIFIER_FIXUP" }),
|
||||
Arguments.of("selection box FS produces a named uniform and a locationed frag output", GL20.GL_FRAGMENT_SHADER, "selection_box.frag", """
|
||||
#version 330 core
|
||||
uniform vec4 u_Color;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = u_Color;
|
||||
}
|
||||
""",
|
||||
new String[] { "vec4 u_Color" }, new String[] { "layout(location = 0)" }, new String[0]),
|
||||
Arguments.of("uvec3 * vec3 must be made explicit by the SPIR-V round-trip", GL20.GL_VERTEX_SHADER, "chunk.vert", """
|
||||
#version 330 core
|
||||
uniform mat4 u_ModelViewProjectionMatrix;
|
||||
uvec3 unpack(uint p) {
|
||||
return uvec3(p) >> uvec3(5u,0u,2u) & uvec3(7u,3u,7u);
|
||||
}
|
||||
vec3 translation(uint p) {
|
||||
return unpack(p) * vec3(16.0);
|
||||
}
|
||||
void main() {
|
||||
gl_Position = u_ModelViewProjectionMatrix * vec4(translation(0u), 1.0);
|
||||
}
|
||||
""",
|
||||
new String[0], new String[0], new String[] { "unpack(p) * vec3" }),
|
||||
Arguments.of("global init must be hoisted or inlined by the SPIR-V round-trip", GL20.GL_VERTEX_SHADER, "botania_fog.vert", """
|
||||
#version 330 core
|
||||
struct Fog { vec4 color; float density; };
|
||||
uniform vec4 fogColor;
|
||||
uniform float fogDensity;
|
||||
Fog fog = Fog(fogColor, fogDensity);
|
||||
void main() {
|
||||
gl_Position = vec4(fog.density) + fog.color;
|
||||
}
|
||||
""",
|
||||
new String[0], new String[0], new String[] { "Fog fog = Fog(fogColor, fogDensity);" }),
|
||||
Arguments.of("uniform bool round-trips as bool, not uint", GL20.GL_FRAGMENT_SHADER, "cloud.frag", """
|
||||
#version 330 core
|
||||
uniform bool u_FogEnabled;
|
||||
in vec4 color;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
vec4 c = color;
|
||||
if (u_FogEnabled) {
|
||||
c.a *= 0.5;
|
||||
}
|
||||
fragColor = c;
|
||||
}
|
||||
""",
|
||||
new String[] { "bool u_FogEnabled" }, new String[0], new String[] { "uniform uint u_FogEnabled", "uniform highp uint u_FogEnabled", "uniform mediump uint u_FogEnabled" }),
|
||||
Arguments.of("redefined builtin translates", GL20.GL_FRAGMENT_SHADER, "redefines_clamp.frag", SpirvCompilerDiskCacheTest.REDEFINES_CLAMP,
|
||||
new String[0], new String[0], new String[0]),
|
||||
Arguments.of("uniform bool with initializer round-trips as bool", GL20.GL_FRAGMENT_SHADER, "iris_common_bool_init.frag", """
|
||||
#version 330 core
|
||||
uniform bool isRightHanded = true;
|
||||
uniform bool heavyFog = false;
|
||||
uniform bool firstPersonCamera = true;
|
||||
in vec4 color;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
float alpha = 1.0;
|
||||
if (isRightHanded) alpha *= 0.9;
|
||||
if (heavyFog) alpha *= 0.8;
|
||||
if (firstPersonCamera) alpha *= 0.95;
|
||||
fragColor = vec4(color.rgb, color.a * alpha);
|
||||
}
|
||||
""",
|
||||
new String[] { "bool isRightHanded", "bool heavyFog", "bool firstPersonCamera" }, new String[0], new String[] { "uniform uint isRightHanded", "uniform highp uint isRightHanded", "uniform mediump uint isRightHanded" }),
|
||||
Arguments.of("Vulkan-reserved sampler identifier is renamed before shaderc and sampler2D is untouched", GL20.GL_FRAGMENT_SHADER, "fontFilter.frag", """
|
||||
#version 330 core
|
||||
uniform sampler2D sampler;
|
||||
in vec2 texCoord;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = texture(sampler, texCoord);
|
||||
}
|
||||
""",
|
||||
new String[0], new String[] { "angelica_sampler_renamed", "sampler2D" }, new String[0]),
|
||||
Arguments.of("C++ keywords as identifiers are renamed and the not() builtin is untouched", GL20.GL_FRAGMENT_SHADER, "reserved.frag", """
|
||||
#version 330 core
|
||||
in vec2 texCoord;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
vec4 try = vec4(texCoord, 0.0, 1.0);
|
||||
float catch = try.x;
|
||||
bvec2 flipped = not(lessThan(texCoord, vec2(0.5)));
|
||||
fragColor = flipped.x ? try : vec4(catch);
|
||||
}
|
||||
""",
|
||||
new String[0], new String[] { "angelica_renamed_try", "angelica_renamed_catch", "not(" }, new String[] { "angelica_renamed_not" }),
|
||||
Arguments.of("multi-declarator bool uniforms are all restored", GL20.GL_FRAGMENT_SHADER, "multi_bool.frag", """
|
||||
#version 330 core
|
||||
uniform bool a, b, c;
|
||||
in vec4 color;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
float alpha = 1.0;
|
||||
if (a) alpha *= 0.9;
|
||||
if (b) alpha *= 0.8;
|
||||
if (c) alpha *= 0.7;
|
||||
fragColor = vec4(color.rgb, color.a * alpha);
|
||||
}
|
||||
""",
|
||||
new String[] { "bool a", "bool b", "bool c" }, new String[0], new String[] { "uniform uint", "uniform highp uint", "uniform mediump uint" }),
|
||||
Arguments.of("compound bool expression rewrites both branches", GL20.GL_FRAGMENT_SHADER, "compound_bool.frag", """
|
||||
#version 330 core
|
||||
uniform bool a;
|
||||
uniform bool b;
|
||||
in vec4 color;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
vec4 c = color;
|
||||
if (a && b) { c.a *= 0.5; }
|
||||
fragColor = c;
|
||||
}
|
||||
""",
|
||||
new String[0], new String[0], new String[] { "!= 0u", "uniform uint" }));
|
||||
}
|
||||
|
||||
final String es = SpirvShaderTranslator.glslToGlslEs(src, GL20.GL_VERTEX_SHADER, "selection_box.vert");
|
||||
dumpOnFailure("selection_box.vert", src, es);
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource
|
||||
void translates(String label, int stage, String file, String src, String[] uniformDecls, String[] present, String[] absent) {
|
||||
final String es = SpirvShaderTranslator.glslToGlslEs(src, stage, file);
|
||||
dumpOnFailure(file, src, es);
|
||||
|
||||
assertNotNull(es);
|
||||
assertTrue(es.startsWith("#version 320 es"));
|
||||
assertContainsDecl(es, "mat4", "u_MVP");
|
||||
assertContainsDecl(es, "vec3", "u_Min");
|
||||
assertContainsDecl(es, "vec3", "u_Max");
|
||||
assertFalse(es.contains("_RESERVED_IDENTIFIER_FIXUP"), "uniforms must not be wrapped in spvc's default-block struct");
|
||||
for (String decl : uniformDecls) assertContainsDecl(es, decl.substring(0, decl.indexOf(' ')), decl.substring(decl.indexOf(' ') + 1));
|
||||
for (String needle : present) assertTrue(compact(es).contains(compact(needle)), needle);
|
||||
for (String needle : absent) assertFalse(compact(es).contains(compact(needle)), needle);
|
||||
}
|
||||
|
||||
@Test
|
||||
void selectionBoxFs_producesNamedUniformAndLocationedFragOutput() {
|
||||
final String src = """
|
||||
#version 330 core
|
||||
uniform vec4 u_Color;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = u_Color;
|
||||
}
|
||||
""";
|
||||
|
||||
final String es = SpirvShaderTranslator.glslToGlslEs(src, GL20.GL_FRAGMENT_SHADER, "selection_box.frag");
|
||||
dumpOnFailure("selection_box.frag", src, es);
|
||||
|
||||
assertNotNull(es);
|
||||
assertContainsDecl(es, "vec4", "u_Color");
|
||||
assertTrue(es.contains("layout(location = 0)") || es.contains("layout(location=0)"), "fragment output must have layout(location=0)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void celeritasChunk_impliedUvecVecPromotion_becomesExplicit() {
|
||||
final String src = """
|
||||
#version 330 core
|
||||
uniform mat4 u_ModelViewProjectionMatrix;
|
||||
uvec3 unpack(uint p) {
|
||||
return uvec3(p) >> uvec3(5u,0u,2u) & uvec3(7u,3u,7u);
|
||||
}
|
||||
vec3 translation(uint p) {
|
||||
return unpack(p) * vec3(16.0);
|
||||
}
|
||||
void main() {
|
||||
gl_Position = u_ModelViewProjectionMatrix * vec4(translation(0u), 1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
final String es = SpirvShaderTranslator.glslToGlslEs(src, GL20.GL_VERTEX_SHADER, "chunk.vert");
|
||||
dumpOnFailure("chunk.vert", src, es);
|
||||
|
||||
assertNotNull(es);
|
||||
assertFalse(es.contains("unpack(p) * vec3"), "uvec3 * vec3 must be made explicit by the SPIR-V round-trip");
|
||||
}
|
||||
|
||||
@Test
|
||||
void globalInitReferencingUniforms_survivesRoundTrip() {
|
||||
final String src = """
|
||||
#version 330 core
|
||||
struct Fog { vec4 color; float density; };
|
||||
uniform vec4 fogColor;
|
||||
uniform float fogDensity;
|
||||
Fog fog = Fog(fogColor, fogDensity);
|
||||
void main() {
|
||||
gl_Position = vec4(fog.density) + fog.color;
|
||||
}
|
||||
""";
|
||||
|
||||
final String es = SpirvShaderTranslator.glslToGlslEs(src, GL20.GL_VERTEX_SHADER, "botania_fog.vert");
|
||||
dumpOnFailure("botania_fog.vert", src, es);
|
||||
|
||||
assertNotNull(es);
|
||||
assertFalse(es.contains("Fog fog = Fog(fogColor, fogDensity);"), "global init must be hoisted / inlined by the SPIR-V round-trip");
|
||||
}
|
||||
|
||||
@Test
|
||||
void uniformBool_roundTripsAsBool_notUint() {
|
||||
final String src = """
|
||||
#version 330 core
|
||||
uniform bool u_FogEnabled;
|
||||
in vec4 color;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
vec4 c = color;
|
||||
if (u_FogEnabled) {
|
||||
c.a *= 0.5;
|
||||
static Stream<Arguments> varyingLocations() {
|
||||
return Stream.of(
|
||||
Arguments.of("varyings stripped from VS outputs, kept on attributes", GL20.GL_VERTEX_SHADER, "strip.vert", """
|
||||
#version 330 core
|
||||
layout(location = 0) in vec3 a_Position;
|
||||
layout(location = 1) in vec4 a_Color;
|
||||
uniform mat4 u_MVP;
|
||||
out vec4 v_Color;
|
||||
out vec2 v_TexCoord;
|
||||
void main() {
|
||||
v_Color = a_Color;
|
||||
v_TexCoord = a_Position.xy;
|
||||
gl_Position = u_MVP * vec4(a_Position, 1.0);
|
||||
}
|
||||
fragColor = c;
|
||||
}
|
||||
""";
|
||||
|
||||
final String es = SpirvShaderTranslator.glslToGlslEs(src, GL20.GL_FRAGMENT_SHADER, "cloud.frag");
|
||||
dumpOnFailure("cloud.frag (bool uniform)", src, es);
|
||||
|
||||
assertNotNull(es);
|
||||
assertContainsDecl(es, "bool", "u_FogEnabled");
|
||||
assertFalse(es.contains("uniform uint u_FogEnabled") || es.contains("uniform highp uint u_FogEnabled") || es.contains("uniform mediump uint u_FogEnabled"),
|
||||
"emitted uniform must not be `uint` - `glUniform1i` only accepts int/bool uniforms");
|
||||
}
|
||||
|
||||
@Test
|
||||
void redefinedBuiltin_translates() {
|
||||
assertNotNull(SpirvShaderTranslator.glslToGlslEs(SpirvCompilerDiskCacheTest.REDEFINES_CLAMP, GL20.GL_FRAGMENT_SHADER, "redefines_clamp.frag"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void uniformBoolWithInitializer_roundTripsAsBool() {
|
||||
final String src = """
|
||||
#version 330 core
|
||||
uniform bool isRightHanded = true;
|
||||
uniform bool heavyFog = false;
|
||||
uniform bool firstPersonCamera = true;
|
||||
in vec4 color;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
float alpha = 1.0;
|
||||
if (isRightHanded) alpha *= 0.9;
|
||||
if (heavyFog) alpha *= 0.8;
|
||||
if (firstPersonCamera) alpha *= 0.95;
|
||||
fragColor = vec4(color.rgb, color.a * alpha);
|
||||
}
|
||||
""";
|
||||
|
||||
final String es = SpirvShaderTranslator.glslToGlslEs(src, GL20.GL_FRAGMENT_SHADER, "iris_common_bool_init.frag");
|
||||
dumpOnFailure("iris_common_bool_init.frag", src, es);
|
||||
|
||||
assertNotNull(es);
|
||||
assertContainsDecl(es, "bool", "isRightHanded");
|
||||
assertContainsDecl(es, "bool", "heavyFog");
|
||||
assertContainsDecl(es, "bool", "firstPersonCamera");
|
||||
assertFalse(es.contains("uniform uint isRightHanded") || es.contains("uniform highp uint isRightHanded") || es.contains("uniform mediump uint isRightHanded"),
|
||||
"isRightHanded must not be emitted as uint\n\n" + es);
|
||||
}
|
||||
|
||||
@Test
|
||||
void varyingLocations_strippedFromVsOutAndFsIn_keptOnAttributesAndMrtOutputs() {
|
||||
final String vs = """
|
||||
#version 330 core
|
||||
layout(location = 0) in vec3 a_Position;
|
||||
layout(location = 1) in vec4 a_Color;
|
||||
uniform mat4 u_MVP;
|
||||
out vec4 v_Color;
|
||||
out vec2 v_TexCoord;
|
||||
void main() {
|
||||
v_Color = a_Color;
|
||||
v_TexCoord = a_Position.xy;
|
||||
gl_Position = u_MVP * vec4(a_Position, 1.0);
|
||||
}
|
||||
""";
|
||||
final String fs = """
|
||||
#version 330 core
|
||||
in vec2 v_TexCoord;
|
||||
in vec4 v_Color;
|
||||
uniform sampler2D u_Tex;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = v_Color * texture(u_Tex, v_TexCoord);
|
||||
}
|
||||
""";
|
||||
|
||||
final String esVs = SpirvShaderTranslator.glslToGlslEs(vs, GL20.GL_VERTEX_SHADER, "strip.vert");
|
||||
final String esFs = SpirvShaderTranslator.glslToGlslEs(fs, GL20.GL_FRAGMENT_SHADER, "strip.frag");
|
||||
dumpOnFailure("strip.vert", vs, esVs);
|
||||
dumpOnFailure("strip.frag", fs, esFs);
|
||||
|
||||
assertNotNull(esVs);
|
||||
assertNotNull(esFs);
|
||||
|
||||
assertFalse(hasLayoutLocation(esVs, "v_Color"));
|
||||
assertFalse(hasLayoutLocation(esVs, "v_TexCoord"));
|
||||
assertFalse(hasLayoutLocation(esFs, "v_Color"));
|
||||
assertFalse(hasLayoutLocation(esFs, "v_TexCoord"));
|
||||
assertTrue(hasLayoutLocation(esVs, "a_Position"));
|
||||
assertTrue(hasLayoutLocation(esVs, "a_Color"));
|
||||
assertTrue(hasLayoutLocation(esFs, "fragColor"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void varyingLocations_strippedFromGeometryShaderInAndOut() {
|
||||
final String gs = """
|
||||
#version 330 core
|
||||
layout(triangles) in;
|
||||
layout(triangle_strip, max_vertices = 3) out;
|
||||
in vec4 v_Color[];
|
||||
in vec2 v_TexCoord[];
|
||||
out vec4 g_Color;
|
||||
out vec2 g_TexCoord;
|
||||
void main() {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
gl_Position = gl_in[i].gl_Position;
|
||||
g_Color = v_Color[i];
|
||||
g_TexCoord = v_TexCoord[i];
|
||||
EmitVertex();
|
||||
""",
|
||||
new String[] { "a_Position", "a_Color" }, new String[] { "v_Color", "v_TexCoord" }),
|
||||
Arguments.of("varyings stripped from FS inputs, kept on MRT outputs", GL20.GL_FRAGMENT_SHADER, "strip.frag", """
|
||||
#version 330 core
|
||||
in vec2 v_TexCoord;
|
||||
in vec4 v_Color;
|
||||
uniform sampler2D u_Tex;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = v_Color * texture(u_Tex, v_TexCoord);
|
||||
}
|
||||
EndPrimitive();
|
||||
}
|
||||
""";
|
||||
|
||||
final String esGs = SpirvShaderTranslator.glslToGlslEs(gs, GL32.GL_GEOMETRY_SHADER, "passthrough.geom");
|
||||
dumpOnFailure("passthrough.geom", gs, esGs);
|
||||
|
||||
assertNotNull(esGs);
|
||||
assertFalse(hasLayoutLocation(esGs, "v_Color"));
|
||||
assertFalse(hasLayoutLocation(esGs, "v_TexCoord"));
|
||||
assertFalse(hasLayoutLocation(esGs, "g_Color"));
|
||||
assertFalse(hasLayoutLocation(esGs, "g_TexCoord"));
|
||||
""",
|
||||
new String[] { "fragColor" }, new String[] { "v_Color", "v_TexCoord" }),
|
||||
Arguments.of("varyings stripped from geometry shader inputs and outputs", GL32.GL_GEOMETRY_SHADER, "passthrough.geom", """
|
||||
#version 330 core
|
||||
layout(triangles) in;
|
||||
layout(triangle_strip, max_vertices = 3) out;
|
||||
in vec4 v_Color[];
|
||||
in vec2 v_TexCoord[];
|
||||
out vec4 g_Color;
|
||||
out vec2 g_TexCoord;
|
||||
void main() {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
gl_Position = gl_in[i].gl_Position;
|
||||
g_Color = v_Color[i];
|
||||
g_TexCoord = v_TexCoord[i];
|
||||
EmitVertex();
|
||||
}
|
||||
EndPrimitive();
|
||||
}
|
||||
""",
|
||||
new String[0], new String[] { "v_Color", "v_TexCoord", "g_Color", "g_TexCoord" }),
|
||||
Arguments.of("VS inputs keep explicit layouts and strip auto-assigned ones", GL20.GL_VERTEX_SHADER, "gbuffers_terrain.vert", """
|
||||
#version 330 core
|
||||
layout(location = 0) in vec4 iris_Vertex;
|
||||
layout(location = 1) in vec4 iris_Color;
|
||||
layout(location = 2) in vec4 iris_MultiTexCoord0;
|
||||
layout(location = 3) in vec4 iris_MultiTexCoord1;
|
||||
layout(location = 4) in vec3 iris_Normal;
|
||||
in vec4 mc_Entity;
|
||||
in vec4 mc_midTexCoord;
|
||||
in vec4 at_midBlock;
|
||||
uniform mat4 iris_MVP;
|
||||
out vec4 v_Color;
|
||||
void main() {
|
||||
v_Color = iris_Color + mc_Entity + at_midBlock + mc_midTexCoord;
|
||||
gl_Position = iris_MVP * iris_Vertex;
|
||||
}
|
||||
""",
|
||||
new String[] { "iris_Vertex", "iris_Color", "iris_MultiTexCoord0", "iris_MultiTexCoord1", "iris_Normal" }, new String[] { "mc_Entity", "mc_midTexCoord", "at_midBlock" }));
|
||||
}
|
||||
|
||||
@Test
|
||||
void vsInputs_preserveExplicitLayouts_stripAutoAssignedOnes() {
|
||||
final String vs = """
|
||||
#version 330 core
|
||||
layout(location = 0) in vec4 iris_Vertex;
|
||||
layout(location = 1) in vec4 iris_Color;
|
||||
layout(location = 2) in vec4 iris_MultiTexCoord0;
|
||||
layout(location = 3) in vec4 iris_MultiTexCoord1;
|
||||
layout(location = 4) in vec3 iris_Normal;
|
||||
in vec4 mc_Entity;
|
||||
in vec4 mc_midTexCoord;
|
||||
in vec4 at_midBlock;
|
||||
uniform mat4 iris_MVP;
|
||||
out vec4 v_Color;
|
||||
void main() {
|
||||
v_Color = iris_Color + mc_Entity + at_midBlock + mc_midTexCoord;
|
||||
gl_Position = iris_MVP * iris_Vertex;
|
||||
}
|
||||
""";
|
||||
|
||||
final String es = SpirvShaderTranslator.glslToGlslEs(vs, GL20.GL_VERTEX_SHADER, "gbuffers_terrain.vert");
|
||||
dumpOnFailure("gbuffers_terrain.vert", vs, es);
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource
|
||||
void varyingLocations(String label, int stage, String file, String src, String[] withLocation, String[] withoutLocation) {
|
||||
final String es = SpirvShaderTranslator.glslToGlslEs(src, stage, file);
|
||||
dumpOnFailure(file, src, es);
|
||||
|
||||
assertNotNull(es);
|
||||
// Explicit iris_* layouts preserved.
|
||||
assertTrue(hasLayoutLocation(es, "iris_Vertex"), "iris_Vertex must keep its explicit layout");
|
||||
assertTrue(hasLayoutLocation(es, "iris_Color"), "iris_Color must keep its explicit layout");
|
||||
assertTrue(hasLayoutLocation(es, "iris_MultiTexCoord0"), "iris_MultiTexCoord0 must keep its explicit layout");
|
||||
assertTrue(hasLayoutLocation(es, "iris_MultiTexCoord1"), "iris_MultiTexCoord1 must keep its explicit layout");
|
||||
assertTrue(hasLayoutLocation(es, "iris_Normal"), "iris_Normal must keep its explicit layout");
|
||||
|
||||
assertFalse(hasLayoutLocation(es, "mc_Entity"), "mc_Entity layout must be stripped");
|
||||
assertFalse(hasLayoutLocation(es, "mc_midTexCoord"), "mc_midTexCoord layout must be stripped");
|
||||
assertFalse(hasLayoutLocation(es, "at_midBlock"), "at_midBlock layout must be stripped");
|
||||
for (String name : withLocation) assertTrue(hasLayoutLocation(es, name), name + " must keep its layout");
|
||||
for (String name : withoutLocation) assertFalse(hasLayoutLocation(es, name), name + " layout must be stripped");
|
||||
}
|
||||
|
||||
private static boolean hasLayoutLocation(String source, String name) {
|
||||
@@ -282,98 +251,6 @@ class SpirvShaderTranslatorTest {
|
||||
return p.matcher(source).find();
|
||||
}
|
||||
|
||||
@Test
|
||||
void vulkanReservedSamplerAsIdentifier_isRenamedBeforeShaderc() {
|
||||
final String src = """
|
||||
#version 330 core
|
||||
uniform sampler2D sampler;
|
||||
in vec2 texCoord;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = texture(sampler, texCoord);
|
||||
}
|
||||
""";
|
||||
|
||||
final String es = SpirvShaderTranslator.glslToGlslEs(src, GL20.GL_FRAGMENT_SHADER, "fontFilter.frag");
|
||||
dumpOnFailure("fontFilter.frag", src, es);
|
||||
|
||||
assertNotNull(es);
|
||||
assertTrue(es.contains("angelica_sampler_renamed"));
|
||||
assertTrue(es.contains("sampler2D"), "sampler2D type must not be touched by the rename");
|
||||
}
|
||||
|
||||
@Test
|
||||
void cppKeywordsAsIdentifiers_areRenamed() {
|
||||
final String src = """
|
||||
#version 330 core
|
||||
in vec2 texCoord;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
vec4 try = vec4(texCoord, 0.0, 1.0);
|
||||
float catch = try.x;
|
||||
bvec2 flipped = not(lessThan(texCoord, vec2(0.5)));
|
||||
fragColor = flipped.x ? try : vec4(catch);
|
||||
}
|
||||
""";
|
||||
|
||||
final String es = SpirvShaderTranslator.glslToGlslEs(src, GL20.GL_FRAGMENT_SHADER, "reserved.frag");
|
||||
dumpOnFailure("reserved.frag", src, es);
|
||||
|
||||
assertNotNull(es);
|
||||
assertTrue(es.contains("angelica_renamed_try"), es);
|
||||
assertTrue(es.contains("angelica_renamed_catch"), es);
|
||||
assertTrue(es.contains("not("), "the not() builtin must not be touched by the rename\n" + es);
|
||||
assertFalse(es.contains("angelica_renamed_not"), es);
|
||||
}
|
||||
|
||||
@Test
|
||||
void uniformBool_multiDeclarator_allRestored() {
|
||||
final String src = """
|
||||
#version 330 core
|
||||
uniform bool a, b, c;
|
||||
in vec4 color;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
float alpha = 1.0;
|
||||
if (a) alpha *= 0.9;
|
||||
if (b) alpha *= 0.8;
|
||||
if (c) alpha *= 0.7;
|
||||
fragColor = vec4(color.rgb, color.a * alpha);
|
||||
}
|
||||
""";
|
||||
final String es = SpirvShaderTranslator.glslToGlslEs(src, GL20.GL_FRAGMENT_SHADER, "multi_bool.frag");
|
||||
dumpOnFailure("multi_bool.frag", src, es);
|
||||
|
||||
assertNotNull(es);
|
||||
assertContainsDecl(es, "bool", "a");
|
||||
assertContainsDecl(es, "bool", "b");
|
||||
assertContainsDecl(es, "bool", "c");
|
||||
assertFalse(es.contains("uniform uint") || es.contains("uniform highp uint") || es.contains("uniform mediump uint"),
|
||||
"no bool uniform should leak through as uint");
|
||||
}
|
||||
|
||||
@Test
|
||||
void uniformBool_compoundExpression_bothBranchesRewritten() {
|
||||
final String src = """
|
||||
#version 330 core
|
||||
uniform bool a;
|
||||
uniform bool b;
|
||||
in vec4 color;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
vec4 c = color;
|
||||
if (a && b) { c.a *= 0.5; }
|
||||
fragColor = c;
|
||||
}
|
||||
""";
|
||||
final String es = SpirvShaderTranslator.glslToGlslEs(src, GL20.GL_FRAGMENT_SHADER, "compound_bool.frag");
|
||||
dumpOnFailure("compound_bool.frag", src, es);
|
||||
|
||||
assertNotNull(es);
|
||||
assertFalse(es.contains("!= 0u"), "no bool use site should still compare against 0u");
|
||||
assertFalse(es.contains("uniform uint"), "bool uniforms must not leak as uint");
|
||||
}
|
||||
|
||||
@Test
|
||||
void uniformBool_throughDefaultBlock_restoresWithoutLeakingMemberSyntax() {
|
||||
final String src = """
|
||||
@@ -424,6 +301,10 @@ class SpirvShaderTranslatorTest {
|
||||
assertFalse(es.contains("GL_EXT_samplerless_texture_functions"), "the samplerless extension is an SDL_GPU concern and is not valid to inject here\n\n" + es);
|
||||
}
|
||||
|
||||
private static String compact(String s) {
|
||||
return s.replaceAll("\\s+", "");
|
||||
}
|
||||
|
||||
private static void assertContainsDecl(String source, String type, String name) {
|
||||
final String needle1 = "uniform " + type + " " + name;
|
||||
final String needle2 = "uniform highp " + type + " " + name;
|
||||
|
||||
+58
-71
@@ -1,82 +1,69 @@
|
||||
package com.gtnewhorizons.angelica.glsm.shader;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
class UserFunctionRenameTest {
|
||||
|
||||
@Test
|
||||
void definitionForwardDeclarationAndCallsAreRenamed() {
|
||||
final String out = GlslVulkanPreprocess.renameUserFunctions("""
|
||||
#version 330 core
|
||||
float fade(float t);
|
||||
out vec4 color;
|
||||
void main() { color = vec4(fade(0.5)); }
|
||||
float fade(float t) { return t * t; }
|
||||
""");
|
||||
assertEquals("""
|
||||
#version 330 core
|
||||
float angelica_renamed_fade(float t);
|
||||
out vec4 color;
|
||||
void main() { color = vec4(angelica_renamed_fade(0.5)); }
|
||||
float angelica_renamed_fade(float t) { return t * t; }
|
||||
""", out);
|
||||
static Stream<Arguments> renames() {
|
||||
return Stream.of(
|
||||
Arguments.of("definition, forward declaration and calls are renamed", """
|
||||
#version 330 core
|
||||
float fade(float t);
|
||||
out vec4 color;
|
||||
void main() { color = vec4(fade(0.5)); }
|
||||
float fade(float t) { return t * t; }
|
||||
""", """
|
||||
#version 330 core
|
||||
float angelica_renamed_fade(float t);
|
||||
out vec4 color;
|
||||
void main() { color = vec4(angelica_renamed_fade(0.5)); }
|
||||
float angelica_renamed_fade(float t) { return t * t; }
|
||||
"""),
|
||||
Arguments.of("overloads of one name are renamed together", """
|
||||
#version 330 core
|
||||
out vec4 color;
|
||||
float clamp(float min, float f, float max) { return f; }
|
||||
vec2 clamp(vec2 f) { return vec2(clamp(0.0, f.x, 1.0), clamp(0.0, f.y, 1.0)); }
|
||||
void main() { color = vec4(clamp(vec2(0.5)), 0.0, 1.0); }
|
||||
""", """
|
||||
#version 330 core
|
||||
out vec4 color;
|
||||
float angelica_renamed_clamp(float min, float f, float max) { return f; }
|
||||
vec2 angelica_renamed_clamp(vec2 f) { return vec2(angelica_renamed_clamp(0.0, f.x, 1.0), angelica_renamed_clamp(0.0, f.y, 1.0)); }
|
||||
void main() { color = vec4(angelica_renamed_clamp(vec2(0.5)), 0.0, 1.0); }
|
||||
"""),
|
||||
Arguments.of("name used as macro argument is left alone", """
|
||||
#version 330 core
|
||||
#define SAT(fn, x) fn(0.0, x, 1.0)
|
||||
out vec4 color;
|
||||
float clamp(float min, float f, float max) { return f; }
|
||||
void main() { color = vec4(SAT(clamp, 0.5)); }
|
||||
""", null),
|
||||
Arguments.of("name used in macro body is left alone", """
|
||||
#version 330 core
|
||||
#define FADE(x) fade(x)
|
||||
out vec4 color;
|
||||
float fade(float t) { return t * t; }
|
||||
void main() { color = vec4(FADE(0.5)); }
|
||||
""", null),
|
||||
Arguments.of("name shared with a variable is left alone", """
|
||||
#version 330 core
|
||||
out vec4 color;
|
||||
float fade(float t) { return t * t; }
|
||||
void main() { float fade = 2.0; color = vec4(fade); }
|
||||
""", null),
|
||||
Arguments.of("shader with only main has nothing to rename", "#version 330 core\nout vec4 color;\nvoid main() { color = vec4(1.0); }\n", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void overloadsOfOneNameAreRenamedTogether() {
|
||||
final String out = GlslVulkanPreprocess.renameUserFunctions("""
|
||||
#version 330 core
|
||||
out vec4 color;
|
||||
float clamp(float min, float f, float max) { return f; }
|
||||
vec2 clamp(vec2 f) { return vec2(clamp(0.0, f.x, 1.0), clamp(0.0, f.y, 1.0)); }
|
||||
void main() { color = vec4(clamp(vec2(0.5)), 0.0, 1.0); }
|
||||
""");
|
||||
assertEquals("""
|
||||
#version 330 core
|
||||
out vec4 color;
|
||||
float angelica_renamed_clamp(float min, float f, float max) { return f; }
|
||||
vec2 angelica_renamed_clamp(vec2 f) { return vec2(angelica_renamed_clamp(0.0, f.x, 1.0), angelica_renamed_clamp(0.0, f.y, 1.0)); }
|
||||
void main() { color = vec4(angelica_renamed_clamp(vec2(0.5)), 0.0, 1.0); }
|
||||
""", out);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nameUsedAsMacroArgumentIsLeftAlone() {
|
||||
assertNull(GlslVulkanPreprocess.renameUserFunctions("""
|
||||
#version 330 core
|
||||
#define SAT(fn, x) fn(0.0, x, 1.0)
|
||||
out vec4 color;
|
||||
float clamp(float min, float f, float max) { return f; }
|
||||
void main() { color = vec4(SAT(clamp, 0.5)); }
|
||||
"""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nameUsedInMacroBodyIsLeftAlone() {
|
||||
assertNull(GlslVulkanPreprocess.renameUserFunctions("""
|
||||
#version 330 core
|
||||
#define FADE(x) fade(x)
|
||||
out vec4 color;
|
||||
float fade(float t) { return t * t; }
|
||||
void main() { color = vec4(FADE(0.5)); }
|
||||
"""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nameSharedWithVariableIsLeftAlone() {
|
||||
assertNull(GlslVulkanPreprocess.renameUserFunctions("""
|
||||
#version 330 core
|
||||
out vec4 color;
|
||||
float fade(float t) { return t * t; }
|
||||
void main() { float fade = 2.0; color = vec4(fade); }
|
||||
"""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shaderWithOnlyMainHasNothingToRename() {
|
||||
assertNull(GlslVulkanPreprocess.renameUserFunctions("#version 330 core\nout vec4 color;\nvoid main() { color = vec4(1.0); }\n"));
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource
|
||||
void renames(String label, String source, String expected) {
|
||||
assertEquals(expected, GlslVulkanPreprocess.renameUserFunctions(source));
|
||||
}
|
||||
}
|
||||
@@ -30,18 +30,6 @@ class FenceQueueTest {
|
||||
assertTrue(q.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void firstBytesTracksFirstId() {
|
||||
final FenceQueue q = new FenceQueue();
|
||||
q.enqueue(100L, 7);
|
||||
q.enqueue(200L, 9);
|
||||
assertEquals(100L, q.firstId());
|
||||
assertEquals(7, q.firstBytes());
|
||||
q.dequeue();
|
||||
assertEquals(200L, q.firstId());
|
||||
assertEquals(9, q.firstBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
void drainAndRefillKeepsBackingArray() {
|
||||
final FenceQueue q = new FenceQueue();
|
||||
|
||||
+25
-27
@@ -53,26 +53,6 @@ class StreamingBufferTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void persistent_uploadAlignment() {
|
||||
Assumptions.assumeTrue(RenderSystem.supportsBufferStorage(), "GL4.4+ required");
|
||||
|
||||
PersistentStreamingBuffer buf = PersistentStreamingBuffer.createOrNull(4096);
|
||||
assertNotNull(buf);
|
||||
try {
|
||||
ByteBuffer data = memAlloc(128);
|
||||
fillPattern(data);
|
||||
data.flip();
|
||||
|
||||
int firstVertex = buf.upload(data, 32);
|
||||
assertTrue(firstVertex >= 0, "Upload should succeed");
|
||||
assertEquals(0, firstVertex % 4, "firstVertex must be quad-aligned");
|
||||
memFree(data);
|
||||
} finally {
|
||||
buf.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void persistent_uploadAlignment_nonPow2Stride() {
|
||||
Assumptions.assumeTrue(RenderSystem.supportsBufferStorage(), "GL4.4+ required");
|
||||
@@ -142,7 +122,7 @@ class StreamingBufferTest {
|
||||
data1.flip();
|
||||
int v1 = buf.upload(data1, stride);
|
||||
assertTrue(v1 >= 0);
|
||||
buf.postDraw();
|
||||
buf.endFrame();
|
||||
memFree(data1);
|
||||
|
||||
// Wait for GPU to finish so fence is signaled
|
||||
@@ -162,7 +142,7 @@ class StreamingBufferTest {
|
||||
assertEquals((byte) ((i + 100) & 0xFF), mapped.get(offset2 + i), "Mismatch at byte " + i);
|
||||
}
|
||||
|
||||
buf.postDraw();
|
||||
buf.endFrame();
|
||||
memFree(data2);
|
||||
} finally {
|
||||
buf.destroy();
|
||||
@@ -182,14 +162,14 @@ class StreamingBufferTest {
|
||||
|
||||
buf.upload(data, 32);
|
||||
assertEquals(4096 - 1024, buf.getRemaining(), "upload consumes its bytes");
|
||||
buf.postDraw();
|
||||
buf.endFrame();
|
||||
|
||||
data.position(0).limit(512);
|
||||
buf.upload(data, 32);
|
||||
buf.postDraw();
|
||||
buf.endFrame();
|
||||
|
||||
GL11.glFinish();
|
||||
buf.postDraw();
|
||||
buf.endFrame();
|
||||
assertEquals(4096, buf.getRemaining(), "all fenced regions reclaimed after completion");
|
||||
memFree(data);
|
||||
} finally {
|
||||
@@ -224,7 +204,25 @@ class StreamingBufferTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void persistent_exhaustionReclaimsPendingWithoutPostDraw() {
|
||||
void persistent_thresholdFenceExcludesNewestReservation() {
|
||||
Assumptions.assumeTrue(RenderSystem.supportsBufferStorage(), "GL4.4+ required");
|
||||
|
||||
final GlStreamingRing ring = GlStreamingRing.create(GL15.GL_ARRAY_BUFFER, 1024);
|
||||
assertNotNull(ring);
|
||||
try {
|
||||
assertEquals(0, ring.reserve(256, 16));
|
||||
assertEquals(0, ring.fencesIssued(), "the caller has not drawn the reserved bytes yet");
|
||||
assertEquals(256, ring.reserve(16, 16));
|
||||
assertEquals(1, ring.fencesIssued(), "the next reservation fences earlier draws");
|
||||
ring.endFrame();
|
||||
assertEquals(2, ring.fencesIssued(), "the newest reservation still needs a fence after its draw");
|
||||
} finally {
|
||||
ring.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void persistent_exhaustionReclaimsPendingWithoutEndFrame() {
|
||||
Assumptions.assumeTrue(RenderSystem.supportsBufferStorage(), "GL4.4+ required");
|
||||
|
||||
PersistentStreamingBuffer buf = PersistentStreamingBuffer.createOrNull(1024);
|
||||
@@ -238,7 +236,7 @@ class StreamingBufferTest {
|
||||
data.position(0).limit(512);
|
||||
assertTrue(buf.upload(data, 32) >= 0);
|
||||
data.position(0).limit(512);
|
||||
assertTrue(buf.upload(data, 32) >= 0, "ring must self-fence and reclaim without postDraw");
|
||||
assertTrue(buf.upload(data, 32) >= 0, "ring must self-fence and reclaim without endFrame");
|
||||
memFree(data);
|
||||
} finally {
|
||||
buf.destroy();
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package com.gtnewhorizons.angelica.glsm.streaming;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.EntityIdsOnlyHandler;
|
||||
import com.gtnewhorizons.angelica.glsm.GLCompatTest;
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GlTestSupport;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.FfpFixture;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
|
||||
@GLCompatTest
|
||||
class TessellatorEntityIdsGLTest {
|
||||
|
||||
private static final int[] GREEN = { 0, 255, 0, 255 };
|
||||
private static final int[] BLUE = { 0, 0, 255, 255 };
|
||||
|
||||
private ImmediateExtendedAttribHandler previousHandler;
|
||||
private int program;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
previousHandler = EntityIdsOnlyHandler.install();
|
||||
program = GlTestSupport.program(TessellatorEntityIdsFixture.VERTEX, TessellatorEntityIdsFixture.FRAGMENT);
|
||||
GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
|
||||
GLStateManager.disableDepthTest();
|
||||
GLStateManager.disableCull();
|
||||
GLStateManager.glViewport(0, 0, 8, 8);
|
||||
GLStateManager.glUseProgram(program);
|
||||
GLStateManager.glClearColor(0, 0, 0, 1);
|
||||
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
GLStateManager.glUseProgram(0);
|
||||
GLStateManager.glDeleteProgram(program);
|
||||
GLStateManager.glPopAttrib();
|
||||
GLSMHooks.immediateExtendedHandler = previousHandler;
|
||||
}
|
||||
|
||||
@Test
|
||||
void idsChangedMidBatchApplyToTheVerticesAddedAfterTheChange() {
|
||||
TessellatorEntityIdsFixture.drawIdsChangedMidBatch();
|
||||
|
||||
assertColor(1, GREEN, "left quad was added under item 7");
|
||||
assertColor(6, BLUE, "right quad was added under item 50020");
|
||||
}
|
||||
|
||||
@Test
|
||||
void idsChangedAfterTheLastVertexLeaveTheBatchAlone() {
|
||||
TessellatorEntityIdsFixture.drawIdsChangedAfterTheLastVertex();
|
||||
|
||||
assertColor(1, GREEN, "every vertex was added under item 7");
|
||||
assertColor(6, GREEN, "every vertex was added under item 7");
|
||||
}
|
||||
|
||||
private static void assertColor(int x, int[] expected, String message) {
|
||||
final int[] pixel = FfpFixture.readPixel(x, 4);
|
||||
assertArrayEquals(expected, pixel, message + ", got " + Arrays.toString(pixel));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_ERR">
|
||||
<Console name="Console" target="SYSTEM_ERR" follow="true">
|
||||
<PatternLayout pattern="[%-5level] %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.gtnewhorizons.angelica.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
|
||||
import com.gtnewhorizons.angelica.glsm.hooks.ImmediateExtendedAttribHandler;
|
||||
|
||||
public final class EntityIdsOnlyHandler implements ImmediateExtendedAttribHandler {
|
||||
|
||||
public static ImmediateExtendedAttribHandler install() {
|
||||
final ImmediateExtendedAttribHandler previous = GLSMHooks.immediateExtendedHandler;
|
||||
GLSMHooks.immediateExtendedHandler = new EntityIdsOnlyHandler();
|
||||
GLStateManager.ctx().dirtyEntityIdsAttrib = true;
|
||||
return previous;
|
||||
}
|
||||
|
||||
@Override public boolean wantsExtended() { return false; }
|
||||
@Override public boolean wantsTangent() { return false; }
|
||||
@Override public boolean wantsEntityIds() { return GLStateManager.getActiveProgram() != 0; }
|
||||
@Override public boolean wantsExtendedCapture() { return false; }
|
||||
@Override public boolean wantsFaceNormals() { return false; }
|
||||
@Override public void writeFaceNormals(int[] rawBuffer, int vertexCount) {}
|
||||
@Override public void build(int[] rawBuffer, int vertexCount, int vertsPerPrim, int normalIntIndex, long dstAddr, int dstStride) {}
|
||||
@Override public void buildPacked(long srcBase, int stride, int posOffset, int texOffset, int normalOffset,
|
||||
int vertexCount, int vertsPerPrim, long dstAddr, int dstStride) {}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback,
|
||||
|
||||
@Override
|
||||
public void beforeAll(ExtensionContext context) throws LWJGLException {
|
||||
GlsmRedirectAgent.requireInstalled();
|
||||
if (startFailure != null) {
|
||||
throw new IllegalStateException("GLSMCoreExtension context setup failed earlier in this JVM", startFailure);
|
||||
}
|
||||
@@ -81,6 +82,7 @@ public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback,
|
||||
public void afterEach(ExtensionContext context) {
|
||||
final int error = GL11.glGetError();
|
||||
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
|
||||
GlsmRedirectAgent.assertNoFailures();
|
||||
assertEquals(GL11.GL_NO_ERROR, error, () -> "GL Error: 0x" + Integer.toHexString(error));
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ public class GLSMExtension implements BeforeAllCallback, BeforeEachCallback, Aft
|
||||
|
||||
@Override
|
||||
public void beforeAll(ExtensionContext context) throws LWJGLException {
|
||||
GlsmRedirectAgent.requireInstalled();
|
||||
if (startFailure != null) {
|
||||
throw new IllegalStateException("GLSMExtension context setup failed earlier in this JVM", startFailure);
|
||||
}
|
||||
@@ -179,6 +180,7 @@ public class GLSMExtension implements BeforeAllCallback, BeforeEachCallback, Aft
|
||||
fail("Test leaked an open display-list recorder! Missing glEndList.");
|
||||
}
|
||||
|
||||
GlsmRedirectAgent.assertNoFailures();
|
||||
final int error = GL11.glGetError();
|
||||
assertEquals(GL11.GL_NO_ERROR, error,
|
||||
() -> "GL Error: 0x" + Integer.toHexString(error));
|
||||
|
||||
Loaded 100 of 345 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user