mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-10-25 20:22:22 +00:00
Reduce test duplication (#2276)
This commit is contained in:
1 parent
b446968e66
commit
e98c927a93
112 files changed
+2524
-4069
No files matched your search
@@ -2663,6 +2663,13 @@ public class GLStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i <= glCtx.maxBoundImageUnit; i++) {
|
||||
final ImageUnitBinding image = glCtx.imageUnits.get(i);
|
||||
if (image != null && image.getTexture() == id) {
|
||||
RENDER_BACKEND.bindImageTexture(i, 0, 0, false, 0, GL15.GL_READ_ONLY, GL30.GL_R8);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore active unit, then shrink the texture via a temporary bind
|
||||
if (changedUnit) {
|
||||
RENDER_BACKEND.activeTexture(GL13.GL_TEXTURE0 + savedUnit);
|
||||
@@ -4357,7 +4364,7 @@ public class GLStateManager {
|
||||
for (int i = 0; i <= glCtx.maxBoundImageUnit; i++) {
|
||||
final ImageUnitBinding image = glCtx.imageUnits.get(i);
|
||||
if (image != null && image.getTexture() == id) {
|
||||
image.setBinding(0, 0, false, 0, 0, 0);
|
||||
image.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8711,7 +8718,7 @@ public class GLStateManager {
|
||||
if (binding == null) {
|
||||
warnOnce("imageUnitRange", "glBindImageTexture unit {} exceeds GL_MAX_IMAGE_UNITS {}", unit, glCtx.imageUnits.size());
|
||||
} else {
|
||||
binding.setBinding(texture, level, layered, layer, access, format);
|
||||
binding.setBinding(texture, level, texture != 0 && layered, texture != 0 ? layer : 0, access, format);
|
||||
if (texture != 0 && unit > glCtx.maxBoundImageUnit) glCtx.maxBoundImageUnit = unit;
|
||||
}
|
||||
if (access != GL15.GL_READ_ONLY) TextureInfoCache.INSTANCE.onWrittenOutsideUploads(texture);
|
||||
|
||||
@@ -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;
|
||||
|
||||
+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 {
|
||||
|
||||
+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
|
||||
|
||||
@@ -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
|
||||
@@ -131,64 +135,67 @@ public class GLSM_VAO_UnitTest {
|
||||
|
||||
@Test
|
||||
void testGenericAttributeSupersedesFfpConventional() {
|
||||
final int vao = UniversalVAO.genVertexArrays();
|
||||
UniversalVAO.bindVertexArray(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);
|
||||
UniversalVAO.bindVertexArray(0);
|
||||
GLStateManager.glDeleteBuffers(vbo);
|
||||
UniversalVAO.deleteVertexArrays(vao);
|
||||
GLStateManager.glDisableVertexAttribArray(0);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenericAttributeWinsRegardlessOfOrder() {
|
||||
final int vao = UniversalVAO.genVertexArrays();
|
||||
UniversalVAO.bindVertexArray(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);
|
||||
UniversalVAO.bindVertexArray(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();
|
||||
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);
|
||||
UniversalVAO.bindVertexArray(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() {
|
||||
|
||||
+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({
|
||||
"mailbox is taken when the backend offers it, , true, MAILBOX",
|
||||
"vsync is the fallback 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, MAILBOX",
|
||||
"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
|
||||
|
||||
@@ -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();
|
||||
|
||||
+19
-51
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -39,8 +39,7 @@ class WideLineClipGLTest {
|
||||
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();
|
||||
|
||||
+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);
|
||||
|
||||
+110
-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,101 @@ 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/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",
|
||||
})
|
||||
void aliasIsRedirected(String owner, String name, String desc, String expectedName) {
|
||||
assertEquals(desc, assertRedirected(owner, name, desc, 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",
|
||||
})
|
||||
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 +333,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 +377,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 +386,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 +394,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 +412,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();
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -224,6 +224,11 @@ public final class FfpFixture {
|
||||
assertTrue(coveredPixels > base.length / 8, "base draw covered only " + coveredPixels + " of " + base.length + " pixels");
|
||||
}
|
||||
|
||||
public static void activateFfp() {
|
||||
ShaderManager.enable();
|
||||
ShaderManager.getInstance().activate();
|
||||
}
|
||||
|
||||
public static void resetFfpState() {
|
||||
GLStateManager.setFfpInstancing(Instancing.NONE);
|
||||
final ShaderManager sm = ShaderManager.getInstance();
|
||||
|
||||
@@ -1379,6 +1379,37 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
resourceManager.markBufferContentsDefined(dstGlId);
|
||||
readbackShadows.invalidate(dstGlId, writeOffset);
|
||||
|
||||
final ByteBuffer dstUboShadow = resourceManager.getUboShadow(dstGlId);
|
||||
if (dstUboShadow != null) {
|
||||
final ByteBuffer dstSlice = dstUboShadow.duplicate();
|
||||
dstSlice.position((int) writeOffset).limit((int) (writeOffset + size));
|
||||
if (!readUboStore(srcGlId, readOffset, dstSlice, (int) size)) {
|
||||
final long srcHandle = resourceManager.getBufferHandle(srcGlId);
|
||||
if (srcHandle != 0) downloadBufferBlocking(srcHandle, (int) readOffset, (int) size, dstSlice);
|
||||
}
|
||||
s().uboRangeGen++;
|
||||
return;
|
||||
}
|
||||
if (resourceManager.getUboShadow(srcGlId) != null) {
|
||||
final long dstHandle = resourceManager.getBufferHandle(dstGlId);
|
||||
if (dstHandle != 0) {
|
||||
final ByteBuffer buf = MemoryUtil.memAlloc((int) size);
|
||||
try {
|
||||
if (readUboStore(srcGlId, readOffset, buf, (int) size)) {
|
||||
buf.position(0).limit((int) size);
|
||||
if (s().deferUploads) {
|
||||
enqueuePreCopied(buf, dstHandle, writeOffset, false);
|
||||
} else if (frameManager.getCommandBuffer() != 0) {
|
||||
resourceManager.uploadToBuffer(frameManager.ensureCopyPass(), buf, dstHandle, writeOffset, false);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
MemoryUtil.memFree(buf);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (s().deferUploads) {
|
||||
final long dstHandle = resourceManager.getBufferHandle(dstGlId);
|
||||
if (dstHandle != 0) {
|
||||
@@ -2998,6 +3029,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
final ByteBuffer shadow = resourceManager.getOrAllocUboShadow(glId, (int) size);
|
||||
shadow.position(0).limit((int) size);
|
||||
for (int i = 0; i < (int) size; i += 8) shadow.putLong(i, 0L);
|
||||
cs.uboRangeGen++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -3053,6 +3085,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
shadow.position(0).limit((int) dataSize);
|
||||
shadow.put(src);
|
||||
shadow.position(0).limit((int) dataSize);
|
||||
cs.uboRangeGen++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -3138,12 +3171,13 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
if (target == GL31.GL_UNIFORM_BUFFER) {
|
||||
final ByteBuffer shadow = resourceManager.getUboShadow(glId);
|
||||
if (shadow != null) {
|
||||
if (offset < 0 || offset + data.remaining() > shadow.capacity()) {
|
||||
if (offset < 0 || offset + data.remaining() > resourceManager.getBufferSize(glId)) {
|
||||
warnInvalidBufferOp("glBufferSubData out of range on uniform buffer", glId);
|
||||
return;
|
||||
}
|
||||
shadow.position(0);
|
||||
ByteRegionCopy.copyByteRegion(data, data.position(), shadow, (int) offset, data.remaining());
|
||||
cs.uboRangeGen++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -3252,6 +3286,7 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
shadow.put(staging);
|
||||
shadow.position(0).limit(shadow.capacity());
|
||||
resourceManager.markBufferContentsDefined(glId);
|
||||
cs.uboRangeGen++;
|
||||
}
|
||||
MemoryUtil.memFree(staging);
|
||||
return true;
|
||||
@@ -3340,6 +3375,10 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
|
||||
private void seedMappedStaging(int target, int glId, long offset, long length, ByteBuffer staging) {
|
||||
final int len = (int) length;
|
||||
if (readUboStore(glId, offset, staging, len)) {
|
||||
staging.position(0).limit(len);
|
||||
return;
|
||||
}
|
||||
final long handle = resourceManager.getBufferHandle(glId);
|
||||
|
||||
if (handle == 0 || resourceManager.hasUndefinedContents(glId)) {
|
||||
@@ -3348,7 +3387,6 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
}
|
||||
|
||||
final ByteBuffer direct = switch (target) {
|
||||
case GL31.GL_UNIFORM_BUFFER -> resourceManager.getUboShadow(glId);
|
||||
case GL30.GL_PIXEL_UNPACK_BUFFER -> resourceManager.getPboStaging(glId);
|
||||
default -> null;
|
||||
};
|
||||
@@ -3452,9 +3490,10 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
@Override public void getBufferSubData(int target, long offset, ByteBuffer data) {
|
||||
final int glId = getBoundBuffer(target);
|
||||
if (glId == 0 || data == null) return;
|
||||
final int rem = data.remaining();
|
||||
if (readUboStore(glId, offset, data, rem)) return;
|
||||
final long handle = resourceManager.getBufferHandle(glId);
|
||||
if (handle == 0) return;
|
||||
final int rem = data.remaining();
|
||||
|
||||
if (readbackShadows.serve(glId, offset, data, rem)) return;
|
||||
if (readBufferRangeCpuFirst(target, glId, handle, offset, data, rem)) return;
|
||||
@@ -3462,6 +3501,19 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
downloadBufferBlocking(handle, (int) offset, rem, data);
|
||||
}
|
||||
|
||||
private boolean readUboStore(int glId, long offset, ByteBuffer dst, int len) {
|
||||
final ByteBuffer shadow = resourceManager.getUboShadow(glId);
|
||||
if (shadow == null) return false;
|
||||
final PersistentMapping pm = resourceManager.getPersistentMapping(glId);
|
||||
if (pm != null && pm.covers(offset, len)) {
|
||||
serveFromShadow(pm.staging, (int) pm.stagingIndex(offset), dst, len);
|
||||
return true;
|
||||
}
|
||||
if (offset < 0 || offset + len > resourceManager.getBufferSize(glId)) return false;
|
||||
serveFromShadow(shadow, (int) offset, dst, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void downloadBufferBlocking(long handle, int offset, int size, ByteBuffer out) {
|
||||
if (size <= 0) return;
|
||||
flushAndSubmitMidFrame();
|
||||
@@ -4237,10 +4289,19 @@ public class SDLGPURenderBackend extends RenderBackend {
|
||||
warnInvalidBufferOp("glClearBufferSubData out of range", glId);
|
||||
return;
|
||||
}
|
||||
final int elemBytes = Math.max(1, PixelOps.glPixelSize(format, type));
|
||||
final ByteBuffer uboShadow = resourceManager.getUboShadow(glId);
|
||||
if (uboShadow != null) {
|
||||
for (int i = 0; i < (int) size; i++) {
|
||||
final int e = i % elemBytes;
|
||||
uboShadow.put((int) offset + i, data != null && e < data.remaining() ? data.get(data.position() + e) : 0);
|
||||
}
|
||||
s().uboRangeGen++;
|
||||
return;
|
||||
}
|
||||
final long handle = resourceManager.getBufferHandle(glId);
|
||||
if (handle == 0 || frameManager.getCommandBuffer() == 0) return;
|
||||
|
||||
final int elemBytes = Math.max(1, PixelOps.glPixelSize(format, type));
|
||||
final boolean defer = s().deferUploads && target != GL43.GL_SHADER_STORAGE_BUFFER;
|
||||
// Undeferred clears stage one large pattern and upload it repeatedly
|
||||
final int chunkBytes = (int) Math.min(size, defer ? CLEAR_CHUNK_BYTES : FILL_CHUNK_BYTES);
|
||||
|
||||
@@ -135,15 +135,6 @@ class PresenterTest {
|
||||
assertEquals(before, fm.registeredFrameCount(), "the splash present must not touch the calling thread's frame");
|
||||
}
|
||||
|
||||
@Test
|
||||
void tryRequestEnqueuesWhenIdle() {
|
||||
final DeferredExecutor executor = new DeferredExecutor();
|
||||
final Presenter p = presenter(executor);
|
||||
|
||||
assertTrue(p.tryRequestPresent(1L, 10, 10, SDL_FLIP_NONE));
|
||||
assertEquals(1, executor.tasks.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tryRequestDropsWithoutBlockingWhileAPresentIsPending() {
|
||||
final DeferredExecutor executor = new DeferredExecutor();
|
||||
|
||||
+74
-11
@@ -7,6 +7,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
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 org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
@@ -181,6 +182,7 @@ class GlsmSdlBufferMappingLifetimeTest {
|
||||
final int ubo = GLStateManager.glGenBuffers();
|
||||
final ByteBuffer data = MemoryUtil.memAlloc(256);
|
||||
final ByteBuffer big = MemoryUtil.memAlloc(64);
|
||||
ByteBuffer out = null;
|
||||
try {
|
||||
fill(data, 6);
|
||||
fill(big, 7);
|
||||
@@ -194,13 +196,73 @@ class GlsmSdlBufferMappingLifetimeTest {
|
||||
big.clear();
|
||||
GLStateManager.glBufferSubData(GL31.GL_UNIFORM_BUFFER, -8, big);
|
||||
});
|
||||
out = readBack(GL31.GL_UNIFORM_BUFFER, 256);
|
||||
assertPattern(out, 6);
|
||||
} finally {
|
||||
if (out != null) MemoryUtil.memFree(out);
|
||||
MemoryUtil.memFree(data);
|
||||
MemoryUtil.memFree(big);
|
||||
GLStateManager.glDeleteBuffers(ubo);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void uniformBufferMapForReadSeesItsContents() {
|
||||
final int ubo = GLStateManager.glGenBuffers();
|
||||
final ByteBuffer data = MemoryUtil.memAlloc(256);
|
||||
try {
|
||||
fill(data, 7);
|
||||
GLStateManager.glBindBuffer(GL31.GL_UNIFORM_BUFFER, ubo);
|
||||
GLStateManager.glBufferData(GL31.GL_UNIFORM_BUFFER, data, GL15.GL_DYNAMIC_DRAW);
|
||||
final ByteBuffer mapped = GLStateManager.glMapBufferRange(GL31.GL_UNIFORM_BUFFER, 0, 256, GL30.GL_MAP_READ_BIT);
|
||||
assertNotNull(mapped);
|
||||
assertPattern(mapped, 7);
|
||||
assertTrue(GLStateManager.glUnmapBuffer(GL31.GL_UNIFORM_BUFFER));
|
||||
} finally {
|
||||
MemoryUtil.memFree(data);
|
||||
GLStateManager.glDeleteBuffers(ubo);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyBufferSubDataMovesBytesIntoAndOutOfAUniformBuffer() {
|
||||
final int src = GLStateManager.glGenBuffers();
|
||||
final int ubo = GLStateManager.glGenBuffers();
|
||||
final int dst = GLStateManager.glGenBuffers();
|
||||
final ByteBuffer srcData = MemoryUtil.memAlloc(256);
|
||||
final ByteBuffer uboData = MemoryUtil.memAlloc(256);
|
||||
ByteBuffer outUbo = null;
|
||||
ByteBuffer outDst = null;
|
||||
try {
|
||||
fill(srcData, 5);
|
||||
fill(uboData, 6);
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_READ_BUFFER, src);
|
||||
GLStateManager.glBufferData(GL31.GL_COPY_READ_BUFFER, srcData, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glBindBuffer(GL31.GL_UNIFORM_BUFFER, ubo);
|
||||
GLStateManager.glBufferData(GL31.GL_UNIFORM_BUFFER, uboData, GL15.GL_DYNAMIC_DRAW);
|
||||
GLStateManager.glCopyBufferSubData(GL31.GL_COPY_READ_BUFFER, GL31.GL_UNIFORM_BUFFER, 64, 64, 64);
|
||||
outUbo = readBack(GL31.GL_UNIFORM_BUFFER, 256);
|
||||
for (int i = 0; i < 256; i++) {
|
||||
final boolean copied = i >= 64 && i < 128;
|
||||
assertEquals(copied ? srcData.get(i) : uboData.get(i), outUbo.get(i), (copied ? "copied" : "untouched") + " byte " + i);
|
||||
}
|
||||
|
||||
GLStateManager.glBindBuffer(GL31.GL_COPY_WRITE_BUFFER, dst);
|
||||
GLStateManager.glBufferData(GL31.GL_COPY_WRITE_BUFFER, 64L, GL15.GL_STATIC_DRAW);
|
||||
GLStateManager.glCopyBufferSubData(GL31.GL_UNIFORM_BUFFER, GL31.GL_COPY_WRITE_BUFFER, 0, 0, 64);
|
||||
outDst = readBack(GL31.GL_COPY_WRITE_BUFFER, 64);
|
||||
assertPattern(outDst, 6);
|
||||
} finally {
|
||||
if (outUbo != null) MemoryUtil.memFree(outUbo);
|
||||
if (outDst != null) MemoryUtil.memFree(outDst);
|
||||
MemoryUtil.memFree(srcData);
|
||||
MemoryUtil.memFree(uboData);
|
||||
GLStateManager.glDeleteBuffers(src);
|
||||
GLStateManager.glDeleteBuffers(ubo);
|
||||
GLStateManager.glDeleteBuffers(dst);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void outOfRangeCopyBufferSubDataIsIgnored() {
|
||||
final int src = GLStateManager.glGenBuffers();
|
||||
@@ -227,24 +289,25 @@ class GlsmSdlBufferMappingLifetimeTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void outOfRangeClearBufferSubDataIsIgnored() {
|
||||
@ParameterizedTest(name = "target {0}")
|
||||
@ValueSource(ints = {GL43.GL_SHADER_STORAGE_BUFFER, GL31.GL_UNIFORM_BUFFER})
|
||||
void outOfRangeClearBufferSubDataIsIgnored(int target) {
|
||||
final int clearStart = 64;
|
||||
final int clearEnd = 96;
|
||||
final int ssbo = GLStateManager.glGenBuffers();
|
||||
final int buffer = GLStateManager.glGenBuffers();
|
||||
final ByteBuffer data = MemoryUtil.memAlloc(256);
|
||||
ByteBuffer out = null;
|
||||
try {
|
||||
fill(data, 8);
|
||||
GLStateManager.glBindBuffer(GL43.GL_SHADER_STORAGE_BUFFER, ssbo);
|
||||
GLStateManager.glBufferData(GL43.GL_SHADER_STORAGE_BUFFER, data, GL15.GL_DYNAMIC_DRAW);
|
||||
GLStateManager.glClearBufferSubData(GL43.GL_SHADER_STORAGE_BUFFER, GL30.GL_R8, clearStart, clearEnd - clearStart, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, null);
|
||||
GLStateManager.glClearBufferSubData(GL43.GL_SHADER_STORAGE_BUFFER, GL30.GL_R8, 200, 128, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, null);
|
||||
GLStateManager.glClearBufferSubData(GL43.GL_SHADER_STORAGE_BUFFER, GL30.GL_R8, -4, 16, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, null);
|
||||
GLStateManager.glClearBufferSubData(GL43.GL_SHADER_STORAGE_BUFFER, GL30.GL_R8, 1024, 16, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, null);
|
||||
GLStateManager.glBindBuffer(target, buffer);
|
||||
GLStateManager.glBufferData(target, data, GL15.GL_DYNAMIC_DRAW);
|
||||
GLStateManager.glClearBufferSubData(target, GL30.GL_R8, clearStart, clearEnd - clearStart, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, null);
|
||||
GLStateManager.glClearBufferSubData(target, GL30.GL_R8, 200, 128, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, null);
|
||||
GLStateManager.glClearBufferSubData(target, GL30.GL_R8, -4, 16, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, null);
|
||||
GLStateManager.glClearBufferSubData(target, GL30.GL_R8, 1024, 16, GL11.GL_RED, GL11.GL_UNSIGNED_BYTE, null);
|
||||
GlsmSdlHeadlessRig.endFrame();
|
||||
GlsmSdlHeadlessRig.beginFrame();
|
||||
out = readBack(GL43.GL_SHADER_STORAGE_BUFFER, 256);
|
||||
out = readBack(target, 256);
|
||||
for (int i = 0; i < 256; i++) {
|
||||
final boolean cleared = i >= clearStart && i < clearEnd;
|
||||
assertEquals(cleared ? 0 : data.get(i), out.get(i), cleared ? "in-range clear did not zero byte " + i : "out-of-range clear touched byte " + i);
|
||||
@@ -252,7 +315,7 @@ class GlsmSdlBufferMappingLifetimeTest {
|
||||
} finally {
|
||||
if (out != null) MemoryUtil.memFree(out);
|
||||
MemoryUtil.memFree(data);
|
||||
GLStateManager.glDeleteBuffers(ssbo);
|
||||
GLStateManager.glDeleteBuffers(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.glsm;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||
import com.gtnewhorizons.angelica.glsm.GlTestSupport;
|
||||
import com.gtnewhorizons.angelica.glsm.ffp.LogicOpFixture;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL31;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.SIZE;
|
||||
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.describe;
|
||||
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.pixelAt;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@Tag("glsm-sdl")
|
||||
@ExtendWith(GlsmSdlFrameExtension.class)
|
||||
class GlsmSdlExternalUboPushTest {
|
||||
|
||||
private static final String VERT = """
|
||||
#version 330 core
|
||||
layout(location = 0) in vec3 a_Position;
|
||||
void main() {
|
||||
gl_Position = vec4(a_Position, 1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String FRAG = """
|
||||
#version 330 core
|
||||
layout(std140) uniform Tint {
|
||||
vec4 color;
|
||||
};
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = color;
|
||||
}
|
||||
""";
|
||||
|
||||
private static final int BINDING = 2;
|
||||
private static final int[] UNUSED_COLOR = LogicOpFixture.SOURCES[0];
|
||||
|
||||
private int program;
|
||||
private int ubo;
|
||||
|
||||
@AfterEach
|
||||
void cleanup() {
|
||||
GLStateManager.glUseProgram(0);
|
||||
GLStateManager.glBindBufferBase(GL31.GL_UNIFORM_BUFFER, BINDING, 0);
|
||||
if (ubo != 0) GLStateManager.glDeleteBuffers(ubo);
|
||||
if (program != 0) GLStateManager.glDeleteProgram(program);
|
||||
LogicOpFixture.deleteResources();
|
||||
}
|
||||
|
||||
@Test
|
||||
void subDataBetweenDrawsReachesTheSecondDraw() {
|
||||
program = GlTestSupport.program(VERT, FRAG);
|
||||
GLStateManager.glUniformBlockBinding(program, GLStateManager.glGetUniformBlockIndex(program, "Tint"), BINDING);
|
||||
|
||||
ubo = GLStateManager.glGenBuffers();
|
||||
GLStateManager.glBindBuffer(GL31.GL_UNIFORM_BUFFER, ubo);
|
||||
GLStateManager.glBufferData(GL31.GL_UNIFORM_BUFFER, color(1f, 0f, 0f), GL15.GL_DYNAMIC_DRAW);
|
||||
GLStateManager.glBindBufferBase(GL31.GL_UNIFORM_BUFFER, BINDING, ubo);
|
||||
|
||||
GLStateManager.glUseProgram(program);
|
||||
LogicOpFixture.quad(UNUSED_COLOR);
|
||||
|
||||
GLStateManager.glBindBuffer(GL31.GL_UNIFORM_BUFFER, ubo);
|
||||
GLStateManager.glBufferSubData(GL31.GL_UNIFORM_BUFFER, 0, color(0f, 1f, 0f));
|
||||
LogicOpFixture.quad(UNUSED_COLOR);
|
||||
|
||||
final int got = pixelAt(GlsmSdlHeadlessRig.readTarget(), SIZE, SIZE / 2, SIZE / 2);
|
||||
assertEquals(0xFF00FF00, got, () -> "second draw must see the sub-data written after the first: " + describe(got));
|
||||
}
|
||||
|
||||
private static FloatBuffer color(float r, float g, float b) {
|
||||
final FloatBuffer buf = BufferUtils.createFloatBuffer(4);
|
||||
buf.put(r).put(g).put(b).put(1f).flip();
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
+1
-5
@@ -155,11 +155,7 @@ class GlsmSdlWideLineTest {
|
||||
void wideLineCoversItsFirstPixelButNotItsLast() {
|
||||
final float y = pixelCenter(16);
|
||||
GLStateManager.glLineWidth(2.0f);
|
||||
GLStateManager.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
GLStateManager.glBegin(GL11.GL_LINES);
|
||||
GLStateManager.glVertex3f(pixelCenter(40), y, 0.0f);
|
||||
GLStateManager.glVertex3f(pixelCenter(20), y, 0.0f);
|
||||
GLStateManager.glEnd();
|
||||
line(new float[] { pixelCenter(40), y, 0.0f }, new float[] { pixelCenter(20), y, 0.0f });
|
||||
|
||||
final int[] pixels = GlsmSdlHeadlessRig.readTarget();
|
||||
assertEquals(2, litInColumn(pixels, 40), "first pixel of a right-to-left line");
|
||||
|
||||
+2
-14
@@ -11,7 +11,6 @@ import org.lwjgl.util.spvc.Spvc;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class PerFrameBlockDedupTest {
|
||||
@@ -54,11 +53,13 @@ class PerFrameBlockDedupTest {
|
||||
|
||||
st.boundProgramObj = a;
|
||||
applier.putUniform(st, 0, new float[]{0.75f});
|
||||
assertEquals(0.75f, st.uniformBlocks[0].staging(BLOCK_SIZE).get(16 >> 2));
|
||||
st.uniformBlocks[0].dirty = false;
|
||||
|
||||
st.boundProgramObj = b;
|
||||
applier.putUniform(st, 0, new float[]{0.25f});
|
||||
assertTrue(st.uniformBlocks[0].dirty, "a genuinely new value must still reach the buffer");
|
||||
assertEquals(0.25f, st.uniformBlocks[0].staging(BLOCK_SIZE).get(16 >> 2), "the new value must be in the staging buffer");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -84,19 +85,6 @@ class PerFrameBlockDedupTest {
|
||||
assertFalse(st.uniformBlocks[0].dirty, "per-frame block untouched");
|
||||
}
|
||||
|
||||
@Test
|
||||
void dedupDoesNotDropTheValueItself() {
|
||||
st.boundProgramObj = programWithBlockMemberAt(16, ShaderManager.BLOCK_PER_FRAME);
|
||||
applier.putUniform(st, 0, new float[]{0.75f});
|
||||
final float first = st.uniformBlocks[0].staging(BLOCK_SIZE).get(16 >> 2);
|
||||
|
||||
st.boundProgramObj = programWithBlockMemberAt(16, ShaderManager.BLOCK_PER_FRAME);
|
||||
applier.putUniform(st, 0, new float[]{0.25f});
|
||||
final float second = st.uniformBlocks[0].staging(BLOCK_SIZE).get(16 >> 2);
|
||||
|
||||
assertNotEquals(first, second, "the new value must be in the staging buffer");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Tag("sdl-gpu-device")
|
||||
void flushUniformBlocksEndsTheCopyPassOnceForTwoDirtyBlocks() throws Exception {
|
||||
|
||||
+29
-73
@@ -6,6 +6,8 @@ import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
|
||||
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.GL11;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.util.spvc.Spvc;
|
||||
@@ -58,6 +60,11 @@ class PipelineCacheVertexInputTest {
|
||||
return out;
|
||||
}
|
||||
|
||||
private static PipelineCache fp32Cache(int shaderInputMask, int... vecSizesByLocation) {
|
||||
final int[] vecSizes = vecSizes(vecSizesByLocation);
|
||||
return cache(shaderInputMask, vecSizes, fp32(vecSizes));
|
||||
}
|
||||
|
||||
private static PipelineCache cache(int shaderInputMask, int[] vecSizes, int[] baseTypes) {
|
||||
return cache(nextTestSetId++, shaderInputMask, vecSizes, baseTypes);
|
||||
}
|
||||
@@ -110,9 +117,7 @@ class PipelineCacheVertexInputTest {
|
||||
}
|
||||
|
||||
private static PipelineCache coercionCache(String name) {
|
||||
final int[] vecSizes = vecSizes(4);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(1, vecSizes, baseTypes);
|
||||
final PipelineCache c = fp32Cache(1, 4);
|
||||
final String[] names = new String[ContextState.MAX_VERTEX_ATTRIBS];
|
||||
names[0] = name;
|
||||
c.shaderInputName = names;
|
||||
@@ -192,9 +197,7 @@ class PipelineCacheVertexInputTest {
|
||||
|
||||
@Test
|
||||
void sparseLayout_locations0and2_buildsDenseBindingArray() {
|
||||
final int[] vecSizes = vecSizes(3, 0, 2);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(0x5, vecSizes, baseTypes);
|
||||
final PipelineCache c = fp32Cache(0x5, 3, 0, 2);
|
||||
|
||||
final ContextState cs = new ContextState();
|
||||
enableFloatAttrib(cs, 0, 3, 20);
|
||||
@@ -246,9 +249,7 @@ class PipelineCacheVertexInputTest {
|
||||
|
||||
@Test
|
||||
void correctedDhGenericLayout_buildsSuccessfully() {
|
||||
final int[] vecSizes = vecSizes(3, 4);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(0x3, vecSizes, baseTypes);
|
||||
final PipelineCache c = fp32Cache(0x3, 3, 4);
|
||||
|
||||
final ContextState cs = new ContextState();
|
||||
enableFloatAttrib(cs, 0, 3, 12);
|
||||
@@ -261,9 +262,7 @@ class PipelineCacheVertexInputTest {
|
||||
|
||||
@Test
|
||||
void denseLayout_locations0_1_2_noPlaceholders() {
|
||||
final int[] vecSizes = vecSizes(3, 4, 2);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(0x7, vecSizes, baseTypes);
|
||||
final PipelineCache c = fp32Cache(0x7, 3, 4, 2);
|
||||
|
||||
final ContextState cs = new ContextState();
|
||||
enableFloatAttrib(cs, 0, 3, 24);
|
||||
@@ -283,9 +282,7 @@ class PipelineCacheVertexInputTest {
|
||||
|
||||
@Test
|
||||
void shaderExpectsButVaoMissing_emitsConstantBinding() {
|
||||
final int[] vecSizes = vecSizes(3, 4);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(0x3, vecSizes, baseTypes);
|
||||
final PipelineCache c = fp32Cache(0x3, 3, 4);
|
||||
|
||||
final ContextState cs = new ContextState();
|
||||
enableFloatAttrib(cs, 0, 3, 12);
|
||||
@@ -305,9 +302,7 @@ class PipelineCacheVertexInputTest {
|
||||
|
||||
@Test
|
||||
void constantBindingUsesZeroPitchExceptOnMetal() {
|
||||
final int[] vecSizes = vecSizes(0, 0, 0, 0, 3);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(1 << 4, vecSizes, baseTypes);
|
||||
final PipelineCache c = fp32Cache(1 << 4, 0, 0, 0, 0, 3);
|
||||
final ContextState cs = new ContextState();
|
||||
|
||||
for (String driver : new String[] { "vulkan", "metal", "direct3d12" }) {
|
||||
@@ -323,9 +318,7 @@ class PipelineCacheVertexInputTest {
|
||||
|
||||
@Test
|
||||
void vaoExtraAttribNotInShader_droppedFromPipeline() {
|
||||
final int[] vecSizes = vecSizes(4, 0, 4);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(0x5, vecSizes, baseTypes); // shaderInputMask = 0x5
|
||||
final PipelineCache c = fp32Cache(0x5, 4, 0, 4); // shaderInputMask = 0x5
|
||||
|
||||
final ContextState cs = new ContextState();
|
||||
enableFloatAttrib(cs, 0, 3, 24);
|
||||
@@ -359,9 +352,7 @@ class PipelineCacheVertexInputTest {
|
||||
|
||||
@Test
|
||||
void interiorGapWithoutExpansion_fillsGapWithPlaceholder() {
|
||||
final int[] vecSizes = vecSizes(3, 0, 0, 2);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(0x9, vecSizes, baseTypes);
|
||||
final PipelineCache c = fp32Cache(0x9, 3, 0, 0, 2);
|
||||
|
||||
final ContextState cs = new ContextState();
|
||||
enableFloatAttrib(cs, 0, 3, 20);
|
||||
@@ -391,9 +382,7 @@ class PipelineCacheVertexInputTest {
|
||||
|
||||
@Test
|
||||
void enabledAttribWithoutPointer_treatedAsDisabled() {
|
||||
final int[] vecSizes = vecSizes(3, 4, 0, 2);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(0xB, vecSizes, baseTypes); // locs 0, 1, 3
|
||||
final PipelineCache c = fp32Cache(0xB, 3, 4, 0, 2); // locs 0, 1, 3
|
||||
|
||||
final ContextState cs = new ContextState();
|
||||
enableFloatAttrib(cs, 0, 3, 16);
|
||||
@@ -442,9 +431,7 @@ class PipelineCacheVertexInputTest {
|
||||
|
||||
@Test
|
||||
void enabledAttribWithDeadBuffer_demotedToConstantExpansion() {
|
||||
final int[] vecSizes = vecSizes(3, 4);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(0x3, vecSizes, baseTypes);
|
||||
final PipelineCache c = fp32Cache(0x3, 3, 4);
|
||||
store.setBufferHandleResolver(id -> id == 42 ? 0xBEEFL : 0L);
|
||||
|
||||
final ContextState cs = new ContextState();
|
||||
@@ -466,9 +453,7 @@ class PipelineCacheVertexInputTest {
|
||||
|
||||
@Test
|
||||
void pipelineKeyDiffersBetweenDeadAndLiveBinding() {
|
||||
final int[] vecSizes = vecSizes(3, 4);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(0x3, vecSizes, baseTypes);
|
||||
final PipelineCache c = fp32Cache(0x3, 3, 4);
|
||||
|
||||
final ContextState cs = new ContextState();
|
||||
enableFloatAttrib(cs, 0, 3, 16);
|
||||
@@ -534,9 +519,7 @@ class PipelineCacheVertexInputTest {
|
||||
@Test
|
||||
void bindingsArrayIsDenseIndexedBySlot() {
|
||||
// D3D12 requirement: SDL's D3D12 backend reads vertex_buffer_descriptions[buffer_slot] as a flat array, so bindings.get(i).slot() must equal i
|
||||
final int[] vecSizes = vecSizes(3, 0, 0, 0, 0, 2);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(0x21, vecSizes, baseTypes);
|
||||
final PipelineCache c = fp32Cache(0x21, 3, 0, 0, 0, 0, 2);
|
||||
|
||||
final ContextState cs = new ContextState();
|
||||
enableFloatAttrib(cs, 0, 3, 12);
|
||||
@@ -552,11 +535,10 @@ class PipelineCacheVertexInputTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void offsetOnlyPointerChange_keepsKeyAndSkipsRecompute() {
|
||||
final int[] vecSizes = vecSizes(3);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(0x1, vecSizes, baseTypes);
|
||||
@ParameterizedTest(name = "second pointer offset {0}")
|
||||
@ValueSource(longs = {104L, 100L})
|
||||
void reissuedPointerWithSameAlignment_keepsKeyAndSkipsRecompute(long secondOffset) {
|
||||
final PipelineCache c = fp32Cache(0x1, 3);
|
||||
|
||||
final ContextState cs = new ContextState();
|
||||
final VertexAttribs attribs = new VertexAttribs();
|
||||
@@ -565,38 +547,16 @@ class PipelineCacheVertexInputTest {
|
||||
final long keyA = c.computeKey(store, cs);
|
||||
final int runsAfterA = c.inputHashLoopRuns;
|
||||
|
||||
attribs.applyVertexAttribPointer(store, cs, 0, 3, GL11.GL_FLOAT, false, false, 12, 104L);
|
||||
attribs.applyVertexAttribPointer(store, cs, 0, 3, GL11.GL_FLOAT, false, false, 12, secondOffset);
|
||||
final long keyB = c.computeKey(store, cs);
|
||||
|
||||
assertEquals(keyA, keyB, "an offset change that keeps the alignment class must not change the pipeline key");
|
||||
assertEquals(runsAfterA, c.inputHashLoopRuns, "an offset-only change must not force the input-hash loop to rerun");
|
||||
}
|
||||
|
||||
@Test
|
||||
void identicalReissuedPointer_isNoOp() {
|
||||
final int[] vecSizes = vecSizes(3);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(0x1, vecSizes, baseTypes);
|
||||
|
||||
final ContextState cs = new ContextState();
|
||||
final VertexAttribs attribs = new VertexAttribs();
|
||||
attribs.applyVertexAttribPointer(store, cs, 0, 3, GL11.GL_FLOAT, false, false, 12, 100L);
|
||||
|
||||
final long keyA = c.computeKey(store, cs);
|
||||
final int runsAfterA = c.inputHashLoopRuns;
|
||||
|
||||
attribs.applyVertexAttribPointer(store, cs, 0, 3, GL11.GL_FLOAT, false, false, 12, 100L);
|
||||
final long keyB = c.computeKey(store, cs);
|
||||
|
||||
assertEquals(keyA, keyB, "an identical re-issued pointer must not change the key");
|
||||
assertEquals(runsAfterA, c.inputHashLoopRuns, "an identical re-issued pointer must be a no-op and not force a rehash");
|
||||
assertEquals(keyA, keyB, "a re-issued pointer that keeps the alignment class must not change the pipeline key");
|
||||
assertEquals(runsAfterA, c.inputHashLoopRuns, "a re-issued pointer that keeps the alignment class must not force the input-hash loop to rerun");
|
||||
}
|
||||
|
||||
@Test
|
||||
void twoEqualLayoutVaosShareKey() {
|
||||
final int[] vecSizes = vecSizes(3, 4);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(0x3, vecSizes, baseTypes);
|
||||
final PipelineCache c = fp32Cache(0x3, 3, 4);
|
||||
|
||||
final ContextState csA = new ContextState();
|
||||
enableFloatAttrib(csA, 0, 3, 28);
|
||||
@@ -611,9 +571,7 @@ class PipelineCacheVertexInputTest {
|
||||
|
||||
@Test
|
||||
void everyLayoutFieldChangesKey() {
|
||||
final int[] vecSizes = vecSizes(3);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(0x1, vecSizes, baseTypes);
|
||||
final PipelineCache c = fp32Cache(0x1, 3);
|
||||
|
||||
final ContextState base = new ContextState();
|
||||
enableFloatAttrib(base, 0, 3, 16);
|
||||
@@ -670,9 +628,7 @@ class PipelineCacheVertexInputTest {
|
||||
|
||||
@Test
|
||||
void livenessGenBumpForcesRecompute() {
|
||||
final int[] vecSizes = vecSizes(3);
|
||||
final int[] baseTypes = fp32(vecSizes);
|
||||
final PipelineCache c = cache(0x1, vecSizes, baseTypes);
|
||||
final PipelineCache c = fp32Cache(0x1, 3);
|
||||
|
||||
final ContextState cs = new ContextState();
|
||||
enableFloatAttrib(cs, 0, 3, 12);
|
||||
|
||||
-5
@@ -8,11 +8,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class BufferContentsDefinedTest {
|
||||
|
||||
@Test
|
||||
void unknownBuffer_isNotUndefined() {
|
||||
assertFalse(SdlTestRig.resourceManager().hasUndefinedContents(4242));
|
||||
}
|
||||
|
||||
@Test
|
||||
void markDefined_clearsTheFlag() {
|
||||
final ResourceManager rm = SdlTestRig.resourceManager();
|
||||
|
||||
+3
-16
@@ -11,14 +11,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class BufferGlParamsTest {
|
||||
|
||||
@Test
|
||||
void unknownBuffer_reportsGlDefaults() {
|
||||
final ResourceManager rm = SdlTestRig.resourceManager();
|
||||
assertEquals(GL15.GL_STATIC_DRAW, rm.getBufferGlUsage(4242));
|
||||
assertEquals(BufferParams.MUTABLE_STORE, rm.getBufferStorageFlags(4242));
|
||||
assertEquals(0L, rm.getBufferSize(4242));
|
||||
}
|
||||
|
||||
@Test
|
||||
void recordedParams_roundTrip() {
|
||||
final ResourceManager rm = SdlTestRig.resourceManager();
|
||||
@@ -30,15 +22,10 @@ class BufferGlParamsTest {
|
||||
rm.recordBufferGlParams(2, GL15.GL_DYNAMIC_DRAW, flags);
|
||||
assertEquals(GL15.GL_DYNAMIC_DRAW, rm.getBufferGlUsage(2));
|
||||
assertEquals(flags, rm.getBufferStorageFlags(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void recordedParams_overwrittenOnRespecification() {
|
||||
final ResourceManager rm = SdlTestRig.resourceManager();
|
||||
rm.recordBufferGlParams(3, GL15.GL_DYNAMIC_DRAW, GL30.GL_MAP_WRITE_BIT);
|
||||
rm.recordBufferGlParams(3, GL15.GL_STATIC_DRAW, BufferParams.MUTABLE_STORE);
|
||||
assertEquals(GL15.GL_STATIC_DRAW, rm.getBufferGlUsage(3));
|
||||
assertEquals(BufferParams.MUTABLE_STORE, rm.getBufferStorageFlags(3));
|
||||
rm.recordBufferGlParams(2, GL15.GL_STATIC_DRAW, BufferParams.MUTABLE_STORE);
|
||||
assertEquals(GL15.GL_STATIC_DRAW, rm.getBufferGlUsage(2));
|
||||
assertEquals(BufferParams.MUTABLE_STORE, rm.getBufferStorageFlags(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+8
-21
@@ -7,6 +7,8 @@ import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
@@ -33,28 +35,12 @@ class BufferSizeGpuExactnessTest {
|
||||
if (resourceManager != null) resourceManager.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
void subBucketSize_reportsRequestedNotBucketed() {
|
||||
final int glId = 9001;
|
||||
final long handle = resourceManager.createBuffer(glId, FormatMap.mapBufferUsage(GL15.GL_ARRAY_BUFFER), 1000);
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@CsvSource({"sub-bucket,9001,1000", "bucket boundary,9002,4096"})
|
||||
void createdBufferReportsRequestedSize(String name, int glId, long size) {
|
||||
final long handle = resourceManager.createBuffer(glId, FormatMap.mapBufferUsage(GL15.GL_ARRAY_BUFFER), size);
|
||||
assertNotEquals(0L, handle, "buffer creation failed");
|
||||
assertEquals(1000L, resourceManager.getBufferSize(glId));
|
||||
resourceManager.deleteBuffer(glId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bucketBoundarySize_isUnchanged() {
|
||||
final int glId = 9002;
|
||||
resourceManager.createBuffer(glId, FormatMap.mapBufferUsage(GL15.GL_ARRAY_BUFFER), 4096);
|
||||
assertEquals(4096L, resourceManager.getBufferSize(glId));
|
||||
resourceManager.deleteBuffer(glId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void freshStore_reportsUndefinedContents() {
|
||||
final int glId = 9005;
|
||||
resourceManager.createBuffer(glId, FormatMap.mapBufferUsage(GL15.GL_ARRAY_BUFFER), 1000);
|
||||
assertTrue(resourceManager.hasUndefinedContents(glId));
|
||||
assertEquals(size, resourceManager.getBufferSize(glId));
|
||||
resourceManager.deleteBuffer(glId);
|
||||
}
|
||||
|
||||
@@ -63,6 +49,7 @@ class BufferSizeGpuExactnessTest {
|
||||
final int usage = FormatMap.mapBufferUsage(GL15.GL_ARRAY_BUFFER);
|
||||
final int first = 9006;
|
||||
resourceManager.createBuffer(first, usage, 1000);
|
||||
assertTrue(resourceManager.hasUndefinedContents(first));
|
||||
resourceManager.markBufferContentsDefined(first);
|
||||
resourceManager.deleteBuffer(first);
|
||||
|
||||
|
||||
-6
@@ -10,12 +10,6 @@ class ColorContentLifetimeTest {
|
||||
|
||||
private static final long TEX = 0xC010125L;
|
||||
|
||||
@Test
|
||||
void freshTextureHasUndefinedContents() {
|
||||
final ResourceManager rm = SdlTestRig.resourceManager();
|
||||
assertFalse(rm.isTextureContentDefined(TEX), "never written, so the first pass must clear");
|
||||
}
|
||||
|
||||
@Test
|
||||
void markedTextureHasDefinedContents() {
|
||||
final ResourceManager rm = SdlTestRig.resourceManager();
|
||||
|
||||
-13
@@ -1,7 +1,6 @@
|
||||
package com.gtnewhorizons.angelica.sdlgpu.resource;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.TestThreads;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig;
|
||||
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
|
||||
@@ -12,18 +11,6 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class CrossThreadUploadCBIsolationTest {
|
||||
|
||||
@Test
|
||||
void differentThreadsHaveDistinctFrameStates() throws Exception {
|
||||
final FrameManager fm = SdlTestRig.frameManager();
|
||||
final FrameManager.FrameState testThreadFs = fm.frame();
|
||||
|
||||
final AtomicReference<FrameManager.FrameState> workerFs = new AtomicReference<>();
|
||||
TestThreads.run("worker-thread", () -> workerFs.set(fm.frame()));
|
||||
|
||||
assertNotNull(workerFs.get());
|
||||
assertNotSame(testThreadFs, workerFs.get(), "each thread must get its own FrameManager.FrameState instance from the FrameManager ThreadLocal");
|
||||
}
|
||||
|
||||
@Test
|
||||
void clientThreadsRecordingsDoNotAppearInSplashThreadsState() throws Exception {
|
||||
final FrameManager fm = SdlTestRig.frameManager();
|
||||
|
||||
-5
@@ -23,11 +23,6 @@ class FormatMapBufferUsageTest {
|
||||
assertEquals(expected, FormatMap.mapBufferUsage(GL15.GL_ELEMENT_ARRAY_BUFFER));
|
||||
}
|
||||
|
||||
@Test
|
||||
void storageBufferTarget_hasNoVertexUsage() {
|
||||
assertEquals(0, FormatMap.mapBufferUsage(GL43.GL_SHADER_STORAGE_BUFFER) & SDL_GPU_BUFFERUSAGE_VERTEX);
|
||||
}
|
||||
|
||||
@Test
|
||||
void drawIndirectBufferTarget_singleIndirectFlag() {
|
||||
assertEquals(SDL_GPU_BUFFERUSAGE_INDIRECT, FormatMap.mapBufferUsage(GL40.GL_DRAW_INDIRECT_BUFFER));
|
||||
|
||||
+22
-40
@@ -3,6 +3,11 @@ package com.gtnewhorizons.angelica.sdlgpu.resource;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
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.assertFalse;
|
||||
@@ -24,16 +29,6 @@ class FullCoverageClearDiscardTest {
|
||||
return new ResourceManager.TextureMeta(0, 0, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, W, H, 1, 1, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void fullTargetColorBlitDropsThePendingClear() {
|
||||
final ContextState st = new ContextState();
|
||||
FBOClearTracker.recordPendingColorClear(st, HANDLE, 1f, 0f, 0f, 1f);
|
||||
|
||||
assertTrue(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta(), true));
|
||||
assertFalse(st.pendingColorTextures.contains(HANDLE), "pending clear must be dropped, not left queued");
|
||||
assertFalse(st.pendingColorValues.containsKey(HANDLE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyWithNoPendingClearDefinesTheDestination() {
|
||||
final ContextState st = new ContextState();
|
||||
@@ -59,32 +54,25 @@ class FullCoverageClearDiscardTest {
|
||||
assertFalse(st.pendingDepthValues.containsKey(HANDLE) || st.pendingStencilValues.containsKey(HANDLE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void partialCoverageKeepsTheClear() {
|
||||
final int[][] partial = {
|
||||
{ 0, 0, 0, W / 2, H }, // narrower
|
||||
{ 0, 0, 0, W, H / 2 }, // shorter
|
||||
{ 1, 0, 0, W, H }, // offset x
|
||||
{ 0, 1, 0, W, H }, // offset y
|
||||
{ 0, 0, 1, W, H }, // not level 0
|
||||
};
|
||||
for (int[] c : partial) {
|
||||
final ContextState st = new ContextState();
|
||||
FBOClearTracker.recordPendingColorClear(st, HANDLE, 1f, 0f, 0f, 1f);
|
||||
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, c[0], c[1], c[2], c[3], c[4], meta(), true),
|
||||
"x=" + c[0] + " y=" + c[1] + " level=" + c[2] + " w=" + c[3] + " h=" + c[4]);
|
||||
assertTrue(st.pendingColorTextures.contains(HANDLE), "the clear must survive a partial overwrite");
|
||||
}
|
||||
private static Stream<Arguments> clearsThatSurvive() {
|
||||
final ResourceManager.TextureMeta volume = new ResourceManager.TextureMeta(0, 0, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, W, H, 4, 1, 0);
|
||||
return Stream.of(
|
||||
Arguments.of("narrower", 0, 0, 0, W / 2, H, meta()),
|
||||
Arguments.of("shorter", 0, 0, 0, W, H / 2, meta()),
|
||||
Arguments.of("offset x", 1, 0, 0, W, H, meta()),
|
||||
Arguments.of("offset y", 0, 1, 0, W, H, meta()),
|
||||
Arguments.of("not level 0", 0, 0, 1, W, H, meta()),
|
||||
Arguments.of("volume slice", 0, 0, 0, W, H, volume),
|
||||
Arguments.of("missing meta", 0, 0, 0, W, H, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void volumeTextureKeepsTheClear() {
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("clearsThatSurvive")
|
||||
void partialCoverageKeepsTheClear(String name, int x, int y, int level, int w, int h, ResourceManager.TextureMeta meta) {
|
||||
final ContextState st = new ContextState();
|
||||
FBOClearTracker.recordPendingColorClear(st, HANDLE, 1f, 0f, 0f, 1f);
|
||||
final ResourceManager.TextureMeta volume = new ResourceManager.TextureMeta(0, 0, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, W, H, 4, 1, 0);
|
||||
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, volume, true),
|
||||
"one slice of a volume is not full coverage");
|
||||
assertTrue(st.pendingColorTextures.contains(HANDLE));
|
||||
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, x, y, level, w, h, meta, true));
|
||||
assertTrue(st.pendingColorTextures.contains(HANDLE), "the clear must survive a partial overwrite");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -93,14 +81,6 @@ class FullCoverageClearDiscardTest {
|
||||
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta(), true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingMetaFallsBackToMaterializing() {
|
||||
final ContextState st = new ContextState();
|
||||
FBOClearTracker.recordPendingColorClear(st, HANDLE, 1f, 0f, 0f, 1f);
|
||||
assertFalse(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, null, true));
|
||||
assertTrue(st.pendingColorTextures.contains(HANDLE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void discardBumpsMutationGenSoSamplerFlushReevaluates() {
|
||||
final ContextState st = new ContextState();
|
||||
@@ -110,5 +90,7 @@ class FullCoverageClearDiscardTest {
|
||||
|
||||
assertTrue(tracker().discardPendingClearIfFullyCovered(st, HANDLE, 0, 0, 0, W, H, meta(), true));
|
||||
assertEquals(before + 1, st.pendingMutationGen);
|
||||
assertFalse(st.pendingColorTextures.contains(HANDLE), "pending clear must be dropped, not left queued");
|
||||
assertFalse(st.pendingColorValues.containsKey(HANDLE));
|
||||
}
|
||||
}
|
||||
-7
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.sdlgpu.resource;
|
||||
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig;
|
||||
|
||||
import com.gtnewhorizons.angelica.sdlgpu.device.Device;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
@@ -32,7 +31,6 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
@Test
|
||||
void gateStaysClosedUntilAWriteMarksARegionDirty() {
|
||||
final SdlTestRig rig = SdlTestRig.create();
|
||||
final Device device = rig.device;
|
||||
final FrameManager fm = rig.frameManager;
|
||||
final ResourceManager rm = rig.resourceManager;
|
||||
final CountingSink sink = new CountingSink();
|
||||
@@ -63,7 +61,6 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
@Test
|
||||
void widenAndRemoveKeepTheCountExact() {
|
||||
final SdlTestRig rig = SdlTestRig.create();
|
||||
final Device device = rig.device;
|
||||
final FrameManager fm = rig.frameManager;
|
||||
final ResourceManager rm = rig.resourceManager;
|
||||
final PersistentBufferSync sync = new PersistentBufferSync(fm, rm, new CountingSink());
|
||||
@@ -87,7 +84,6 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
@Test
|
||||
void mappingsVersionMovesOnlyWhenTheMapChanges() {
|
||||
final SdlTestRig rig = SdlTestRig.create();
|
||||
final Device device = rig.device;
|
||||
final FrameManager fm = rig.frameManager;
|
||||
final ResourceManager rm = rig.resourceManager;
|
||||
|
||||
@@ -122,7 +118,6 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
@Test
|
||||
void repeatedDrainsKeepUploadingWhenTheMappingSetIsUnchanged() {
|
||||
final SdlTestRig rig = SdlTestRig.create();
|
||||
final Device device = rig.device;
|
||||
final FrameManager fm = rig.frameManager;
|
||||
final ResourceManager rm = rig.resourceManager;
|
||||
final CountingSink sink = new CountingSink();
|
||||
@@ -148,7 +143,6 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
@Test
|
||||
void aReplacedMappingIsPickedUpByALaterDrain() {
|
||||
final SdlTestRig rig = SdlTestRig.create();
|
||||
final Device device = rig.device;
|
||||
final FrameManager fm = rig.frameManager;
|
||||
final ResourceManager rm = rig.resourceManager;
|
||||
final CountingSink sink = new CountingSink();
|
||||
@@ -178,7 +172,6 @@ class PersistentBufferSyncDirtyGateTest {
|
||||
@Test
|
||||
void concurrentWritesAndDrainsLeaveTheGateExact() throws Exception {
|
||||
final SdlTestRig rig = SdlTestRig.create();
|
||||
final Device device = rig.device;
|
||||
final FrameManager fm = rig.frameManager;
|
||||
final ResourceManager rm = rig.resourceManager;
|
||||
final CountingSink sink = new CountingSink();
|
||||
|
||||
+4
-13
@@ -53,19 +53,6 @@ class PersistentDrainBoundaryTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void oneDrainCoversEveryRangeDirtiedSinceTheLastDrain() {
|
||||
try (Fixture f = new Fixture()) {
|
||||
for (int i = 0; i < 64; i++) f.sync.onPersistentBufferWrite(1, i * 188L, 188L);
|
||||
f.sync.enqueueDirtyPersistentRegions();
|
||||
|
||||
assertEquals(1, f.sink.offsets.size(), "contiguous writes drain as a single upload");
|
||||
assertEquals(0L, f.sink.offsets.getLong(0));
|
||||
assertEquals(64L * 188L, f.sink.sizes.getLong(0), "the union spans every byte written since the last drain");
|
||||
assertFalse(f.rm.hasDirtyPersistentRegions());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void theAccumulatedUnionMatchesThePerDrawSum() {
|
||||
final long batched;
|
||||
@@ -73,6 +60,10 @@ class PersistentDrainBoundaryTest {
|
||||
for (int i = 0; i < 64; i++) f.sync.onPersistentBufferWrite(1, i * 188L, 188L);
|
||||
f.sync.enqueueDirtyPersistentRegions();
|
||||
batched = f.sink.totalBytes();
|
||||
assertEquals(1, f.sink.offsets.size(), "contiguous writes drain as a single upload");
|
||||
assertEquals(0L, f.sink.offsets.getLong(0));
|
||||
assertEquals(64L * 188L, f.sink.sizes.getLong(0), "the union spans every byte written since the last drain");
|
||||
assertFalse(f.rm.hasDirtyPersistentRegions());
|
||||
}
|
||||
try (Fixture f = new Fixture()) {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
|
||||
+1
-12
@@ -36,6 +36,7 @@ class PersistentMappingDirtyRangeTest {
|
||||
assertEquals(16, PersistentMapping.rangeOffset(claimed));
|
||||
assertEquals(32, PersistentMapping.rangeSize(claimed));
|
||||
assertFalse(pm.isDirty(), "claiming leaves the mapping clean");
|
||||
assertTrue(PersistentMapping.isClean(pm.claimDirty()), "second claim gets nothing");
|
||||
} finally {
|
||||
free(pm);
|
||||
}
|
||||
@@ -82,18 +83,6 @@ class PersistentMappingDirtyRangeTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void claimIsExclusive() {
|
||||
final PersistentMapping pm = mapping(256);
|
||||
try {
|
||||
pm.markDirty(0, 8);
|
||||
assertFalse(PersistentMapping.isClean(pm.claimDirty()), "first claim wins");
|
||||
assertTrue(PersistentMapping.isClean(pm.claimDirty()), "second claim gets nothing");
|
||||
} finally {
|
||||
free(pm);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void concurrentWritersProduceExactlyOneTransitionPerClaim() throws Exception {
|
||||
final PersistentMapping pm = mapping(4096);
|
||||
|
||||
-9
@@ -26,15 +26,6 @@ class ReadbackShadowsTest {
|
||||
return ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder());
|
||||
}
|
||||
|
||||
@Test
|
||||
void serveRefusesAStaleSlot() {
|
||||
final ReadbackShadows shadows = shadows();
|
||||
SdlReflect.putReadbackSlot(shadows, 7, 0, SENTINEL_HANDLE, 64, false);
|
||||
|
||||
assertFalse(shadows.serve(7, 0, out(64), 64),
|
||||
"a slot invalidated by a GPU-to-GPU write must not be served as if it were downloaded");
|
||||
}
|
||||
|
||||
@Test
|
||||
void serveRefusesAnUnknownSlot() {
|
||||
final ReadbackShadows shadows = shadows();
|
||||
|
||||
+43
-58
@@ -3,6 +3,12 @@ package com.gtnewhorizons.angelica.sdlgpu.resource;
|
||||
|
||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||
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.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -21,70 +27,49 @@ class SDLGPUFboTargetCacheTest {
|
||||
assertEquals(0L, fbo.primaryTarget);
|
||||
}
|
||||
|
||||
@Test
|
||||
void depthOnly_primaryTargetIsDepth() {
|
||||
final FboState fbo = new FboState();
|
||||
fbo.depthTexture = 0xDDDDL;
|
||||
fbo.recomputeTargets();
|
||||
assertFalse(fbo.hasAnyColor);
|
||||
assertEquals(0xDDDDL, fbo.primaryTarget);
|
||||
private static Arguments row(String name, Consumer<FboState> setup, boolean anyColor, long primary) {
|
||||
return Arguments.of(name, setup, anyColor, primary);
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleColor_primaryTargetIsColor() {
|
||||
final FboState fbo = new FboState();
|
||||
fbo.colorTextures[0] = 0xAAAAL;
|
||||
fbo.depthTexture = 0xDDDDL;
|
||||
// default drawBuffers = {0}
|
||||
fbo.recomputeTargets();
|
||||
assertTrue(fbo.hasAnyColor);
|
||||
assertEquals(0xAAAAL, fbo.primaryTarget);
|
||||
private static Stream<Arguments> recomputedTargets() {
|
||||
return Stream.of(
|
||||
row("depth only", f -> f.depthTexture = 0xDDDDL, false, 0xDDDDL),
|
||||
row("single color over depth, default drawBuffers", f -> {
|
||||
f.colorTextures[0] = 0xAAAAL;
|
||||
f.depthTexture = 0xDDDDL;
|
||||
}, true, 0xAAAAL),
|
||||
row("disabled slot 0, first enabled drawBuffer is index 1", f -> {
|
||||
f.colorTextures[0] = 0xAAAAL;
|
||||
f.colorTextures[1] = 0xBBBBL;
|
||||
f.drawBuffers = new int[]{-1, 1};
|
||||
f.depthTexture = 0xDDDDL;
|
||||
}, true, 0xBBBBL),
|
||||
row("all drawBuffers disabled falls through to depth", f -> {
|
||||
f.colorTextures[0] = 0xAAAAL;
|
||||
f.drawBuffers = new int[]{-1};
|
||||
f.depthTexture = 0xDDDDL;
|
||||
}, false, 0xDDDDL),
|
||||
row("MRT first color wins", f -> {
|
||||
f.colorTextures[0] = 0xAAAAL;
|
||||
f.colorTextures[1] = 0xBBBBL;
|
||||
f.colorTextures[2] = 0xCCCCL;
|
||||
f.drawBuffers = new int[]{0, 1, 2};
|
||||
}, true, 0xAAAAL),
|
||||
row("empty drawBuffers means no color even if bound", f -> {
|
||||
f.colorTextures[0] = 0xAAAAL;
|
||||
f.drawBuffers = new int[0];
|
||||
f.depthTexture = 0xDDDDL;
|
||||
}, false, 0xDDDDL));
|
||||
}
|
||||
|
||||
@Test
|
||||
void disabledDrawBuffer_doesNotCount() {
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("recomputedTargets")
|
||||
void recomputeTargets_picksPrimaryTarget(String name, Consumer<FboState> setup, boolean anyColor, long primary) {
|
||||
final FboState fbo = new FboState();
|
||||
fbo.colorTextures[0] = 0xAAAAL;
|
||||
fbo.colorTextures[1] = 0xBBBBL;
|
||||
fbo.drawBuffers = new int[]{-1, 1}; // GL_NONE then attachment 1
|
||||
fbo.depthTexture = 0xDDDDL;
|
||||
setup.accept(fbo);
|
||||
fbo.recomputeTargets();
|
||||
assertTrue(fbo.hasAnyColor);
|
||||
assertEquals(0xBBBBL, fbo.primaryTarget, "first enabled drawBuffer is index 1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void allDrawBuffersDisabled_falsThroughToDepth() {
|
||||
final FboState fbo = new FboState();
|
||||
fbo.colorTextures[0] = 0xAAAAL;
|
||||
fbo.drawBuffers = new int[]{-1};
|
||||
fbo.depthTexture = 0xDDDDL;
|
||||
fbo.recomputeTargets();
|
||||
assertFalse(fbo.hasAnyColor);
|
||||
assertEquals(0xDDDDL, fbo.primaryTarget);
|
||||
}
|
||||
|
||||
@Test
|
||||
void mrtFirstColorWins_evenWhenMultipleAttached() {
|
||||
final FboState fbo = new FboState();
|
||||
fbo.colorTextures[0] = 0xAAAAL;
|
||||
fbo.colorTextures[1] = 0xBBBBL;
|
||||
fbo.colorTextures[2] = 0xCCCCL;
|
||||
fbo.drawBuffers = new int[]{0, 1, 2};
|
||||
fbo.recomputeTargets();
|
||||
assertTrue(fbo.hasAnyColor);
|
||||
assertEquals(0xAAAAL, fbo.primaryTarget);
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyDrawBuffers_noColorEvenIfBound() {
|
||||
final FboState fbo = new FboState();
|
||||
fbo.colorTextures[0] = 0xAAAAL;
|
||||
fbo.drawBuffers = new int[0];
|
||||
fbo.depthTexture = 0xDDDDL;
|
||||
fbo.recomputeTargets();
|
||||
assertFalse(fbo.hasAnyColor);
|
||||
assertEquals(0xDDDDL, fbo.primaryTarget);
|
||||
assertEquals(anyColor, fbo.hasAnyColor);
|
||||
assertEquals(primary, fbo.primaryTarget);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+18
-26
@@ -3,8 +3,13 @@ package com.gtnewhorizons.angelica.sdlgpu.resource;
|
||||
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
|
||||
import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig;
|
||||
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.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -22,26 +27,28 @@ class TextureReleaseUploadOrderTest {
|
||||
Reflect.setDeclared(ResourceManager.class, rm, "lastTextureUploadSeq", lastUpload);
|
||||
}
|
||||
|
||||
@Test
|
||||
void releaseIsImmediateWhenNoTransferThreadIsAttached() {
|
||||
final ResourceManager rm = SdlTestRig.resourceManager();
|
||||
setUploadSeqs(rm, 50L);
|
||||
assertFalse(rm.shouldDeferTextureRelease(), "with no transfer thread there is nothing to outlive");
|
||||
private static Stream<Arguments> releasePaths() {
|
||||
final BiConsumer<ResourceManager, Integer> delete = ResourceManager::deleteTexture;
|
||||
final BiConsumer<ResourceManager, Integer> realloc = ResourceManager::releaseTextureHandleForRealloc;
|
||||
return Stream.of(
|
||||
Arguments.of("glDeleteTextures", delete, 50L, 10L),
|
||||
Arguments.of("glTexImage re-specification", realloc, 7L, 0L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void releaseIsDeferredWhileTextureUploadsAreStillQueued() {
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("releasePaths")
|
||||
void releaseIsDeferredWhileTextureUploadsAreStillQueued(String path, BiConsumer<ResourceManager, Integer> release, long lastUpload, long submitted) {
|
||||
final SdlTestRig rig = SdlTestRig.create();
|
||||
final ResourceManager rm = rig.resourceManager;
|
||||
final TransferThread tt = rig.installTransferThread();
|
||||
setUploadSeqs(rm, 50L);
|
||||
Reflect.setDeclared(TransferThread.class, tt, "submittedSeq", 10L);
|
||||
setUploadSeqs(rm, lastUpload);
|
||||
Reflect.setDeclared(TransferThread.class, tt, "submittedSeq", submitted);
|
||||
|
||||
rm.trackTextureHandle(HANDLE);
|
||||
final int glId = rm.genTexture();
|
||||
SdlReflect.putTextureHandle(rm, glId, HANDLE);
|
||||
|
||||
rm.deleteTexture(glId);
|
||||
release.accept(rm, glId);
|
||||
assertEquals(List.of(HANDLE), deferredReleases(rm), "the free must wait for the queued upload");
|
||||
}
|
||||
|
||||
@@ -64,6 +71,7 @@ class TextureReleaseUploadOrderTest {
|
||||
final SdlTestRig rig = SdlTestRig.create();
|
||||
final ResourceManager rm = rig.resourceManager;
|
||||
rig.frameManager.setResourceManager(rm);
|
||||
setUploadSeqs(rm, 50L);
|
||||
assertFalse(rm.shouldDeferTextureRelease(), "nothing queued yet");
|
||||
|
||||
rig.frameManager.frame().pendingTexHandles.add(HANDLE);
|
||||
@@ -90,20 +98,4 @@ class TextureReleaseUploadOrderTest {
|
||||
assertEquals(0xFEEDL, handles.getLong(1), "other destinations are untouched");
|
||||
assertEquals(0L, handles.getLong(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void reallocReleaseObeysTheSameRule() {
|
||||
final SdlTestRig rig = SdlTestRig.create();
|
||||
final ResourceManager rm = rig.resourceManager;
|
||||
final TransferThread tt = rig.installTransferThread();
|
||||
setUploadSeqs(rm, 7L);
|
||||
Reflect.setDeclared(TransferThread.class, tt, "submittedSeq", 0L);
|
||||
|
||||
rm.trackTextureHandle(HANDLE);
|
||||
final int glId = rm.genTexture();
|
||||
SdlReflect.putTextureHandle(rm, glId, HANDLE);
|
||||
|
||||
rm.releaseTextureHandleForRealloc(glId);
|
||||
assertEquals(List.of(HANDLE), deferredReleases(rm), "glTexImage2D re-specification frees the old handle on the same path as a delete");
|
||||
}
|
||||
}
|
||||
+2
-12
@@ -26,22 +26,12 @@ class TransferThreadWakePolicyTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void windowIsEvenlySpaced() {
|
||||
long previous = -1;
|
||||
void wakesExactlyOnEveryWindowBoundary() {
|
||||
for (long seq = 1; seq <= 512; seq++) {
|
||||
if (!TransferThread.shouldWakeOnEnqueue(seq)) continue;
|
||||
if (previous >= 0) assertEquals(16, seq - previous, "gaps between unparks must be uniform");
|
||||
previous = seq;
|
||||
assertEquals(seq % 16 == 0, TransferThread.shouldWakeOnEnqueue(seq), "seq " + seq);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void neighboursOfAWakeDoNotWake() {
|
||||
assertTrue(TransferThread.shouldWakeOnEnqueue(16));
|
||||
assertFalse(TransferThread.shouldWakeOnEnqueue(15));
|
||||
assertFalse(TransferThread.shouldWakeOnEnqueue(17));
|
||||
}
|
||||
|
||||
private static TransferThread stoppedThread(long submittedSeq) {
|
||||
final SdlTestRig rig = SdlTestRig.create();
|
||||
final TransferThread tt = rig.newTransferThread();
|
||||
|
||||
+26
-42
@@ -76,6 +76,18 @@ class UploadArenaGpuRoundTripTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static void uploadPattern(long dst, int size, int seed) {
|
||||
final ByteBuffer data = pattern(size, seed);
|
||||
assertTrue(resourceManager.arenaUpload(data, dst, 0, false));
|
||||
MemoryUtil.memFree(data);
|
||||
}
|
||||
|
||||
private static void assertDownloadedPattern(long gpuBuffer, int size, int seed, String what) {
|
||||
final ByteBuffer got = download(gpuBuffer, size);
|
||||
assertPattern(got, size, seed, what);
|
||||
MemoryUtil.memFree(got);
|
||||
}
|
||||
|
||||
private static ByteBuffer download(long gpuBuffer, int size) {
|
||||
try (var stack = stackPush()) {
|
||||
final SDL_GPUTransferBufferCreateInfo ci = SDL_GPUTransferBufferCreateInfo.calloc(stack)
|
||||
@@ -125,15 +137,11 @@ class UploadArenaGpuRoundTripTest {
|
||||
beginFrame();
|
||||
final long dst = createVertexBuffer(size);
|
||||
assertTrue(dst != 0);
|
||||
final ByteBuffer data = pattern(size, 7);
|
||||
assertTrue(resourceManager.arenaUpload(data, dst, 0, false));
|
||||
MemoryUtil.memFree(data);
|
||||
uploadPattern(dst, size, 7);
|
||||
endFrame();
|
||||
|
||||
SDL_WaitForGPUIdle(sdlDevice);
|
||||
final ByteBuffer got = download(dst, size);
|
||||
assertPattern(got, size, 7, "single-frame upload");
|
||||
MemoryUtil.memFree(got);
|
||||
assertDownloadedPattern(dst, size, 7, "single-frame upload");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -145,9 +153,7 @@ class UploadArenaGpuRoundTripTest {
|
||||
beginFrame();
|
||||
final long armor = createVertexBuffer(size);
|
||||
assertTrue(armor != 0);
|
||||
final ByteBuffer armorData = pattern(size, 3);
|
||||
assertTrue(resourceManager.arenaUpload(armorData, armor, 0, false));
|
||||
MemoryUtil.memFree(armorData);
|
||||
uploadPattern(armor, size, 3);
|
||||
final long junkDst = createVertexBuffer(junkSize);
|
||||
endFrame();
|
||||
|
||||
@@ -171,9 +177,7 @@ class UploadArenaGpuRoundTripTest {
|
||||
}
|
||||
|
||||
SDL_WaitForGPUIdle(sdlDevice);
|
||||
final ByteBuffer got = download(armor, size);
|
||||
assertPattern(got, size, 3, "upload after 60 frames of churn");
|
||||
MemoryUtil.memFree(got);
|
||||
assertDownloadedPattern(armor, size, 3, "upload after 60 frames of churn");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -186,31 +190,23 @@ class UploadArenaGpuRoundTripTest {
|
||||
// frame 66
|
||||
beginFrame();
|
||||
long big = createVertexBuffer(bigSize);
|
||||
ByteBuffer bigData = pattern(bigSize, 41);
|
||||
assertTrue(resourceManager.arenaUpload(bigData, big, 0, false));
|
||||
MemoryUtil.memFree(bigData);
|
||||
uploadPattern(big, bigSize, 41);
|
||||
final long med = createVertexBuffer(medSize);
|
||||
final ByteBuffer medData = pattern(medSize, 42);
|
||||
assertTrue(resourceManager.arenaUpload(medData, med, 0, false));
|
||||
MemoryUtil.memFree(medData);
|
||||
uploadPattern(med, medSize, 42);
|
||||
endFrame();
|
||||
|
||||
final int bigGlId = nextGlId - 2;
|
||||
beginFrame();
|
||||
resourceManager.deleteBuffer(bigGlId);
|
||||
big = resourceManager.createBuffer(bigGlId, SDL_GPU_BUFFERUSAGE_VERTEX, bigSize);
|
||||
bigData = pattern(bigSize, 43);
|
||||
assertTrue(resourceManager.arenaUpload(bigData, big, 0, false));
|
||||
MemoryUtil.memFree(bigData);
|
||||
uploadPattern(big, bigSize, 43);
|
||||
endFrame();
|
||||
|
||||
final long[] armor = new long[armorSizes.length];
|
||||
beginFrame();
|
||||
for (int i = 0; i < armorSizes.length; i++) {
|
||||
armor[i] = createVertexBuffer(armorSizes[i]);
|
||||
final ByteBuffer data = pattern(armorSizes[i], 50 + i);
|
||||
assertTrue(resourceManager.arenaUpload(data, armor[i], 0, false));
|
||||
MemoryUtil.memFree(data);
|
||||
uploadPattern(armor[i], armorSizes[i], 50 + i);
|
||||
}
|
||||
endFrame();
|
||||
|
||||
@@ -218,17 +214,13 @@ class UploadArenaGpuRoundTripTest {
|
||||
beginFrame();
|
||||
resourceManager.deleteBuffer(bigGlId);
|
||||
big = resourceManager.createBuffer(bigGlId, SDL_GPU_BUFFERUSAGE_VERTEX, bigSize);
|
||||
bigData = pattern(bigSize, 60 + frame);
|
||||
assertTrue(resourceManager.arenaUpload(bigData, big, 0, false));
|
||||
MemoryUtil.memFree(bigData);
|
||||
uploadPattern(big, bigSize, 60 + frame);
|
||||
endFrame();
|
||||
}
|
||||
|
||||
SDL_WaitForGPUIdle(sdlDevice);
|
||||
for (int i = 0; i < armorSizes.length; i++) {
|
||||
final ByteBuffer got = download(armor[i], armorSizes[i]);
|
||||
assertPattern(got, armorSizes[i], 50 + i, "armor buffer " + i + " (size " + armorSizes[i] + ")");
|
||||
MemoryUtil.memFree(got);
|
||||
assertDownloadedPattern(armor[i], armorSizes[i], 50 + i, "armor buffer " + i + " (size " + armorSizes[i] + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,9 +230,7 @@ class UploadArenaGpuRoundTripTest {
|
||||
final int size = 54864;
|
||||
beginFrame();
|
||||
final long a = createVertexBuffer(size);
|
||||
final ByteBuffer dataA = pattern(size, 11);
|
||||
assertTrue(resourceManager.arenaUpload(dataA, a, 0, false));
|
||||
MemoryUtil.memFree(dataA);
|
||||
uploadPattern(a, size, 11);
|
||||
|
||||
resourceManager.flushUploadArena();
|
||||
final ByteBuffer readback = download(a, size);
|
||||
@@ -252,9 +242,7 @@ class UploadArenaGpuRoundTripTest {
|
||||
endFrame();
|
||||
|
||||
SDL_WaitForGPUIdle(sdlDevice);
|
||||
final ByteBuffer got = download(b, size);
|
||||
assertPattern(got, size, 11, "B built from mid-frame readback");
|
||||
MemoryUtil.memFree(got);
|
||||
assertDownloadedPattern(b, size, 11, "B built from mid-frame readback");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -266,17 +254,13 @@ class UploadArenaGpuRoundTripTest {
|
||||
beginFrame();
|
||||
for (int i = 0; i < chunks; i++) {
|
||||
dsts[i] = createVertexBuffer(chunk);
|
||||
final ByteBuffer data = pattern(chunk, 20 + i);
|
||||
assertTrue(resourceManager.arenaUpload(data, dsts[i], 0, false));
|
||||
MemoryUtil.memFree(data);
|
||||
uploadPattern(dsts[i], chunk, 20 + i);
|
||||
}
|
||||
endFrame();
|
||||
|
||||
SDL_WaitForGPUIdle(sdlDevice);
|
||||
for (int i = 0; i < chunks; i++) {
|
||||
final ByteBuffer got = download(dsts[i], chunk);
|
||||
assertPattern(got, chunk, 20 + i, "grow-path upload " + i);
|
||||
MemoryUtil.memFree(got);
|
||||
assertDownloadedPattern(dsts[i], chunk, 20 + i, "grow-path upload " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-11
@@ -35,17 +35,6 @@ class UploadArenaTest {
|
||||
assertFalse(a.fits(-1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resetClearsOffsetAndCountButKeepsCapacity() {
|
||||
final UploadArena a = new UploadArena();
|
||||
a.reserve(500);
|
||||
a.reset();
|
||||
assertEquals(0, a.usedBytes());
|
||||
assertEquals(0, a.copyCount());
|
||||
assertEquals(UploadArena.INITIAL_CAPACITY, a.capacity());
|
||||
assertEquals(0, a.reserve(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
void growIsDeferredUntilApplyGrow() {
|
||||
final UploadArena a = new UploadArena();
|
||||
@@ -92,6 +81,9 @@ class UploadArenaTest {
|
||||
assertEquals(-1, a.reserve(4096));
|
||||
a.requestGrow(want);
|
||||
a.reset();
|
||||
assertEquals(0, a.usedBytes());
|
||||
assertEquals(0, a.copyCount());
|
||||
assertEquals(UploadArena.INITIAL_CAPACITY, a.capacity());
|
||||
assertTrue(a.applyGrow());
|
||||
assertEquals(0, a.reserve(a.capacity()));
|
||||
assertEquals(1, a.copyCount());
|
||||
|
||||
+2
-2
@@ -35,9 +35,9 @@ class UploadCBAutoFlushThresholdTest {
|
||||
@Test
|
||||
void manyTinyUploadsAccumulateBytes() {
|
||||
final FrameManager fm = SdlTestRig.frameManager();
|
||||
for (int i = 0; i < 255; i++) fm.recordUploadCommands(65536, 1);
|
||||
for (int i = 0; i < 255; i++) fm.recordUploadCommands(65536, 0);
|
||||
assertTrue(fm.frame().pendingUploadBytes < MAX_PENDING_UPLOAD_BYTES);
|
||||
fm.recordUploadCommands(65536, 1);
|
||||
fm.recordUploadCommands(65536, 0);
|
||||
assertTrue(fm.shouldAutoSubmitPendingUpload());
|
||||
}
|
||||
|
||||
|
||||
-13
@@ -59,19 +59,6 @@ class AttribLocationPatchTest {
|
||||
assertEquals(DESIRED[i], prog.resolvedAttribLocations.getInt(NAMES[i]), () -> "resolved location for " + NAMES[idx]);
|
||||
}
|
||||
assertEquals(0x3f, prog.vertexInputMask, "six inputs at 0..5");
|
||||
}
|
||||
|
||||
@Test
|
||||
void metadataFollowsTheRemappedLocation() {
|
||||
final ShaderManager.ProgramObject prog = new ShaderManager.ProgramObject();
|
||||
prog.vertexSpirv = ByteBuffer.allocateDirect(NAMES.length * Integer.BYTES).order(ByteOrder.nativeOrder());
|
||||
final ShaderManager.StageReflection vs = reflectionWithInputs(prog.vertexSpirv);
|
||||
for (int i = 0; i < NAMES.length; i++) {
|
||||
prog.attribLocationBindings.put(NAMES[i], DESIRED[i]);
|
||||
}
|
||||
|
||||
ShaderManager.applyAttribLocationsAndInputMask(prog, vs);
|
||||
|
||||
assertEquals(4, prog.vertexInputVecSize[1]);
|
||||
assertEquals(Spvc.SPVC_BASETYPE_FP32, prog.vertexInputBaseType[1]);
|
||||
assertEquals(1, prog.vertexInputVecSize[5]);
|
||||
|
||||
+3
-18
@@ -52,6 +52,8 @@ class PerFrameBlockInjectorTest {
|
||||
for (Member m : BLOCK.members()) {
|
||||
assertTrue(out.contains(PerFrameBlockInjector.std140Type(m.type()) + " " + m.name() + ";"), "missing member " + m.name());
|
||||
}
|
||||
assertFalse(out.contains("uniform float rainStrength;"));
|
||||
assertTrue(out.contains(" float rainStrength;"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -74,17 +76,12 @@ class PerFrameBlockInjectorTest {
|
||||
assertTrue(out.contains("uniform float rainStrength, packOwnedValue;"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyMemberListIsANoOp() {
|
||||
final String src = "#version 330 core\nvoid main() {}\n";
|
||||
assertEquals(src, ShaderTransformChain.inject(src, new PerFrameUniformBlock(List.of()), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void noBlocksReturnsTheSourceInstance() {
|
||||
final String src = "#version 330 core\nuniform float frameTimeCounter;\nvoid main() {}\n";
|
||||
assertSame(src, ShaderTransformChain.inject(src, null, null));
|
||||
assertSame(src, ShaderTransformChain.inject(src, new PerFrameUniformBlock(List.of()), new PerFrameUniformBlock(List.of())));
|
||||
assertSame(src, ShaderTransformChain.inject(src, new PerFrameUniformBlock(List.of()), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -161,18 +158,6 @@ class PerFrameBlockInjectorTest {
|
||||
memFree(compileOrFail(out, Shaderc.shaderc_fragment_shader));
|
||||
}
|
||||
|
||||
@Test
|
||||
void uniformDeclaredWithTheMatchingTypeIsStillReplaced() {
|
||||
final String out = ShaderTransformChain.inject("""
|
||||
#version 330 core
|
||||
uniform float rainStrength;
|
||||
out vec4 fragColor;
|
||||
void main() { fragColor = vec4(rainStrength); }
|
||||
""", BLOCK, null);
|
||||
assertFalse(out.contains("uniform float rainStrength;"));
|
||||
assertTrue(out.contains(" float rainStrength;"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shaderReferencingNoMemberIsUntouched() {
|
||||
final String src = """
|
||||
|
||||
+33
-149
@@ -61,132 +61,44 @@ class Std140WriterTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void scalarArray_writesEachAtStride16() {
|
||||
final FloatBuffer ubo = outBuf(16);
|
||||
try {
|
||||
Std140Writer.write(ubo, array(/*off*/ 0, /*stride*/ 16, /*len*/ 4, 1, 1), new float[]{1, 2, 3, 4}, 4);
|
||||
assertEq(1f, ubo.get(0), "elem0");
|
||||
assertEq(0f, ubo.get(1), "elem0 pad");
|
||||
assertEq(2f, ubo.get(4), "elem1");
|
||||
assertEq(3f, ubo.get(8), "elem2");
|
||||
assertEq(4f, ubo.get(12), "elem3");
|
||||
} finally {
|
||||
MemoryUtil.memFree(ubo);
|
||||
}
|
||||
private static float[] ramp(int n) {
|
||||
final float[] out = new float[n];
|
||||
for (int i = 0; i < n; i++) out[i] = i + 1;
|
||||
return out;
|
||||
}
|
||||
|
||||
@Test
|
||||
void vec3Array_eachElementAtStride16_zeroPaddedFourthLane() {
|
||||
|
||||
final FloatBuffer ubo = outBuf(16);
|
||||
try {
|
||||
Std140Writer.write(ubo, array(/*off*/ 0, /*stride*/ 16, /*len*/ 4, 3, 1),
|
||||
new float[]{1, 2, 3, 10, 20, 30, 100, 200, 300, -1, -2, -3}, 4);
|
||||
assertEq(1f, ubo.get(0), "e0.x"); assertEq(2f, ubo.get(1), "e0.y"); assertEq(3f, ubo.get(2), "e0.z");
|
||||
assertEq(0f, ubo.get(3), "e0.pad");
|
||||
assertEq(10f, ubo.get(4), "e1.x"); assertEq(20f, ubo.get(5), "e1.y"); assertEq(30f, ubo.get(6), "e1.z");
|
||||
assertEq(0f, ubo.get(7), "e1.pad");
|
||||
assertEq(100f, ubo.get(8), "e2.x"); assertEq(200f, ubo.get(9), "e2.y"); assertEq(300f, ubo.get(10), "e2.z");
|
||||
assertEq(0f, ubo.get(11), "e2.pad");
|
||||
assertEq(-1f, ubo.get(12), "e3.x"); assertEq(-2f, ubo.get(13), "e3.y"); assertEq(-3f, ubo.get(14), "e3.z");
|
||||
assertEq(0f, ubo.get(15), "e3.pad");
|
||||
} finally {
|
||||
MemoryUtil.memFree(ubo);
|
||||
}
|
||||
static Stream<Arguments> layouts() {
|
||||
final float[] mat3Src = {1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
final float[] mat3Ubo = {1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 0};
|
||||
return Stream.of(
|
||||
Arguments.of("scalar array at stride 16", array(0, 16, 4, 1, 1), new float[]{1, 2, 3, 4}, 4,
|
||||
new float[]{1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0}),
|
||||
Arguments.of("vec3 array zero-pads the fourth lane", array(0, 16, 4, 3, 1),
|
||||
new float[]{1, 2, 3, 10, 20, 30, 100, 200, 300, -1, -2, -3}, 4,
|
||||
new float[]{1, 2, 3, 0, 10, 20, 30, 0, 100, 200, 300, 0, -1, -2, -3, 0}),
|
||||
Arguments.of("vec4 array needs no padding", array(0, 16, 2, 4, 1), ramp(8), 2, ramp(8)),
|
||||
Arguments.of("mat2 columns are vec4 each", single(0, 32, 2, 2), new float[]{1, 2, 3, 4}, 1,
|
||||
new float[]{1, 2, 0, 0, 3, 4, 0, 0}),
|
||||
Arguments.of("mat3 columns padded to vec4", single(0, 48, 3, 3), mat3Src, 1, mat3Ubo),
|
||||
Arguments.of("mat3 padded when reflection under-reports size", single(0, 36, 3, 3), mat3Src, 1, mat3Ubo),
|
||||
Arguments.of("mat4 needs no padding", single(0, 64, 4, 4), ramp(16), 1, ramp(16)),
|
||||
Arguments.of("mat3 array pads each matrix", array(0, 48, 2, 3, 3),
|
||||
new float[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90}, 2,
|
||||
new float[]{1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 0, 10, 20, 30, 0, 40, 50, 60, 0, 70, 80, 90, 0}),
|
||||
Arguments.of("mat4 array at stride 64", array(0, 64, 2, 4, 4), ramp(32), 2, ramp(32)),
|
||||
Arguments.of("srcCount clamped to array length", array(0, 16, 2, 1, 1), new float[]{1, 2, 3, 4}, 4,
|
||||
new float[]{1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}),
|
||||
Arguments.of("srcCount clamped to source size", array(0, 16, 4, 1, 1), new float[]{7, 8}, 4,
|
||||
new float[]{7, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void vec4Array_eachElementAtStride16_noPaddingNeeded() {
|
||||
final FloatBuffer ubo = outBuf(8);
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("layouts")
|
||||
void writesLayout(String name, ShaderManager.UniformMemberInfo info, float[] src, int srcCount, float[] expectedUbo) {
|
||||
final FloatBuffer ubo = outBuf(expectedUbo.length);
|
||||
try {
|
||||
Std140Writer.write(ubo, array(/*off*/ 0, /*stride*/ 16, /*len*/ 2, 4, 1), new float[]{1, 2, 3, 4, 5, 6, 7, 8}, 2);
|
||||
assertEq(1f, ubo.get(0)); assertEq(2f, ubo.get(1)); assertEq(3f, ubo.get(2)); assertEq(4f, ubo.get(3));
|
||||
assertEq(5f, ubo.get(4)); assertEq(6f, ubo.get(5)); assertEq(7f, ubo.get(6)); assertEq(8f, ubo.get(7));
|
||||
} finally {
|
||||
MemoryUtil.memFree(ubo);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void mat2_threeColsTimesVec4Each() {
|
||||
final FloatBuffer ubo = outBuf(8);
|
||||
try {
|
||||
Std140Writer.write(ubo, single(/*off*/ 0, /*size*/ 32, /*rows*/ 2, /*cols*/ 2), new float[]{1, 2, 3, 4}, 1);
|
||||
assertEq(1f, ubo.get(0), "c0.x"); assertEq(2f, ubo.get(1), "c0.y");
|
||||
assertEq(0f, ubo.get(2), "c0.pad2"); assertEq(0f, ubo.get(3), "c0.pad3");
|
||||
assertEq(3f, ubo.get(4), "c1.x"); assertEq(4f, ubo.get(5), "c1.y");
|
||||
assertEq(0f, ubo.get(6), "c1.pad2"); assertEq(0f, ubo.get(7), "c1.pad3");
|
||||
} finally {
|
||||
MemoryUtil.memFree(ubo);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void mat3_threeColsEachPaddedToVec4() {
|
||||
final FloatBuffer ubo = outBuf(12);
|
||||
try {
|
||||
Std140Writer.write(ubo, single(/*off*/ 0, /*size*/ 48, 3, 3), new float[]{1, 2, 3, 4, 5, 6, 7, 8, 9}, 1);
|
||||
assertEq(1f, ubo.get(0)); assertEq(2f, ubo.get(1)); assertEq(3f, ubo.get(2)); assertEq(0f, ubo.get(3));
|
||||
assertEq(4f, ubo.get(4)); assertEq(5f, ubo.get(5)); assertEq(6f, ubo.get(6)); assertEq(0f, ubo.get(7));
|
||||
assertEq(7f, ubo.get(8)); assertEq(8f, ubo.get(9)); assertEq(9f, ubo.get(10)); assertEq(0f, ubo.get(11));
|
||||
} finally {
|
||||
MemoryUtil.memFree(ubo);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void mat3_reflectionUnderreportsSize_paddingStillApplied() {
|
||||
final FloatBuffer ubo = outBuf(12);
|
||||
try {
|
||||
Std140Writer.write(ubo, single(/*off*/ 0, /*size*/ 36, 3, 3), new float[]{1, 2, 3, 4, 5, 6, 7, 8, 9}, 1);
|
||||
assertEq(1f, ubo.get(0)); assertEq(2f, ubo.get(1)); assertEq(3f, ubo.get(2)); assertEq(0f, ubo.get(3));
|
||||
assertEq(4f, ubo.get(4)); assertEq(5f, ubo.get(5)); assertEq(6f, ubo.get(6)); assertEq(0f, ubo.get(7));
|
||||
assertEq(7f, ubo.get(8)); assertEq(8f, ubo.get(9)); assertEq(9f, ubo.get(10)); assertEq(0f, ubo.get(11));
|
||||
} finally {
|
||||
MemoryUtil.memFree(ubo);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void mat4_fourColsNoPaddingNeeded() {
|
||||
final FloatBuffer ubo = outBuf(16);
|
||||
try {
|
||||
Std140Writer.write(ubo, single(/*off*/ 0, /*size*/ 64, 4, 4), new float[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, 1);
|
||||
for (int i = 0; i < 16; i++) assertEq(i + 1, ubo.get(i), "[" + i + "]");
|
||||
} finally {
|
||||
MemoryUtil.memFree(ubo);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void mat3Array_perElementStride48_eachMatrixGetsItsOwnPadding() {
|
||||
final FloatBuffer ubo = outBuf(24);
|
||||
try {
|
||||
Std140Writer.write(ubo, array(/*off*/ 0, /*stride*/ 48, /*len*/ 2, 3, 3),
|
||||
new float[]{
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, // matrix 0 (col-major packed)
|
||||
10, 20, 30, 40, 50, 60, 70, 80, 90 // matrix 1
|
||||
}, 2);
|
||||
assertEq(1f, ubo.get(0)); assertEq(2f, ubo.get(1)); assertEq(3f, ubo.get(2)); assertEq(0f, ubo.get(3));
|
||||
assertEq(4f, ubo.get(4)); assertEq(5f, ubo.get(5)); assertEq(6f, ubo.get(6)); assertEq(0f, ubo.get(7));
|
||||
assertEq(7f, ubo.get(8)); assertEq(8f, ubo.get(9)); assertEq(9f, ubo.get(10)); assertEq(0f, ubo.get(11));
|
||||
assertEq(10f, ubo.get(12)); assertEq(20f, ubo.get(13)); assertEq(30f, ubo.get(14)); assertEq(0f, ubo.get(15));
|
||||
assertEq(40f, ubo.get(16)); assertEq(50f, ubo.get(17)); assertEq(60f, ubo.get(18)); assertEq(0f, ubo.get(19));
|
||||
assertEq(70f, ubo.get(20)); assertEq(80f, ubo.get(21)); assertEq(90f, ubo.get(22)); assertEq(0f, ubo.get(23));
|
||||
} finally {
|
||||
MemoryUtil.memFree(ubo);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void mat4Array_perElementStride64() {
|
||||
final FloatBuffer ubo = outBuf(32);
|
||||
try {
|
||||
final float[] data = new float[32];
|
||||
for (int i = 0; i < 32; i++) data[i] = i;
|
||||
Std140Writer.write(ubo, array(/*off*/ 0, /*stride*/ 64, /*len*/ 2, 4, 4), data, 2);
|
||||
for (int i = 0; i < 32; i++) assertEq(i, ubo.get(i), "[" + i + "]");
|
||||
Std140Writer.write(ubo, info, src, srcCount);
|
||||
for (int i = 0; i < expectedUbo.length; i++) assertEq(expectedUbo[i], ubo.get(i), "[" + i + "]");
|
||||
} finally {
|
||||
MemoryUtil.memFree(ubo);
|
||||
}
|
||||
@@ -208,34 +120,6 @@ class Std140WriterTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void srcCount_clampedToArrayLen() {
|
||||
final FloatBuffer ubo = outBuf(16);
|
||||
try {
|
||||
Std140Writer.write(ubo, array(/*off*/ 0, /*stride*/ 16, /*len*/ 2, 1, 1), new float[]{1, 2, 3, 4}, 4);
|
||||
assertEq(1f, ubo.get(0));
|
||||
assertEq(2f, ubo.get(4));
|
||||
assertEq(0f, ubo.get(8)); // not written
|
||||
assertEq(0f, ubo.get(12)); // not written
|
||||
} finally {
|
||||
MemoryUtil.memFree(ubo);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void srcCount_clampedToSrcSize() {
|
||||
final FloatBuffer ubo = outBuf(16);
|
||||
try {
|
||||
Std140Writer.write(ubo, array(/*off*/ 0, /*stride*/ 16, /*len*/ 4, 1, 1), new float[]{7, 8}, 4);
|
||||
assertEq(7f, ubo.get(0));
|
||||
assertEq(8f, ubo.get(4));
|
||||
assertEq(0f, ubo.get(8)); // beyond src
|
||||
assertEq(0f, ubo.get(12));
|
||||
} finally {
|
||||
MemoryUtil.memFree(ubo);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void degenerateTypeDescriptor_throws() {
|
||||
assertThrows(IllegalStateException.class, () -> {
|
||||
|
||||
+11
-20
@@ -2,6 +2,8 @@ package com.gtnewhorizons.angelica.sdlgpu.shader;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.lwjgl.util.shaderc.Shaderc;
|
||||
|
||||
import java.util.List;
|
||||
@@ -30,12 +32,16 @@ class UscaledRetypeTest {
|
||||
assertTrue(out.contains("v_uv = a_TexCoord * 0.5;"), out);
|
||||
}
|
||||
|
||||
@Test
|
||||
void signedPromotedWidthSwizzlesDown() {
|
||||
final String out = UscaledRetype.retype(VS, List.of(new UscaledRetype.Attrib("at_midBlock", 0, 3, 4, true)));
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@CsvSource(delimiter = '|', value = {
|
||||
"signed promoted width swizzles down|4|in ivec4 at_midBlock_i|vec3 at_midBlock = vec3(at_midBlock_i.xyz);",
|
||||
"narrower bound width pads with GL attribute defaults|2|in ivec2 at_midBlock_i|vec3 at_midBlock = vec3(vec2(at_midBlock_i), 0.0);"
|
||||
})
|
||||
void signedRetypeAdaptsToTheBoundWidth(String label, int boundWidth, String decl, String conversion) {
|
||||
final String out = UscaledRetype.retype(VS, List.of(new UscaledRetype.Attrib("at_midBlock", 0, 3, boundWidth, true)));
|
||||
assertNotNull(out);
|
||||
assertTrue(out.contains("in ivec4 at_midBlock_i"), out);
|
||||
assertTrue(out.contains("vec3 at_midBlock = vec3(at_midBlock_i.xyz);"), out);
|
||||
assertTrue(out.contains(decl), out);
|
||||
assertTrue(out.contains(conversion), out);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -58,14 +64,6 @@ class UscaledRetypeTest {
|
||||
assertNull(UscaledRetype.retype(VS, List.of(new UscaledRetype.Attrib("not_present", 0, 2, 2, false))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void narrowerBoundWidthPadsWithGlAttributeDefaults() {
|
||||
final String out = UscaledRetype.retype(VS, List.of(new UscaledRetype.Attrib("at_midBlock", 0, 3, 2, true)));
|
||||
assertNotNull(out);
|
||||
assertTrue(out.contains("in ivec2 at_midBlock_i"), out);
|
||||
assertTrue(out.contains("vec3 at_midBlock = vec3(vec2(at_midBlock_i), 0.0);"), out);
|
||||
}
|
||||
|
||||
@Test
|
||||
void vec4FromShort2PadsZeroThenOne() {
|
||||
final String src = """
|
||||
@@ -128,11 +126,4 @@ class UscaledRetypeTest {
|
||||
assertTrue(out.contains("in uvec2 a_TexCoord_i"), out);
|
||||
assertTrue(out.contains("vec2 a_TexCoord = vec2(a_TexCoord_i);"), out);
|
||||
}
|
||||
|
||||
@Test
|
||||
void retypedInputKeepsTheNameSuffixTheLocationFixupKeysOn() {
|
||||
final String out = UscaledRetype.retype(VS, List.of(new UscaledRetype.Attrib("a_TexCoord", 4, 2, 2, false)));
|
||||
assertNotNull(out);
|
||||
assertTrue(out.contains("a_TexCoord" + UscaledRetype.SUFFIX), out);
|
||||
}
|
||||
}
|
||||
+71
-155
@@ -50,48 +50,28 @@ class MslCrossCompileTest {
|
||||
|
||||
@Test
|
||||
void vertexShaderCrossCompiles() {
|
||||
final ByteBuffer spirv = compileOrFail(SpirvTestShaders.VERTEX_GLSL, Shaderc.shaderc_vertex_shader);
|
||||
try {
|
||||
ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_VERTEX_SHADER);
|
||||
final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL20.GL_VERTEX_SHADER, false);
|
||||
try {
|
||||
final String msl = decode(out.code());
|
||||
assertTrue(msl.length() > 0, "MSL output non-empty");
|
||||
assertTrue(msl.contains("vertex "), "MSL should contain vertex stage qualifier:\n" + msl);
|
||||
assertFalse(msl.contains("descriptor_set"), "MSL must not leak descriptor_set decorations:\n" + msl);
|
||||
assertTrue(msl.contains("[[buffer(0)]]"), "UBO should land at [[buffer(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(0)]]"), "sampler2D should land at [[texture(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[sampler(0)]]"), "sampler2D should land at [[sampler(0)]]:\n" + msl);
|
||||
assertEquals("main0", out.entrypoint(), "Metal forbids main; expect main0");
|
||||
} finally {
|
||||
MemoryUtil.memFree(out.code());
|
||||
}
|
||||
} finally {
|
||||
MemoryUtil.memFree(spirv);
|
||||
}
|
||||
final Msl out = crossCompile(SpirvTestShaders.VERTEX_GLSL, Shaderc.shaderc_vertex_shader, GL20.GL_VERTEX_SHADER, false);
|
||||
final String msl = out.code();
|
||||
assertTrue(msl.length() > 0, "MSL output non-empty");
|
||||
assertTrue(msl.contains("vertex "), "MSL should contain vertex stage qualifier:\n" + msl);
|
||||
assertFalse(msl.contains("descriptor_set"), "MSL must not leak descriptor_set decorations:\n" + msl);
|
||||
assertTrue(msl.contains("[[buffer(0)]]"), "UBO should land at [[buffer(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(0)]]"), "sampler2D should land at [[texture(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[sampler(0)]]"), "sampler2D should land at [[sampler(0)]]:\n" + msl);
|
||||
assertEquals("main0", out.entrypoint(), "Metal forbids main; expect main0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fragmentShaderCrossCompiles() {
|
||||
final ByteBuffer spirv = compileOrFail(SpirvTestShaders.FRAGMENT_GLSL, Shaderc.shaderc_fragment_shader);
|
||||
try {
|
||||
ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_FRAGMENT_SHADER);
|
||||
final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL20.GL_FRAGMENT_SHADER, false);
|
||||
try {
|
||||
final String msl = decode(out.code());
|
||||
assertTrue(msl.contains("fragment "), "MSL should contain fragment stage qualifier:\n" + msl);
|
||||
assertTrue(msl.contains("[[buffer(0)]]"), "Fragment UBO should land at [[buffer(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(0)]]"), "First sampler at [[texture(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(1)]]"), "Second sampler at [[texture(1)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[sampler(0)]]") && msl.contains("[[sampler(1)]]"),
|
||||
"Samplers at slots 0 and 1:\n" + msl);
|
||||
assertEquals("main0", out.entrypoint());
|
||||
} finally {
|
||||
MemoryUtil.memFree(out.code());
|
||||
}
|
||||
} finally {
|
||||
MemoryUtil.memFree(spirv);
|
||||
}
|
||||
final Msl out = crossCompile(SpirvTestShaders.FRAGMENT_GLSL, Shaderc.shaderc_fragment_shader, GL20.GL_FRAGMENT_SHADER, false);
|
||||
final String msl = out.code();
|
||||
assertTrue(msl.contains("fragment "), "MSL should contain fragment stage qualifier:\n" + msl);
|
||||
assertTrue(msl.contains("[[buffer(0)]]"), "Fragment UBO should land at [[buffer(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(0)]]"), "First sampler at [[texture(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(1)]]"), "Second sampler at [[texture(1)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[sampler(0)]]") && msl.contains("[[sampler(1)]]"),
|
||||
"Samplers at slots 0 and 1:\n" + msl);
|
||||
assertEquals("main0", out.entrypoint());
|
||||
}
|
||||
|
||||
private static final String CHUNK_CULL_GLSL = """
|
||||
@@ -111,27 +91,17 @@ class MslCrossCompileTest {
|
||||
|
||||
@Test
|
||||
void ldsMultiMipComputeCrossCompiles() {
|
||||
final ByteBuffer spirv = compileOrFail(SpirvTestShaders.LDS_MULTI_MIP_GLSL, Shaderc.shaderc_compute_shader);
|
||||
try {
|
||||
ShaderManager.remapSpirvForComputeSDLGPU(spirv);
|
||||
final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL43.GL_COMPUTE_SHADER, false);
|
||||
try {
|
||||
final String msl = decode(out.code());
|
||||
assertTrue(msl.contains("kernel "), "Compute MSL should contain kernel stage qualifier:\n" + msl);
|
||||
assertTrue(msl.contains("threadgroup spvUnsafeArray<float") || msl.contains("threadgroup float"), "shared float array must become threadgroup memory:\n" + msl);
|
||||
assertTrue(msl.contains("threadgroup_barrier"), "barrier() must become threadgroup_barrier:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(0)]]"), "u_Src RW image at [[texture(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(1)]]"), "u_Dst1 at [[texture(1)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(2)]]"), "u_Dst2 at [[texture(2)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[buffer(0)]]"), "Down UBO at [[buffer(0)]]:\n" + msl);
|
||||
assertFalse(msl.contains("descriptor_set"), "MSL must not leak descriptor_set decorations:\n" + msl);
|
||||
assertEquals("main0", out.entrypoint());
|
||||
} finally {
|
||||
MemoryUtil.memFree(out.code());
|
||||
}
|
||||
} finally {
|
||||
MemoryUtil.memFree(spirv);
|
||||
}
|
||||
final Msl out = crossCompile(SpirvTestShaders.LDS_MULTI_MIP_GLSL, Shaderc.shaderc_compute_shader, GL43.GL_COMPUTE_SHADER, false);
|
||||
final String msl = out.code();
|
||||
assertTrue(msl.contains("kernel "), "Compute MSL should contain kernel stage qualifier:\n" + msl);
|
||||
assertTrue(msl.contains("threadgroup spvUnsafeArray<float") || msl.contains("threadgroup float"), "shared float array must become threadgroup memory:\n" + msl);
|
||||
assertTrue(msl.contains("threadgroup_barrier"), "barrier() must become threadgroup_barrier:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(0)]]"), "u_Src RW image at [[texture(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(1)]]"), "u_Dst1 at [[texture(1)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(2)]]"), "u_Dst2 at [[texture(2)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[buffer(0)]]"), "Down UBO at [[buffer(0)]]:\n" + msl);
|
||||
assertFalse(msl.contains("descriptor_set"), "MSL must not leak descriptor_set decorations:\n" + msl);
|
||||
assertEquals("main0", out.entrypoint());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,47 +111,27 @@ class MslCrossCompileTest {
|
||||
@Test
|
||||
void clearImage3dComputeCrossCompiles() {
|
||||
final String glsl = TestPaths.readString("src/main/resources/assets/angelica/shaders/sdlgpu/clear_image3d.csh");
|
||||
final ByteBuffer spirv = compileOrFail(glsl, Shaderc.shaderc_compute_shader);
|
||||
try {
|
||||
ShaderManager.remapSpirvForComputeSDLGPU(spirv);
|
||||
final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL43.GL_COMPUTE_SHADER, false);
|
||||
try {
|
||||
final String msl = decode(out.code());
|
||||
assertTrue(msl.contains("kernel "), "compute MSL should contain kernel stage qualifier:\n" + msl);
|
||||
assertTrue(msl.contains("texture3d<uint"), "formatless writeonly uimage3D must resolve to a uint 3D texture:\n" + msl);
|
||||
assertTrue(msl.contains("access::write"), "the image must carry write access:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(0)]]"), "the target image should land at [[texture(0)]]:\n" + msl);
|
||||
assertEquals("main0", out.entrypoint());
|
||||
} finally {
|
||||
MemoryUtil.memFree(out.code());
|
||||
}
|
||||
} finally {
|
||||
MemoryUtil.memFree(spirv);
|
||||
}
|
||||
final Msl out = crossCompile(glsl, Shaderc.shaderc_compute_shader, GL43.GL_COMPUTE_SHADER, false);
|
||||
final String msl = out.code();
|
||||
assertTrue(msl.contains("kernel "), "compute MSL should contain kernel stage qualifier:\n" + msl);
|
||||
assertTrue(msl.contains("texture3d<uint"), "formatless writeonly uimage3D must resolve to a uint 3D texture:\n" + msl);
|
||||
assertTrue(msl.contains("access::write"), "the image must carry write access:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(0)]]"), "the target image should land at [[texture(0)]]:\n" + msl);
|
||||
assertEquals("main0", out.entrypoint());
|
||||
}
|
||||
|
||||
@Test
|
||||
void chunkCullComputeCrossCompiles() {
|
||||
final ByteBuffer spirv = compileOrFail(CHUNK_CULL_GLSL, Shaderc.shaderc_compute_shader);
|
||||
try {
|
||||
ShaderManager.remapSpirvForComputeSDLGPU(spirv);
|
||||
final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL43.GL_COMPUTE_SHADER, false);
|
||||
try {
|
||||
final String msl = decode(out.code());
|
||||
assertTrue(msl.contains("kernel "), "Compute MSL should contain kernel stage qualifier:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(0)]]"), "u_Hiz at [[texture(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[sampler(0)]]"), "u_Hiz sampler at [[sampler(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[buffer(0)]]"), "Frustum UBO at [[buffer(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[buffer(1)]]"), "visible RO SSBO at [[buffer(1)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[buffer(2)]]"), "meta RO SSBO at [[buffer(2)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[buffer(3)]]"), "indirect RW SSBO at [[buffer(3)]] (numUBOs+numROBuf=3):\n" + msl);
|
||||
assertEquals("main0", out.entrypoint());
|
||||
} finally {
|
||||
MemoryUtil.memFree(out.code());
|
||||
}
|
||||
} finally {
|
||||
MemoryUtil.memFree(spirv);
|
||||
}
|
||||
final Msl out = crossCompile(CHUNK_CULL_GLSL, Shaderc.shaderc_compute_shader, GL43.GL_COMPUTE_SHADER, false);
|
||||
final String msl = out.code();
|
||||
assertTrue(msl.contains("kernel "), "Compute MSL should contain kernel stage qualifier:\n" + msl);
|
||||
assertTrue(msl.contains("[[texture(0)]]"), "u_Hiz at [[texture(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[sampler(0)]]"), "u_Hiz sampler at [[sampler(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[buffer(0)]]"), "Frustum UBO at [[buffer(0)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[buffer(1)]]"), "visible RO SSBO at [[buffer(1)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[buffer(2)]]"), "meta RO SSBO at [[buffer(2)]]:\n" + msl);
|
||||
assertTrue(msl.contains("[[buffer(3)]]"), "indirect RW SSBO at [[buffer(3)]] (numUBOs+numROBuf=3):\n" + msl);
|
||||
assertEquals("main0", out.entrypoint());
|
||||
}
|
||||
|
||||
private static final String VERTEX_WRITES_IMAGE_GLSL = """
|
||||
@@ -197,22 +147,12 @@ class MslCrossCompileTest {
|
||||
|
||||
@Test
|
||||
void vertexShaderWritingStorageImageStillReturnsPosition() {
|
||||
final ByteBuffer spirv = compileOrFail(VERTEX_WRITES_IMAGE_GLSL, Shaderc.shaderc_vertex_shader);
|
||||
try {
|
||||
ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_VERTEX_SHADER);
|
||||
final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL20.GL_VERTEX_SHADER, false);
|
||||
try {
|
||||
final String msl = decode(out.code());
|
||||
assertFalse(msl.contains("vertex void main0"),
|
||||
"vertex stage must not be emitted with void return when gl_Position is written:\n" + msl);
|
||||
assertTrue(msl.contains("[[position]]"),
|
||||
"vertex output struct should include [[position]] decoration:\n" + msl);
|
||||
} finally {
|
||||
MemoryUtil.memFree(out.code());
|
||||
}
|
||||
} finally {
|
||||
MemoryUtil.memFree(spirv);
|
||||
}
|
||||
final Msl out = crossCompile(VERTEX_WRITES_IMAGE_GLSL, Shaderc.shaderc_vertex_shader, GL20.GL_VERTEX_SHADER, false);
|
||||
final String msl = out.code();
|
||||
assertFalse(msl.contains("vertex void main0"),
|
||||
"vertex stage must not be emitted with void return when gl_Position is written:\n" + msl);
|
||||
assertTrue(msl.contains("[[position]]"),
|
||||
"vertex output struct should include [[position]] decoration:\n" + msl);
|
||||
}
|
||||
|
||||
private static final String CPP_KEYWORD_LOCAL_GLSL = """
|
||||
@@ -231,20 +171,10 @@ class MslCrossCompileTest {
|
||||
void cppKeywordLocalIsRenamedBeforeMsl() {
|
||||
final GlslVulkanPreprocess.Result pre = GlslVulkanPreprocess.run(CPP_KEYWORD_LOCAL_GLSL, GL20.GL_FRAGMENT_SHADER, "test", true);
|
||||
assertNotNull(pre);
|
||||
final ByteBuffer spirv = compileOrFail(pre.rewrittenSource(), Shaderc.shaderc_fragment_shader);
|
||||
try {
|
||||
ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_FRAGMENT_SHADER);
|
||||
final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL20.GL_FRAGMENT_SHADER, false);
|
||||
try {
|
||||
final String msl = decode(out.code());
|
||||
assertTrue(msl.contains("angelica_renamed_try"), "the local must keep a renamed identifier:\n" + msl);
|
||||
assertFalse(Pattern.compile("\\btry\\b").matcher(msl).find(), "try is reserved in MSL:\n" + msl);
|
||||
} finally {
|
||||
MemoryUtil.memFree(out.code());
|
||||
}
|
||||
} finally {
|
||||
MemoryUtil.memFree(spirv);
|
||||
}
|
||||
final Msl out = crossCompile(pre.rewrittenSource(), Shaderc.shaderc_fragment_shader, GL20.GL_FRAGMENT_SHADER, false);
|
||||
final String msl = out.code();
|
||||
assertTrue(msl.contains("angelica_renamed_try"), "the local must keep a renamed identifier:\n" + msl);
|
||||
assertFalse(Pattern.compile("\\btry\\b").matcher(msl).find(), "try is reserved in MSL:\n" + msl);
|
||||
}
|
||||
|
||||
// Mimics the shape of RwImageStoreExtractor's chunk-vertex compute output:
|
||||
@@ -356,19 +286,9 @@ class MslCrossCompileTest {
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("computeShadersWithImageAtomics")
|
||||
void computeWithImageAtomic_mslBufferSlotsAreUnique(String name, String glsl) {
|
||||
final ByteBuffer spirv = compileOrFail(glsl, Shaderc.shaderc_compute_shader);
|
||||
try {
|
||||
ShaderManager.remapSpirvForComputeSDLGPU(spirv);
|
||||
final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL43.GL_COMPUTE_SHADER, false);
|
||||
try {
|
||||
final String msl = decode(out.code());
|
||||
assertUniqueBufferSlots(msl);
|
||||
} finally {
|
||||
MemoryUtil.memFree(out.code());
|
||||
}
|
||||
} finally {
|
||||
MemoryUtil.memFree(spirv);
|
||||
}
|
||||
final Msl out = crossCompile(glsl, Shaderc.shaderc_compute_shader, GL43.GL_COMPUTE_SHADER, false);
|
||||
final String msl = out.code();
|
||||
assertUniqueBufferSlots(msl);
|
||||
}
|
||||
|
||||
private static void assertUniqueBufferSlots(String msl) {
|
||||
@@ -407,18 +327,7 @@ class MslCrossCompileTest {
|
||||
final String[] names = { "chunk_cull.csh" };
|
||||
for (String name : names) {
|
||||
final String glsl = TestPaths.readString("src/main/resources/assets/angelica/shaders/culling/" + name);
|
||||
final ByteBuffer spirv = compileOrFail(glsl, Shaderc.shaderc_compute_shader);
|
||||
try {
|
||||
ShaderManager.remapSpirvForComputeSDLGPU(spirv);
|
||||
final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL43.GL_COMPUTE_SHADER, false);
|
||||
try {
|
||||
assertTrue(decode(out.code()).contains("kernel "), name + " should produce compute MSL");
|
||||
} finally {
|
||||
MemoryUtil.memFree(out.code());
|
||||
}
|
||||
} finally {
|
||||
MemoryUtil.memFree(spirv);
|
||||
}
|
||||
assertTrue(crossCompile(glsl, Shaderc.shaderc_compute_shader, GL43.GL_COMPUTE_SHADER, false).code().contains("kernel "), name + " should produce compute MSL");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,12 +416,19 @@ class MslCrossCompileTest {
|
||||
}
|
||||
|
||||
private static String computeMsl(String glsl, boolean textureAtomics) {
|
||||
final ByteBuffer spirv = compileOrFail(glsl, Shaderc.shaderc_compute_shader);
|
||||
return crossCompile(glsl, Shaderc.shaderc_compute_shader, GL43.GL_COMPUTE_SHADER, textureAtomics).code();
|
||||
}
|
||||
|
||||
private record Msl(String code, String entrypoint) {}
|
||||
|
||||
private static Msl crossCompile(String glsl, int shaderKind, int glType, boolean textureAtomics) {
|
||||
final ByteBuffer spirv = compileOrFail(glsl, shaderKind);
|
||||
try {
|
||||
ShaderManager.remapSpirvForComputeSDLGPU(spirv);
|
||||
final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL43.GL_COMPUTE_SHADER, textureAtomics);
|
||||
if (glType == GL43.GL_COMPUTE_SHADER) ShaderManager.remapSpirvForComputeSDLGPU(spirv);
|
||||
else ShaderManager.remapSpirvForSDLGPU(spirv, glType);
|
||||
final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, glType, textureAtomics);
|
||||
try {
|
||||
return decode(out.code());
|
||||
return new Msl(decode(out.code()), out.entrypoint());
|
||||
} finally {
|
||||
MemoryUtil.memFree(out.code());
|
||||
}
|
||||
|
||||
@@ -113,16 +113,12 @@ class GLRedirectsTest {
|
||||
"Foo.bar",
|
||||
"String.valueOf",
|
||||
"ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB",
|
||||
"noDotHere",
|
||||
})
|
||||
void nonRedirectedReturnsNull(String classAndMethod) {
|
||||
assertNull(GLRedirects.getTargetMethodName(classAndMethod));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullDotReturnsNull() {
|
||||
assertNull(GLRedirects.getTargetMethodName("noDotHere"));
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "pattern matches {0}")
|
||||
@CsvSource({
|
||||
"GL11.glPushMatrix",
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.gtnewhorizons.angelica.api.tesr;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@@ -15,12 +17,10 @@ class TesrShadersTest {
|
||||
assertThrows(IllegalStateException.class, () -> TesrShaders.register("test:duplicate", NOOP, NOOP));
|
||||
}
|
||||
|
||||
@Test
|
||||
void malformedNamesThrow() {
|
||||
assertThrows(IllegalArgumentException.class, () -> TesrShaders.register("nonamespace", NOOP, NOOP));
|
||||
assertThrows(IllegalArgumentException.class, () -> TesrShaders.register(":path", NOOP, NOOP));
|
||||
assertThrows(IllegalArgumentException.class, () -> TesrShaders.register("ns:", NOOP, NOOP));
|
||||
assertThrows(IllegalArgumentException.class, () -> TesrShaders.register("a:b:c", NOOP, NOOP));
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"nonamespace", ":path", "ns:", "a:b:c"})
|
||||
void malformedNamesThrow(String name) {
|
||||
assertThrows(IllegalArgumentException.class, () -> TesrShaders.register(name, NOOP, NOOP));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -8,10 +8,12 @@ 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.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
@@ -136,6 +138,18 @@ class TextDrawStateGLTest {
|
||||
moved.apply();
|
||||
GLStateManager.popStateTo(depth);
|
||||
|
||||
final ByteBuffer driverColorMask = BufferUtils.createByteBuffer(16);
|
||||
GL11.glGetBoolean(GL11.GL_COLOR_WRITEMASK, driverColorMask);
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_DEPTH_TEST), "driver depth test");
|
||||
assertEquals(GL11.GL_LESS, GL11.glGetInteger(GL11.GL_DEPTH_FUNC), "driver depth func");
|
||||
assertTrue(GL11.glGetBoolean(GL11.GL_DEPTH_WRITEMASK), "driver depth mask");
|
||||
for (int i = 0; i < 4; i++) assertNotEquals(0, driverColorMask.get(i), "driver color mask channel " + i);
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_CULL_FACE), "driver cull face enable");
|
||||
assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode");
|
||||
assertEquals(GL11.GL_CCW, GL11.glGetInteger(GL11.GL_FRONT_FACE), "driver front face");
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL), "driver polygon offset fill");
|
||||
assertEquals(0.0f, GL11.glGetFloat(GL11.GL_POLYGON_OFFSET_FACTOR), "driver polygon offset factor");
|
||||
|
||||
final TextDrawState after = capture();
|
||||
for (Field field : stateFields()) {
|
||||
if (!isGlState(field)) continue;
|
||||
|
||||
+1
-21
@@ -14,6 +14,7 @@ class DeferredDrawBatcherTest {
|
||||
"basic, 42, 0x0302, 0x0001, true, false",
|
||||
"zero fields, 0, 0, 0, false, true",
|
||||
"largest 20-bit texture id, 1048575, 0x0302, 0x0303, true, true",
|
||||
"dst alpha blend constants, 1, 0x0304, 0x0305, true, true",
|
||||
})
|
||||
void stateKey_roundtrip(String name, int textureId, int srcRgb, int dstRgb, boolean blend, boolean depthMask) {
|
||||
long key = DeferredDrawBatcher.packStateKey(textureId, srcRgb, dstRgb, blend, depthMask, true, true);
|
||||
@@ -33,27 +34,6 @@ class DeferredDrawBatcherTest {
|
||||
assertNotEquals(keyA, keyC, "Different blend funcs should produce different keys");
|
||||
}
|
||||
|
||||
@Test
|
||||
void stateKey_blendConstants() {
|
||||
// Verify all common GL blend constants roundtrip correctly
|
||||
int[] blendConstants = {
|
||||
GL11.GL_ZERO, // 0x0000
|
||||
GL11.GL_ONE, // 0x0001
|
||||
GL11.GL_SRC_ALPHA, // 0x0302
|
||||
GL11.GL_ONE_MINUS_SRC_ALPHA, // 0x0303
|
||||
GL11.GL_DST_ALPHA, // 0x0304
|
||||
GL11.GL_ONE_MINUS_DST_ALPHA, // 0x0305
|
||||
};
|
||||
|
||||
for (int src : blendConstants) {
|
||||
for (int dst : blendConstants) {
|
||||
long key = DeferredDrawBatcher.packStateKey(1, src, dst, true, true, true, true);
|
||||
assertEquals(src, DeferredDrawBatcher.unpackSrcRgb(key), "srcRgb roundtrip failed for 0x" + Integer.toHexString(src));
|
||||
assertEquals(dst, DeferredDrawBatcher.unpackDstRgb(key), "dstRgb roundtrip failed for 0x" + Integer.toHexString(dst));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void stateKey_textureEnableRoundtrip() {
|
||||
// tex0=true, tex1=false
|
||||
|
||||
@@ -70,18 +70,6 @@ class FogAggregateTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void colorEvictionDoesNotDiscardInteriorTotals() {
|
||||
final AtomicInteger color = new AtomicInteger(0x010203);
|
||||
final World world = world(new AtomicBoolean(true), color);
|
||||
final float[] actual = new float[4];
|
||||
FogColourGrid.accumulateInterior(world, 0, 64, 0, 1, actual);
|
||||
color.set(0x040506);
|
||||
FogColourGrid.colour(world, 128, 64, 0);
|
||||
FogColourGrid.accumulateInterior(world, 0, 64, 0, 1, actual);
|
||||
assertBits(new float[] {1,2,3,1}, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
void biomeEvictionDoesNotDiscardInteriorTotals() {
|
||||
final AtomicInteger color = new AtomicInteger(0x010203);
|
||||
|
||||
@@ -4,8 +4,13 @@ import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.BiomeGenBase;
|
||||
import net.minecraft.world.chunk.IChunkProvider;
|
||||
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.mockito.Mockito;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.times;
|
||||
@@ -23,24 +28,23 @@ class FogBiomeCacheTest {
|
||||
return world;
|
||||
}
|
||||
|
||||
@Test
|
||||
void repeatLookupsHitTheCache() {
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("lookupCounts")
|
||||
void worldLookupsPerCoordinateList(String label, boolean chunkLoaded, int[][] xz, int expectedWorldLookups) {
|
||||
final BiomeGenBase biome = Mockito.mock(BiomeGenBase.class);
|
||||
final World world = worldWith(biome, true);
|
||||
final World world = worldWith(biome, chunkLoaded);
|
||||
|
||||
assertSame(biome, FogBiomeCache.get(world, 100, -7));
|
||||
assertSame(biome, FogBiomeCache.get(world, 100, -7));
|
||||
verify(world, times(1)).getBiomeGenForCoords(anyInt(), anyInt());
|
||||
for (int[] c : xz) {
|
||||
assertSame(biome, FogBiomeCache.get(world, c[0], c[1]));
|
||||
}
|
||||
verify(world, times(expectedWorldLookups)).getBiomeGenForCoords(anyInt(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void unloadedChunksAreNotCached() {
|
||||
final BiomeGenBase biome = Mockito.mock(BiomeGenBase.class);
|
||||
final World world = worldWith(biome, false);
|
||||
|
||||
FogBiomeCache.get(world, 5000, 5000);
|
||||
FogBiomeCache.get(world, 5000, 5000);
|
||||
verify(world, times(2)).getBiomeGenForCoords(anyInt(), anyInt());
|
||||
private static Stream<Arguments> lookupCounts() {
|
||||
return Stream.of(
|
||||
Arguments.of("repeat lookups hit the cache", true, new int[][] {{100, -7}, {100, -7}}, 1),
|
||||
Arguments.of("unloaded chunks are not cached", false, new int[][] {{5000, 5000}, {5000, 5000}}, 2),
|
||||
Arguments.of("negative coordinates do not collide", true, new int[][] {{-1, 0}, {0, -1}, {-1, -1}}, 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -54,15 +58,4 @@ class FogBiomeCacheTest {
|
||||
assertSame(b, FogBiomeCache.get(worldB, 0, 0), "same coords in a new world must re-resolve");
|
||||
assertSame(a, FogBiomeCache.get(worldA, 0, 0), "switching back re-resolves rather than serving worldB state");
|
||||
}
|
||||
|
||||
@Test
|
||||
void negativeCoordinatesDoNotCollide() {
|
||||
final BiomeGenBase biome = Mockito.mock(BiomeGenBase.class);
|
||||
final World world = worldWith(biome, true);
|
||||
|
||||
FogBiomeCache.get(world, -1, 0);
|
||||
FogBiomeCache.get(world, 0, -1);
|
||||
FogBiomeCache.get(world, -1, -1);
|
||||
verify(world, times(3)).getBiomeGenForCoords(anyInt(), anyInt());
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,12 @@ import net.minecraft.world.biome.BiomeGenBase;
|
||||
import net.minecraft.world.chunk.IChunkProvider;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
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.function.IntBinaryOperator;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.gtnewhorizons.angelica.compat.bop.BopFogTestSupport.BopFog;
|
||||
|
||||
@@ -64,28 +68,23 @@ class FogColourGridTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void oneBlockShiftIsAColdFill() {
|
||||
final World world = mockWorld(true);
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("requeryCounts")
|
||||
void fogColourCallsPerCoordinateList(String label, boolean chunksLoaded, int[][] xyz, int expectedFogColourCalls) {
|
||||
final World world = mockWorld(chunksLoaded);
|
||||
final BiomeGenBase biome = fixedFogBiome(world, 0x123456);
|
||||
|
||||
FogColourGrid.colour(world, 100, 64, 200);
|
||||
FogColourGrid.colour(world, 100, 64, 200);
|
||||
BopFog.verifyCall(biome, times(1));
|
||||
|
||||
FogColourGrid.colour(world, 101, 64, 200);
|
||||
BopFog.verifyCall(biome, times(2));
|
||||
for (int[] c : xyz) {
|
||||
FogColourGrid.colour(world, c[0], c[1], c[2]);
|
||||
}
|
||||
BopFog.verifyCall(biome, times(expectedFogColourCalls));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonCacheableCellIsRequeried() {
|
||||
final World world = mockWorld(false);
|
||||
final BiomeGenBase biome = fixedFogBiome(world, 0x654321);
|
||||
|
||||
FogColourGrid.colour(world, 5, 64, 9);
|
||||
FogColourGrid.colour(world, 5, 64, 9);
|
||||
|
||||
BopFog.verifyCall(biome, times(2));
|
||||
private static Stream<Arguments> requeryCounts() {
|
||||
return Stream.of(
|
||||
Arguments.of("non-cacheable cell is requeried", false, new int[][] {{5, 64, 9}, {5, 64, 9}}, 2),
|
||||
Arguments.of("loaded repeat hits the cache", true, new int[][] {{100, 64, 200}, {100, 64, 200}}, 1),
|
||||
Arguments.of("one block shift is a cold fill", true, new int[][] {{100, 64, 200}, {100, 64, 200}, {101, 64, 200}}, 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -19,6 +19,7 @@ import net.coderbot.iris.pipeline.FixedFunctionWorldRenderingPipeline;
|
||||
import net.coderbot.iris.pipeline.PipelineManager;
|
||||
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
|
||||
import net.coderbot.iris.uniforms.CapturedRenderingState;
|
||||
import net.coderbot.iris.uniforms.CapturedStateScope;
|
||||
import net.coderbot.iris.uniforms.ItemIdManager;
|
||||
import net.coderbot.iris.uniforms.ItemMaterialHelper;
|
||||
import net.coderbot.iris.shaderpack.materialmap.NamespacedId;
|
||||
@@ -34,6 +35,7 @@ import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
@@ -169,6 +171,11 @@ class IrisDisplayListStateTest {
|
||||
try {
|
||||
GLStateManager.glCallList(parent);
|
||||
assertTrue(GLStateManager.isEffectiveBlendEnabled());
|
||||
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND), "driver blend enable");
|
||||
assertEquals(GL11.GL_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb");
|
||||
assertEquals(GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_DST_RGB), "driver blend dst rgb");
|
||||
assertEquals(GL11.GL_ZERO, GL11.glGetInteger(GL14.GL_BLEND_SRC_ALPHA), "driver blend src alpha");
|
||||
assertEquals(GL11.GL_ONE, GL11.glGetInteger(GL14.GL_BLEND_DST_ALPHA), "driver blend dst alpha");
|
||||
final var blend = GLStateManager.getEffectiveBlendState(new com.gtnewhorizons.angelica.glsm.states.BlendState());
|
||||
assertEquals(GL11.GL_SRC_ALPHA, blend.getSrcRgb());
|
||||
assertEquals(GL11.GL_ONE_MINUS_SRC_ALPHA, blend.getDstRgb());
|
||||
@@ -187,31 +194,24 @@ class IrisDisplayListStateTest {
|
||||
@Test
|
||||
void idsAndColorReplayInsideNestedScopesAndRestoreThePlaybackCaller() {
|
||||
final CapturedRenderingState state = CapturedRenderingState.INSTANCE;
|
||||
state.pushCurrentEntityAndItem();
|
||||
state.pushCurrentBlockEntity();
|
||||
state.pushCurrentEntityColor();
|
||||
try {
|
||||
try (CapturedStateScope ignored = new CapturedStateScope()) {
|
||||
for (int mode : new int[] {GL11.GL_COMPILE, GL11.GL_COMPILE_AND_EXECUTE}) {
|
||||
state.setCurrentEntityAndItem(10, 11);
|
||||
state.setCurrentBlockEntity(12);
|
||||
state.setCurrentEntityColor(1, 0, 0, 0.5f);
|
||||
final int child = newList();
|
||||
GLStateManager.glNewList(child, mode);
|
||||
state.pushCurrentEntityAndItem();
|
||||
state.pushCurrentBlockEntity();
|
||||
state.pushCurrentEntityColor();
|
||||
state.setCurrentEntityAndItem(45010, 45011);
|
||||
state.setCurrentBlockEntity(45012);
|
||||
state.setCurrentEntityColor(0, 1, 0, 0.25f);
|
||||
DisplayListManager.recordStateCommand(() -> {
|
||||
assertEquals(45010, state.getCurrentRenderedEntity());
|
||||
assertEquals(45011, state.getCurrentRenderedItem());
|
||||
assertEquals(45012, state.getCurrentRenderedBlockEntity());
|
||||
assertTrue(state.getCurrentEntityColor().equals(0, 1, 0, 0.25f));
|
||||
});
|
||||
state.popCurrentEntityColor();
|
||||
state.popCurrentBlockEntity();
|
||||
state.popCurrentEntityAndItem();
|
||||
try (CapturedStateScope nestedIgnored = new CapturedStateScope()) {
|
||||
state.setCurrentEntityAndItem(45010, 45011);
|
||||
state.setCurrentBlockEntity(45012);
|
||||
state.setCurrentEntityColor(0, 1, 0, 0.25f);
|
||||
DisplayListManager.recordStateCommand(() -> {
|
||||
assertEquals(45010, state.getCurrentRenderedEntity());
|
||||
assertEquals(45011, state.getCurrentRenderedItem());
|
||||
assertEquals(45012, state.getCurrentRenderedBlockEntity());
|
||||
assertTrue(state.getCurrentEntityColor().equals(0, 1, 0, 0.25f));
|
||||
});
|
||||
}
|
||||
GLStateManager.glEndList();
|
||||
|
||||
final int parent = newList();
|
||||
@@ -234,20 +234,13 @@ class IrisDisplayListStateTest {
|
||||
assertTrue(state.getCurrentEntityColor().equals(0, 0, 1, 0.75f));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
state.popCurrentEntityColor();
|
||||
state.popCurrentBlockEntity();
|
||||
state.popCurrentEntityAndItem();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitStateRecordsEvenWhenUnchangedAndOnlyDeduplicatesAdjacentCommands() {
|
||||
final CapturedRenderingState state = CapturedRenderingState.INSTANCE;
|
||||
state.pushCurrentEntityAndItem();
|
||||
state.pushCurrentBlockEntity();
|
||||
state.pushCurrentEntityColor();
|
||||
try {
|
||||
try (CapturedStateScope ignored = new CapturedStateScope()) {
|
||||
state.setCurrentEntityAndItem(7, 8);
|
||||
state.setCurrentBlockEntity(9);
|
||||
state.setCurrentEntityColor(1, 0, 0, 0.5f);
|
||||
@@ -266,10 +259,6 @@ class IrisDisplayListStateTest {
|
||||
assertEquals(8, state.getCurrentRenderedItem());
|
||||
assertEquals(9, state.getCurrentRenderedBlockEntity());
|
||||
assertTrue(state.getCurrentEntityColor().equals(1, 0, 0, 0.5f));
|
||||
} finally {
|
||||
state.popCurrentEntityColor();
|
||||
state.popCurrentBlockEntity();
|
||||
state.popCurrentEntityAndItem();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -377,6 +377,17 @@ class PacerCoreSimTest {
|
||||
return sim;
|
||||
}
|
||||
|
||||
private static Sim tearingSim(String name, int cap) {
|
||||
final Sim sim = new Sim().named(name);
|
||||
sim.tearFree = false;
|
||||
sim.vsyncOn = false;
|
||||
sim.mode = Mode.IMMEDIATE;
|
||||
sim.gate = Gate.NONE;
|
||||
sim.cap = cap;
|
||||
sim.gpuTail = 200 * US;
|
||||
return sim;
|
||||
}
|
||||
|
||||
private static void assertNeverBunches(Result r, long slopNanos) {
|
||||
for (int f = WARMUP; f < FRAMES; f++) {
|
||||
if (r.driven[f] || r.driven[f - 1]) continue;
|
||||
@@ -509,14 +520,8 @@ class PacerCoreSimTest {
|
||||
|
||||
@Test
|
||||
void aTearingCapHoldsItsPeriodAndKeepsLatencyToOneFrameOfWork() {
|
||||
final Sim sim = new Sim().named("vsyncOff cap100");
|
||||
sim.tearFree = false;
|
||||
sim.vsyncOn = false;
|
||||
sim.mode = Mode.IMMEDIATE;
|
||||
sim.gate = Gate.NONE;
|
||||
sim.cap = 100;
|
||||
final Sim sim = tearingSim("vsyncOff cap100", 100);
|
||||
sim.work = 4 * MS;
|
||||
sim.gpuTail = 200 * US;
|
||||
final Result r = run(sim);
|
||||
|
||||
assertTrue(Math.abs(r.minInterval - 10 * MS) < 200 * US, r.toString());
|
||||
@@ -527,14 +532,8 @@ class PacerCoreSimTest {
|
||||
|
||||
@Test
|
||||
void aFrameSlowerThanTheCadenceIsNeverSleptOn() {
|
||||
final Sim sim = new Sim().named("cpuBound R=20ms cap60");
|
||||
sim.tearFree = false;
|
||||
sim.vsyncOn = false;
|
||||
sim.mode = Mode.IMMEDIATE;
|
||||
sim.gate = Gate.NONE;
|
||||
sim.cap = 60;
|
||||
final Sim sim = tearingSim("cpuBound R=20ms cap60", 60);
|
||||
sim.work = 20 * MS;
|
||||
sim.gpuTail = 200 * US;
|
||||
final Result r = run(sim);
|
||||
|
||||
assertEquals(0, r.startParks, r.toString());
|
||||
@@ -544,14 +543,8 @@ class PacerCoreSimTest {
|
||||
|
||||
@Test
|
||||
void aCoarseSleepTimerWidensTheMarginWithoutSaturatingIt() {
|
||||
final Sim sim = new Sim().named("coarseTimer cap60");
|
||||
sim.tearFree = false;
|
||||
sim.vsyncOn = false;
|
||||
sim.mode = Mode.IMMEDIATE;
|
||||
sim.gate = Gate.NONE;
|
||||
sim.cap = 60;
|
||||
final Sim sim = tearingSim("coarseTimer cap60", 60);
|
||||
sim.granularity = 4 * MS;
|
||||
sim.gpuTail = 200 * US;
|
||||
final Result r = run(sim);
|
||||
|
||||
assertTrue(r.margin > PacerCore.MARGIN_MIN_NANOS, r + " a coarse timer must buy extra lead");
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package com.gtnewhorizons.angelica.rendering;
|
||||
|
||||
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||
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 java.util.ArrayList;
|
||||
@@ -70,12 +70,22 @@ class PacerCoreTest {
|
||||
time.now = core.beginFrame(time.now);
|
||||
}
|
||||
|
||||
void feedRun(int count, long firstEndNanos, long durationNanos, long strideNanos) {
|
||||
long feedRun(int count, long firstEndNanos, long durationNanos, long strideNanos) {
|
||||
long end = firstEndNanos;
|
||||
for (int i = 0; i < count; i++) {
|
||||
feed(end, durationNanos, end);
|
||||
end += strideNanos;
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
long feedOffGrid(long endNanos, int count) {
|
||||
long end = endNanos;
|
||||
for (int i = 0; i < count; i++) {
|
||||
end += P60 / 2;
|
||||
feed(end, 5 * MS, end);
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
void idleFrames(int count, long atNanos) {
|
||||
@@ -94,6 +104,8 @@ class PacerCoreTest {
|
||||
Arguments.of("a cap above the refresh rate collapses onto the refresh period", 240, P60, true, P60),
|
||||
Arguments.of("a cap far from every multiple keeps its literal period", 45, P60, true, 1_000_000_000L / 45),
|
||||
Arguments.of("a cap far from every multiple keeps its literal period", 100, P144, true, 1_000_000_000L / 100),
|
||||
Arguments.of("a cap near a vblank multiple snaps to that multiple", 30, P60, true, 2 * P60),
|
||||
Arguments.of("a cap near a vblank multiple snaps to that multiple", 50, P144, true, 3 * P144),
|
||||
Arguments.of("a tearing mode ignores the refresh period", 100, P60, false, 10 * MS),
|
||||
Arguments.of("a tearing mode with no cap is unpaced", 0, P60, false, 0L),
|
||||
Arguments.of("a zero refresh period with no cap is unpaced", 0, 0L, true, 0L),
|
||||
@@ -106,13 +118,6 @@ class PacerCoreTest {
|
||||
assertEquals(expected, PacerCore.cadencePeriodNanos(capHz, refreshPeriodNanos, tearFree));
|
||||
}
|
||||
|
||||
@Test
|
||||
void aCapNearAVblankMultipleSnapsToThatMultiple() {
|
||||
assertEquals(2 * P60, PacerCore.cadencePeriodNanos(30, P60, true));
|
||||
assertEquals(3 * P144, PacerCore.cadencePeriodNanos(50, P144, true));
|
||||
assertEquals(48, RenderBackend.hzFromPeriod(3 * P144));
|
||||
}
|
||||
|
||||
@Test
|
||||
void threeConsistentPairsOnTheRefreshGridLockThePhase() {
|
||||
final Rig rig = new Rig(P60, true, 0);
|
||||
@@ -136,11 +141,9 @@ class PacerCoreTest {
|
||||
@Test
|
||||
void oneBadPairRestartsTheRunOfConsistentPairs() {
|
||||
final Rig rig = new Rig(P60, true, 0);
|
||||
rig.feedRun(PacerCore.LOCK_PAIRS, 20 * MS, 5 * MS, P60);
|
||||
long end = 20 * MS + PacerCore.LOCK_PAIRS * P60;
|
||||
long end = rig.feedRun(PacerCore.LOCK_PAIRS, 20 * MS, 5 * MS, P60);
|
||||
|
||||
end += P60 / 2;
|
||||
rig.feed(end, 5 * MS, end);
|
||||
end = rig.feedOffGrid(end, 1);
|
||||
assertFalse(rig.core.locked());
|
||||
|
||||
for (int i = 0; i < PacerCore.LOCK_PAIRS; i++) {
|
||||
@@ -153,60 +156,31 @@ class PacerCoreTest {
|
||||
@Test
|
||||
void aRunOfInconsistentPairsGivesUpOnTheGateEntirely() {
|
||||
final Rig rig = new Rig(P60, true, 0);
|
||||
rig.feedRun(PacerCore.LOCK_PAIRS + 1, 20 * MS, 5 * MS, P60);
|
||||
final long end = rig.feedRun(PacerCore.LOCK_PAIRS + 1, 20 * MS, 5 * MS, P60);
|
||||
assertTrue(rig.core.locked());
|
||||
|
||||
long end = 20 * MS + (PacerCore.LOCK_PAIRS + 1) * P60;
|
||||
for (int i = 0; i < PacerCore.LOCK_WINDOW + 1; i++) {
|
||||
end += P60 / 2;
|
||||
rig.feed(end, 5 * MS, end);
|
||||
}
|
||||
rig.feedOffGrid(end, PacerCore.LOCK_WINDOW + 1);
|
||||
assertFalse(rig.core.locked(), "a gate that stops landing on the grid must be released");
|
||||
assertFalse(rig.core.probing(), "a gate proven off the grid must not be probed again");
|
||||
}
|
||||
|
||||
@Test
|
||||
void anAbandonedGateStaysRetiredThroughAnotherOnGridRun() {
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@CsvSource({
|
||||
"an abandoned gate stays retired through another on-grid run, false, false",
|
||||
"invalidate clears an abandoned gate, true, true"})
|
||||
void abandonedGate(String label, boolean invalidate, boolean lockedAfter) {
|
||||
final Rig rig = new Rig(P60, true, 120);
|
||||
rig.feedRun(PacerCore.LOCK_PAIRS + 1, 20 * MS, 5 * MS, P60);
|
||||
long end = rig.feedRun(PacerCore.LOCK_PAIRS + 1, 20 * MS, 5 * MS, P60);
|
||||
assertTrue(rig.core.locked());
|
||||
assertEquals(60, rig.core.effectiveCapHz());
|
||||
|
||||
long end = 20 * MS + (PacerCore.LOCK_PAIRS + 1) * P60;
|
||||
for (int i = 0; i < PacerCore.LOCK_WINDOW + 1; i++) {
|
||||
end += P60 / 2;
|
||||
rig.feed(end, 5 * MS, end);
|
||||
}
|
||||
end = rig.feedOffGrid(end, PacerCore.LOCK_WINDOW + 1);
|
||||
assertFalse(rig.core.locked());
|
||||
assertFalse(rig.core.probing());
|
||||
|
||||
for (int i = 0; i < PacerCore.LOCK_PAIRS + 1; i++) {
|
||||
end += P60;
|
||||
rig.feed(end, 5 * MS, end);
|
||||
}
|
||||
assertFalse(rig.core.locked(), "a retired inconsistent gate must not lock again");
|
||||
assertFalse(rig.core.probing());
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidateClearsAnAbandonedGate() {
|
||||
final Rig rig = new Rig(P60, true, 120);
|
||||
rig.feedRun(PacerCore.LOCK_PAIRS + 1, 20 * MS, 5 * MS, P60);
|
||||
|
||||
long end = 20 * MS + (PacerCore.LOCK_PAIRS + 1) * P60;
|
||||
for (int i = 0; i < PacerCore.LOCK_WINDOW + 1; i++) {
|
||||
end += P60 / 2;
|
||||
rig.feed(end, 5 * MS, end);
|
||||
}
|
||||
assertFalse(rig.core.locked());
|
||||
assertFalse(rig.core.probing());
|
||||
|
||||
rig.core.invalidate();
|
||||
for (int i = 0; i < PacerCore.LOCK_PAIRS + 1; i++) {
|
||||
end += P60;
|
||||
rig.feed(end, 5 * MS, end);
|
||||
}
|
||||
assertTrue(rig.core.locked());
|
||||
if (invalidate) rig.core.invalidate();
|
||||
rig.feedRun(PacerCore.LOCK_PAIRS + 1, end + P60, 5 * MS, P60);
|
||||
assertEquals(lockedAfter, rig.core.locked(), label);
|
||||
assertFalse(rig.core.probing());
|
||||
}
|
||||
|
||||
@@ -348,8 +322,9 @@ class PacerCoreTest {
|
||||
@Test
|
||||
void theFirstPacedFrameAfterAnUnpacedStretchIsAnchoredWithoutSleeping() {
|
||||
final Rig rig = new Rig(P60, false, 0);
|
||||
for (int i = 0; i < 5; i++) rig.frame(4 * MS);
|
||||
for (int i = 0; i < 50; i++) rig.frame(4 * MS);
|
||||
assertEquals(0, rig.time.parks);
|
||||
assertEquals(0L, rig.core.slackNanos());
|
||||
|
||||
rig.core.setCap(60);
|
||||
rig.frame(4 * MS);
|
||||
@@ -384,14 +359,6 @@ class PacerCoreTest {
|
||||
assertTrue(snapped.core.paced());
|
||||
}
|
||||
|
||||
@Test
|
||||
void anUnpacedFrameNeverSleeps() {
|
||||
final Rig rig = new Rig(P60, false, 0);
|
||||
for (int i = 0; i < 50; i++) rig.frame(4 * MS);
|
||||
assertEquals(0, rig.time.parks);
|
||||
assertEquals(0L, rig.core.slackNanos());
|
||||
}
|
||||
|
||||
@Test
|
||||
void theMarginIsClampedIntoItsBandByTheCadence() {
|
||||
assertEquals(PacerCore.MARGIN_MIN_NANOS, PacerCore.clampMargin(0L, 16_666_666L));
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -89,32 +90,24 @@ class ReducerStateMachineTest {
|
||||
assertEquals(State.FOCUSED, machine.state());
|
||||
}
|
||||
|
||||
@Test
|
||||
void toggleForcedCountsAsInput() {
|
||||
time += 10_000_000_000L;
|
||||
machine.evaluate(true, true, true, 1_000_000_000L);
|
||||
assertEquals(State.IDLE, machine.state());
|
||||
|
||||
machine.toggleForced();
|
||||
machine.evaluate(true, true, true, 1_000_000_000L);
|
||||
assertEquals(State.FORCED, machine.state());
|
||||
|
||||
machine.toggleForced();
|
||||
machine.evaluate(true, true, true, 1_000_000_000L);
|
||||
assertEquals(State.FOCUSED, machine.state());
|
||||
static Stream<Arguments> inputToggles() {
|
||||
return Stream.of(
|
||||
Arguments.of("forced toggle", (Consumer<ReducerStateMachine>) ReducerStateMachine::toggleForced, State.FORCED),
|
||||
Arguments.of("keybind-disabled toggle", (Consumer<ReducerStateMachine>) ReducerStateMachine::toggleKeybindDisabled, State.DISABLED));
|
||||
}
|
||||
|
||||
@Test
|
||||
void toggleKeybindDisabledCountsAsInput() {
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("inputToggles")
|
||||
void toggleCountsAsInput(String label, Consumer<ReducerStateMachine> toggle, State expected) {
|
||||
time += 10_000_000_000L;
|
||||
machine.evaluate(true, true, true, 1_000_000_000L);
|
||||
assertEquals(State.IDLE, machine.state());
|
||||
|
||||
machine.toggleKeybindDisabled();
|
||||
toggle.accept(machine);
|
||||
machine.evaluate(true, true, true, 1_000_000_000L);
|
||||
assertEquals(State.DISABLED, machine.state());
|
||||
assertEquals(expected, machine.state());
|
||||
|
||||
machine.toggleKeybindDisabled();
|
||||
toggle.accept(machine);
|
||||
machine.evaluate(true, true, true, 1_000_000_000L);
|
||||
assertEquals(State.FOCUSED, machine.state());
|
||||
}
|
||||
@@ -180,13 +173,15 @@ class ReducerStateMachineTest {
|
||||
assertEquals(0.01f, ReducerStateMachine.masterVolume(1.0f, 0), DELTA);
|
||||
}
|
||||
|
||||
@Test
|
||||
void stateVolumeSelectsProfile() {
|
||||
assertEquals(10, ReducerStateMachine.stateVolume(State.FORCED, 10, 20, 30));
|
||||
assertEquals(10, ReducerStateMachine.stateVolume(State.UNFOCUSED, 10, 20, 30));
|
||||
assertEquals(20, ReducerStateMachine.stateVolume(State.MINIMIZED, 10, 20, 30));
|
||||
assertEquals(30, ReducerStateMachine.stateVolume(State.IDLE, 10, 20, 30));
|
||||
assertEquals(100, ReducerStateMachine.stateVolume(State.FOCUSED, 10, 20, 30));
|
||||
assertEquals(100, ReducerStateMachine.stateVolume(State.DISABLED, 10, 20, 30));
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@CsvSource({
|
||||
"FORCED, 10",
|
||||
"UNFOCUSED, 10",
|
||||
"MINIMIZED, 20",
|
||||
"IDLE, 30",
|
||||
"FOCUSED, 100",
|
||||
"DISABLED, 100"})
|
||||
void stateVolumeSelectsProfile(State state, int expected) {
|
||||
assertEquals(expected, ReducerStateMachine.stateVolume(state, 10, 20, 30));
|
||||
}
|
||||
}
|
||||
+85
-97
@@ -7,11 +7,16 @@ import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkBuildOutput;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkTaskOutput;
|
||||
import org.embeddedt.embeddium.impl.render.chunk.compile.executor.ChunkJobResult;
|
||||
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.mockito.Mockito;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
@@ -41,144 +46,127 @@ class DeferredMeshSchedulerTest {
|
||||
return new ChunkJobResult.Failure<>(new RuntimeException("x"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void importantItemRunsOnRunImportant() {
|
||||
ArrayDeque<Runnable> queue = new ArrayDeque<>();
|
||||
Queue<ChunkJobResult<? extends ChunkTaskOutput>> results = new ConcurrentLinkedDeque<>();
|
||||
DeferredMeshScheduler scheduler = new DeferredMeshScheduler(null, () -> null, () -> Mockito.mock(WorldSlice.class), queue::add, results);
|
||||
|
||||
ChunkBuildBuffers buffers = scheduler.acquireBuffers();
|
||||
DeferredSectionMesh m = mesh(buffers, scheduler.acquireSlice(), true, false, success());
|
||||
scheduler.submit(m);
|
||||
|
||||
assertEquals(0, queue.size());
|
||||
assertEquals(1, scheduler.pendingCount());
|
||||
|
||||
scheduler.runImportant();
|
||||
|
||||
assertEquals(1, results.size());
|
||||
assertTrue(results.peek() instanceof ChunkJobResult.Success);
|
||||
assertEquals(0, scheduler.pendingCount());
|
||||
assertSame(buffers, scheduler.acquireBuffers());
|
||||
private static final class Harness {
|
||||
final ArrayDeque<Runnable> queue = new ArrayDeque<>();
|
||||
final Queue<ChunkJobResult<? extends ChunkTaskOutput>> results = new ConcurrentLinkedDeque<>();
|
||||
final DeferredMeshScheduler scheduler = new DeferredMeshScheduler(null, () -> null, () -> Mockito.mock(WorldSlice.class), queue::add, results);
|
||||
}
|
||||
|
||||
@Test
|
||||
void normalItemRunsThroughMainThreadQueue() {
|
||||
ArrayDeque<Runnable> queue = new ArrayDeque<>();
|
||||
Queue<ChunkJobResult<? extends ChunkTaskOutput>> results = new ConcurrentLinkedDeque<>();
|
||||
DeferredMeshScheduler scheduler = new DeferredMeshScheduler(null, () -> null, () -> Mockito.mock(WorldSlice.class), queue::add, results);
|
||||
void importantItemRunsOnRunImportant() {
|
||||
Harness h = new Harness();
|
||||
|
||||
ChunkBuildBuffers buffers = scheduler.acquireBuffers();
|
||||
DeferredSectionMesh m = mesh(buffers, scheduler.acquireSlice(), false, false, success());
|
||||
scheduler.submit(m);
|
||||
ChunkBuildBuffers buffers = h.scheduler.acquireBuffers();
|
||||
DeferredSectionMesh m = mesh(buffers, h.scheduler.acquireSlice(), true, false, success());
|
||||
h.scheduler.submit(m);
|
||||
|
||||
assertEquals(1, queue.size());
|
||||
assertEquals(0, h.queue.size());
|
||||
assertEquals(1, h.scheduler.pendingCount());
|
||||
|
||||
scheduler.runImportant();
|
||||
assertEquals(0, results.size());
|
||||
h.scheduler.runImportant();
|
||||
|
||||
queue.poll().run();
|
||||
assertEquals(1, h.results.size());
|
||||
assertTrue(h.results.peek() instanceof ChunkJobResult.Success);
|
||||
assertEquals(0, h.scheduler.pendingCount());
|
||||
assertSame(buffers, h.scheduler.acquireBuffers());
|
||||
}
|
||||
|
||||
assertEquals(1, results.size());
|
||||
assertTrue(results.peek() instanceof ChunkJobResult.Success);
|
||||
assertEquals(0, scheduler.pendingCount());
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("normalResults")
|
||||
void normalItemRunsThroughMainThreadQueue(String label, Supplier<ChunkJobResult<ChunkBuildOutput>> result, Class<?> expected) {
|
||||
Harness h = new Harness();
|
||||
|
||||
ChunkBuildBuffers buffers = h.scheduler.acquireBuffers();
|
||||
DeferredSectionMesh m = mesh(buffers, h.scheduler.acquireSlice(), false, false, result.get());
|
||||
h.scheduler.submit(m);
|
||||
|
||||
assertEquals(1, h.queue.size());
|
||||
|
||||
h.scheduler.runImportant();
|
||||
assertEquals(0, h.results.size());
|
||||
|
||||
h.queue.poll().run();
|
||||
|
||||
assertEquals(1, h.results.size());
|
||||
assertTrue(expected.isInstance(h.results.peek()));
|
||||
assertEquals(0, h.scheduler.pendingCount());
|
||||
}
|
||||
|
||||
private static Stream<Arguments> normalResults() {
|
||||
return Stream.of(
|
||||
Arguments.of("success", (Supplier<ChunkJobResult<ChunkBuildOutput>>) DeferredMeshSchedulerTest::success, ChunkJobResult.Success.class),
|
||||
Arguments.of("failure is recorded and counters still decrement", (Supplier<ChunkJobResult<ChunkBuildOutput>>) DeferredMeshSchedulerTest::failure, ChunkJobResult.Failure.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void importantItemAfterDestroyReleasesBuffers() {
|
||||
ArrayDeque<Runnable> queue = new ArrayDeque<>();
|
||||
Queue<ChunkJobResult<? extends ChunkTaskOutput>> results = new ConcurrentLinkedDeque<>();
|
||||
DeferredMeshScheduler scheduler = new DeferredMeshScheduler(null, () -> null, () -> Mockito.mock(WorldSlice.class), queue::add, results);
|
||||
Harness h = new Harness();
|
||||
|
||||
ChunkBuildBuffers buffers = scheduler.acquireBuffers();
|
||||
WorldSlice slice = scheduler.acquireSlice();
|
||||
ChunkBuildBuffers buffers = h.scheduler.acquireBuffers();
|
||||
WorldSlice slice = h.scheduler.acquireSlice();
|
||||
DeferredSectionMesh m = mesh(buffers, slice, true, false, success());
|
||||
scheduler.submit(m);
|
||||
scheduler.markDestroyed();
|
||||
scheduler.runImportant();
|
||||
h.scheduler.submit(m);
|
||||
h.scheduler.markDestroyed();
|
||||
h.scheduler.runImportant();
|
||||
|
||||
assertEquals(0, results.size());
|
||||
assertEquals(0, scheduler.pendingCount());
|
||||
assertSame(buffers, scheduler.acquireBuffers());
|
||||
assertSame(slice, scheduler.acquireSlice());
|
||||
assertEquals(0, h.results.size());
|
||||
assertEquals(0, h.scheduler.pendingCount());
|
||||
assertSame(buffers, h.scheduler.acquireBuffers());
|
||||
assertSame(slice, h.scheduler.acquireSlice());
|
||||
Mockito.verify(slice).releaseContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
void markDestroyedDiscardsQueuedItemAndReturnsBuffersToPool() {
|
||||
ArrayDeque<Runnable> queue = new ArrayDeque<>();
|
||||
Queue<ChunkJobResult<? extends ChunkTaskOutput>> results = new ConcurrentLinkedDeque<>();
|
||||
DeferredMeshScheduler scheduler = new DeferredMeshScheduler(null, () -> null, () -> Mockito.mock(WorldSlice.class), queue::add, results);
|
||||
Harness h = new Harness();
|
||||
|
||||
ChunkBuildBuffers buffers = scheduler.acquireBuffers();
|
||||
DeferredSectionMesh m = mesh(buffers, scheduler.acquireSlice(), false, false, success());
|
||||
scheduler.submit(m);
|
||||
scheduler.markDestroyed();
|
||||
queue.poll().run();
|
||||
ChunkBuildBuffers buffers = h.scheduler.acquireBuffers();
|
||||
DeferredSectionMesh m = mesh(buffers, h.scheduler.acquireSlice(), false, false, success());
|
||||
h.scheduler.submit(m);
|
||||
h.scheduler.markDestroyed();
|
||||
h.queue.poll().run();
|
||||
|
||||
assertEquals(0, results.size());
|
||||
assertEquals(0, scheduler.pendingCount());
|
||||
assertSame(buffers, scheduler.acquireBuffers());
|
||||
assertEquals(0, h.results.size());
|
||||
assertEquals(0, h.scheduler.pendingCount());
|
||||
assertSame(buffers, h.scheduler.acquireBuffers());
|
||||
}
|
||||
|
||||
@Test
|
||||
void obsoleteItemIsDiscarded() {
|
||||
ArrayDeque<Runnable> queue = new ArrayDeque<>();
|
||||
Queue<ChunkJobResult<? extends ChunkTaskOutput>> results = new ConcurrentLinkedDeque<>();
|
||||
DeferredMeshScheduler scheduler = new DeferredMeshScheduler(null, () -> null, () -> Mockito.mock(WorldSlice.class), queue::add, results);
|
||||
Harness h = new Harness();
|
||||
|
||||
ChunkBuildBuffers buffers = scheduler.acquireBuffers();
|
||||
DeferredSectionMesh m = mesh(buffers, scheduler.acquireSlice(), false, true, success());
|
||||
scheduler.submit(m);
|
||||
queue.poll().run();
|
||||
ChunkBuildBuffers buffers = h.scheduler.acquireBuffers();
|
||||
DeferredSectionMesh m = mesh(buffers, h.scheduler.acquireSlice(), false, true, success());
|
||||
h.scheduler.submit(m);
|
||||
h.queue.poll().run();
|
||||
|
||||
assertEquals(0, results.size());
|
||||
assertEquals(0, scheduler.pendingCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void failureResultIsRecordedAndCountersStillDecrement() {
|
||||
ArrayDeque<Runnable> queue = new ArrayDeque<>();
|
||||
Queue<ChunkJobResult<? extends ChunkTaskOutput>> results = new ConcurrentLinkedDeque<>();
|
||||
DeferredMeshScheduler scheduler = new DeferredMeshScheduler(null, () -> null, () -> Mockito.mock(WorldSlice.class), queue::add, results);
|
||||
|
||||
ChunkBuildBuffers buffers = scheduler.acquireBuffers();
|
||||
DeferredSectionMesh m = mesh(buffers, scheduler.acquireSlice(), false, false, failure());
|
||||
scheduler.submit(m);
|
||||
queue.poll().run();
|
||||
|
||||
assertEquals(1, results.size());
|
||||
assertTrue(results.peek() instanceof ChunkJobResult.Failure);
|
||||
assertEquals(0, scheduler.pendingCount());
|
||||
assertEquals(0, h.results.size());
|
||||
assertEquals(0, h.scheduler.pendingCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void acquireBuffersAfterCompletedRunReturnsTheSameInstance() {
|
||||
ArrayDeque<Runnable> queue = new ArrayDeque<>();
|
||||
Queue<ChunkJobResult<? extends ChunkTaskOutput>> results = new ConcurrentLinkedDeque<>();
|
||||
DeferredMeshScheduler scheduler = new DeferredMeshScheduler(null, () -> null, () -> Mockito.mock(WorldSlice.class), queue::add, results);
|
||||
Harness h = new Harness();
|
||||
|
||||
ChunkBuildBuffers buffers = scheduler.acquireBuffers();
|
||||
DeferredSectionMesh m = mesh(buffers, scheduler.acquireSlice(), false, false, success());
|
||||
scheduler.submit(m);
|
||||
queue.poll().run();
|
||||
ChunkBuildBuffers buffers = h.scheduler.acquireBuffers();
|
||||
DeferredSectionMesh m = mesh(buffers, h.scheduler.acquireSlice(), false, false, success());
|
||||
h.scheduler.submit(m);
|
||||
h.queue.poll().run();
|
||||
|
||||
assertSame(buffers, scheduler.acquireBuffers());
|
||||
assertSame(buffers, h.scheduler.acquireBuffers());
|
||||
}
|
||||
|
||||
@Test
|
||||
void runReturnsSliceToPool() {
|
||||
ArrayDeque<Runnable> queue = new ArrayDeque<>();
|
||||
Queue<ChunkJobResult<? extends ChunkTaskOutput>> results = new ConcurrentLinkedDeque<>();
|
||||
DeferredMeshScheduler scheduler = new DeferredMeshScheduler(null, () -> null, () -> Mockito.mock(WorldSlice.class), queue::add, results);
|
||||
Harness h = new Harness();
|
||||
|
||||
ChunkBuildBuffers buffers = scheduler.acquireBuffers();
|
||||
WorldSlice slice = scheduler.acquireSlice();
|
||||
ChunkBuildBuffers buffers = h.scheduler.acquireBuffers();
|
||||
WorldSlice slice = h.scheduler.acquireSlice();
|
||||
DeferredSectionMesh m = mesh(buffers, slice, false, false, success());
|
||||
scheduler.submit(m);
|
||||
queue.poll().run();
|
||||
h.scheduler.submit(m);
|
||||
h.queue.poll().run();
|
||||
|
||||
assertSame(slice, scheduler.acquireSlice());
|
||||
assertSame(slice, h.scheduler.acquireSlice());
|
||||
Mockito.verify(slice).releaseContext();
|
||||
}
|
||||
}
|
||||
@@ -1,67 +1,39 @@
|
||||
package com.gtnewhorizons.angelica.rendering.celeritas;
|
||||
|
||||
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;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ObjectPoolTest {
|
||||
|
||||
@Test
|
||||
void acquireOnEmptyPoolCallsFactoryOnce() {
|
||||
@ParameterizedTest(name = "n={0}")
|
||||
@ValueSource(ints = {1, 2, 3})
|
||||
void releaseThenAcquireReturnsSameInstancesWithoutFactory(int n) {
|
||||
AtomicInteger calls = new AtomicInteger();
|
||||
ObjectPool<Object> pool = new ObjectPool<>(() -> {
|
||||
calls.incrementAndGet();
|
||||
return new Object();
|
||||
});
|
||||
|
||||
Object o = pool.acquire();
|
||||
|
||||
assertTrue(o != null);
|
||||
assertEquals(1, calls.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void releaseThenAcquireReturnsSameInstanceWithoutFactory() {
|
||||
AtomicInteger calls = new AtomicInteger();
|
||||
ObjectPool<Object> pool = new ObjectPool<>(() -> {
|
||||
calls.incrementAndGet();
|
||||
return new Object();
|
||||
});
|
||||
|
||||
Object o = pool.acquire();
|
||||
pool.release(o);
|
||||
Object reacquired = pool.acquire();
|
||||
|
||||
assertSame(o, reacquired);
|
||||
assertEquals(1, calls.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void twoReleasesThenTwoAcquiresReturnBothInstances() {
|
||||
AtomicInteger calls = new AtomicInteger();
|
||||
ObjectPool<Object> pool = new ObjectPool<>(() -> {
|
||||
calls.incrementAndGet();
|
||||
return new Object();
|
||||
});
|
||||
|
||||
Object a = pool.acquire();
|
||||
Object b = pool.acquire();
|
||||
pool.release(a);
|
||||
pool.release(b);
|
||||
Object[] acquired = new Object[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
acquired[i] = pool.acquire();
|
||||
assertNotNull(acquired[i]);
|
||||
}
|
||||
for (Object o : acquired) pool.release(o);
|
||||
|
||||
Set<Object> reacquired = new HashSet<>();
|
||||
reacquired.add(pool.acquire());
|
||||
reacquired.add(pool.acquire());
|
||||
for (int i = 0; i < n; i++) reacquired.add(pool.acquire());
|
||||
|
||||
assertEquals(2, reacquired.size());
|
||||
assertTrue(reacquired.contains(a));
|
||||
assertTrue(reacquired.contains(b));
|
||||
assertEquals(2, calls.get());
|
||||
assertEquals(n, reacquired.size());
|
||||
for (Object o : acquired) assertTrue(reacquired.contains(o));
|
||||
assertEquals(n, calls.get());
|
||||
}
|
||||
}
|
||||
+5
-6
@@ -34,6 +34,8 @@ import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
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.GL30;
|
||||
@@ -663,12 +665,9 @@ class IndirectCullDrawParityTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void cutoutAndTranslucentShareOneChainedDispatchWithoutSolid() {
|
||||
unsortedAndTranslucentShareOneChainedDispatch(true);
|
||||
}
|
||||
|
||||
private void unsortedAndTranslucentShareOneChainedDispatch(boolean cutout) {
|
||||
@ParameterizedTest(name = "cutout={0}")
|
||||
@ValueSource(booleans = {false, true})
|
||||
void unsortedAndTranslucentShareOneChainedDispatch(boolean cutout) {
|
||||
assumeTrue(RenderSystem.supportsCompute() && RenderSystem.supportsMultiDrawIndirect(),
|
||||
"compute-driven indirect culling unsupported");
|
||||
|
||||
|
||||
+27
-39
@@ -18,6 +18,10 @@ import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class SectionMetaBufferTest {
|
||||
private static int put(SectionMetaBuffer meta, int pass, int x, int y, int z, ByteBuffer src) {
|
||||
return meta.update(pass, x, y, z, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void slotLayout_originAndFencePosts() {
|
||||
final SectionMetaBuffer meta = new SectionMetaBuffer();
|
||||
@@ -57,23 +61,20 @@ class SectionMetaBufferTest {
|
||||
@Test
|
||||
void slotAllocation_isStablePerSection() {
|
||||
final SectionMetaBuffer meta = new SectionMetaBuffer();
|
||||
final int[] vo = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final int[] ec = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final int[] io = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final ByteBuffer src = SyntheticSectionData.row(0, 0, vo, ec, io);
|
||||
final ByteBuffer src = SyntheticSectionData.sliceMaskOnly(0);
|
||||
try {
|
||||
meta.update(0, 16, 32, 64, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED);
|
||||
put(meta, 0, 16, 32, 64, src);
|
||||
final int firstSlot = meta.lookupSlot(0, 16, 32, 64);
|
||||
meta.update(0, 16, 32, 64, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED);
|
||||
put(meta, 0, 16, 32, 64, src);
|
||||
assertEquals(firstSlot, meta.lookupSlot(0, 16, 32, 64), "slot must be stable across updates");
|
||||
|
||||
meta.update(0, 0, 0, 0, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED);
|
||||
put(meta, 0, 0, 0, 0, src);
|
||||
final int otherSlot = meta.lookupSlot(0, 0, 0, 0);
|
||||
assertEquals(firstSlot + 1, otherSlot, "fresh section -> next high-water slot");
|
||||
|
||||
meta.remove(0, 16, 32, 64, 0);
|
||||
assertEquals(-1, meta.lookupSlot(0, 16, 32, 64), "removed slot should not resolve");
|
||||
meta.update(0, 16, 32, 64, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED);
|
||||
put(meta, 0, 16, 32, 64, src);
|
||||
assertEquals(firstSlot, meta.lookupSlot(0, 16, 32, 64), "free-list reuse should reclaim the slot");
|
||||
} finally {
|
||||
MemoryUtilities.memFree(src);
|
||||
@@ -84,10 +85,7 @@ class SectionMetaBufferTest {
|
||||
@Test
|
||||
void localSectionIndex_unpacking() {
|
||||
final SectionMetaBuffer meta = new SectionMetaBuffer();
|
||||
final int[] vo = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final int[] ec = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final int[] io = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final ByteBuffer src = SyntheticSectionData.row(0, 0, vo, ec, io);
|
||||
final ByteBuffer src = SyntheticSectionData.sliceMaskOnly(0);
|
||||
try {
|
||||
final int x = 3, y = 2, z = 5;
|
||||
final int localIdx = (x << 5) | (z << 2) | y;
|
||||
@@ -136,16 +134,13 @@ class SectionMetaBufferTest {
|
||||
@Test
|
||||
void update_returnsAllocatedSlot() {
|
||||
final SectionMetaBuffer meta = new SectionMetaBuffer();
|
||||
final int[] vo = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final int[] ec = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final int[] io = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final ByteBuffer src = SyntheticSectionData.row(0, 0, vo, ec, io);
|
||||
final ByteBuffer src = SyntheticSectionData.sliceMaskOnly(0);
|
||||
try {
|
||||
final int slotA = meta.update(0, 16, 32, 64, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED);
|
||||
final int slotB = meta.update(0, 0, 0, 0, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED);
|
||||
final int slotA = put(meta, 0, 16, 32, 64, src);
|
||||
final int slotB = put(meta, 0, 0, 0, 0, src);
|
||||
assertEquals(0, slotA, "first allocation -> slot 0");
|
||||
assertEquals(1, slotB, "second distinct section -> slot 1");
|
||||
assertEquals(slotA, meta.update(0, 16, 32, 64, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED), "re-update returns the stable slot");
|
||||
assertEquals(slotA, put(meta, 0, 16, 32, 64, src), "re-update returns the stable slot");
|
||||
assertEquals(-1, meta.update(0, 0, 0, 0, 0, 0L, 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED), "null dataPtr -> -1");
|
||||
} finally {
|
||||
MemoryUtilities.memFree(src);
|
||||
@@ -156,11 +151,10 @@ class SectionMetaBufferTest {
|
||||
@Test
|
||||
void syncIfDirty_positionsAndClearsDirty() {
|
||||
final SectionMetaBuffer meta = new SectionMetaBuffer();
|
||||
final int[] zeros = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final ByteBuffer src = SyntheticSectionData.row(0, 0, zeros, zeros, zeros);
|
||||
final ByteBuffer src = SyntheticSectionData.sliceMaskOnly(0);
|
||||
try {
|
||||
meta.update(0, 16, 32, 64, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED);
|
||||
meta.update(0, 0, 0, 0, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED);
|
||||
put(meta, 0, 16, 32, 64, src);
|
||||
put(meta, 0, 0, 0, 0, src);
|
||||
assertTrue(meta.isDirty(), "dirty after update");
|
||||
final AtomicReference<ByteBuffer> seen = new AtomicReference<>();
|
||||
final boolean ran = meta.syncIfDirty(b -> { seen.set(b); return true; });
|
||||
@@ -180,10 +174,9 @@ class SectionMetaBufferTest {
|
||||
@Test
|
||||
void syncIfDirty_refusedSinkKeepsDataPending() {
|
||||
final SectionMetaBuffer meta = new SectionMetaBuffer();
|
||||
final int[] zeros = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final ByteBuffer src = SyntheticSectionData.row(0, 0, zeros, zeros, zeros);
|
||||
final ByteBuffer src = SyntheticSectionData.sliceMaskOnly(0);
|
||||
try {
|
||||
meta.update(0, 16, 32, 64, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED);
|
||||
put(meta, 0, 16, 32, 64, src);
|
||||
final AtomicInteger offers = new AtomicInteger();
|
||||
|
||||
assertFalse(meta.syncIfDirty(b -> { offers.incrementAndGet(); return false; }), "a refused sink reports no sync");
|
||||
@@ -214,16 +207,15 @@ class SectionMetaBufferTest {
|
||||
@Test
|
||||
void reset_isIdempotent() {
|
||||
final SectionMetaBuffer meta = new SectionMetaBuffer();
|
||||
final int[] zeros = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final ByteBuffer src = SyntheticSectionData.row(0, 0, zeros, zeros, zeros);
|
||||
final ByteBuffer src = SyntheticSectionData.sliceMaskOnly(0);
|
||||
try {
|
||||
meta.update(0, 16, 32, 64, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED);
|
||||
put(meta, 0, 16, 32, 64, src);
|
||||
meta.reset();
|
||||
assertNull(meta.getMirrorForReadOnly(), "mirror gone after reset");
|
||||
assertEquals(-1, meta.lookupSlot(0, 16, 32, 64), "slot map cleared");
|
||||
meta.reset();
|
||||
meta.reset();
|
||||
meta.update(0, 16, 32, 64, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED);
|
||||
put(meta, 0, 16, 32, 64, src);
|
||||
assertEquals(0, meta.lookupSlot(0, 16, 32, 64), "reset is reusable, allocates fresh slot");
|
||||
} finally {
|
||||
MemoryUtilities.memFree(src);
|
||||
@@ -234,8 +226,7 @@ class SectionMetaBufferTest {
|
||||
@Test
|
||||
void concurrent_updates_and_syncs_doNotCrash() throws InterruptedException {
|
||||
final SectionMetaBuffer meta = new SectionMetaBuffer();
|
||||
final int[] zeros = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final ByteBuffer src = SyntheticSectionData.row(0, 0, zeros, zeros, zeros);
|
||||
final ByteBuffer src = SyntheticSectionData.sliceMaskOnly(0);
|
||||
final AtomicBoolean stop = new AtomicBoolean(false);
|
||||
final long srcAddr = MemoryUtilities.memAddress(src);
|
||||
|
||||
@@ -276,13 +267,10 @@ class SectionMetaBufferTest {
|
||||
@Test
|
||||
void perPassRemove_leavesSiblingPasses() {
|
||||
final SectionMetaBuffer meta = new SectionMetaBuffer();
|
||||
final int[] vo = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final int[] ec = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final int[] io = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
final ByteBuffer src = SyntheticSectionData.row(0, 0, vo, ec, io);
|
||||
final ByteBuffer src = SyntheticSectionData.sliceMaskOnly(0);
|
||||
try {
|
||||
meta.update(0, 16, 32, 64, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED);
|
||||
meta.update(1, 16, 32, 64, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED);
|
||||
put(meta, 0, 16, 32, 64, src);
|
||||
put(meta, 1, 16, 32, 64, src);
|
||||
final int slotA = meta.lookupSlot(0, 16, 32, 64);
|
||||
final int slotB = meta.lookupSlot(1, 16, 32, 64);
|
||||
|
||||
@@ -290,7 +278,7 @@ class SectionMetaBufferTest {
|
||||
assertEquals(-1, meta.lookupSlot(0, 16, 32, 64), "pass 0 slot must be gone");
|
||||
assertEquals(slotB, meta.lookupSlot(1, 16, 32, 64), "pass 1 slot must survive pass 0 removal");
|
||||
|
||||
meta.update(0, 0, 0, 0, 0, MemoryUtilities.memAddress(src), 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED);
|
||||
put(meta, 0, 0, 0, 0, src);
|
||||
assertEquals(slotA, meta.lookupSlot(0, 0, 0, 0), "free-list reuse claims pass 0's vacated slot");
|
||||
} finally {
|
||||
MemoryUtilities.memFree(src);
|
||||
|
||||
-13
@@ -223,19 +223,6 @@ class TerrainPassPreparationTest {
|
||||
assertEquals(2, state("totalAppendedEntries"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void absentSolidSelectionPreservesPreparedCutoutAndSortedPasses() {
|
||||
final ChunkRenderListIterable lists = lists(6);
|
||||
prepare(lists);
|
||||
select(lists, 0);
|
||||
gpu.endPass();
|
||||
select(lists, 1);
|
||||
assertRange("primaryPass", 0);
|
||||
gpu.endPass();
|
||||
select(lists, 2);
|
||||
assertRange("sortedPass", 1);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "passMask={0}")
|
||||
@ValueSource(ints = {4, 5}) // translucent alone, and chained after solid
|
||||
void unsortedTranslucentPassClearsIndexPointerMask(int passMask) {
|
||||
|
||||
+10
-14
@@ -605,14 +605,18 @@ class ParticleInstancingGLTest {
|
||||
capture.addVertexWithUV(1.0, 0.0, 0.0, 1.0, 1.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void aSecondQuadAfterASelfDrawSpillsIntoItsOwnBatchWhenTheRunIsNotDrawing() {
|
||||
final RecordingTessellator run = new RecordingTessellator();
|
||||
private static ParticleCaptureTessellator capturedQuad(RecordingTessellator run) {
|
||||
final ParticleCaptureTessellator capture = new ParticleCaptureTessellator();
|
||||
|
||||
capture.begin(100, run, GLStateManager.getModelViewMatrix());
|
||||
capture.setColorRGBA(5, 6, 7, 8);
|
||||
feedQuad(capture);
|
||||
return capture;
|
||||
}
|
||||
|
||||
@Test
|
||||
void aSecondQuadAfterASelfDrawSpillsIntoItsOwnBatchWhenTheRunIsNotDrawing() {
|
||||
final RecordingTessellator run = new RecordingTessellator();
|
||||
final ParticleCaptureTessellator capture = capturedQuad(run);
|
||||
capture.draw();
|
||||
run.log.clear();
|
||||
|
||||
@@ -630,11 +634,7 @@ class ParticleInstancingGLTest {
|
||||
@Test
|
||||
void aClosedSpillReplaysInTheSameOrder() {
|
||||
final RecordingTessellator run = new RecordingTessellator();
|
||||
final ParticleCaptureTessellator capture = new ParticleCaptureTessellator();
|
||||
|
||||
capture.begin(100, run, GLStateManager.getModelViewMatrix());
|
||||
capture.setColorRGBA(5, 6, 7, 8);
|
||||
feedQuad(capture);
|
||||
final ParticleCaptureTessellator capture = capturedQuad(run);
|
||||
capture.draw();
|
||||
run.isDrawing = true;
|
||||
run.log.clear();
|
||||
@@ -658,11 +658,7 @@ class ParticleInstancingGLTest {
|
||||
@Test
|
||||
void aFifthVertexSpillsAndForwardsRemainingCallsToRun() {
|
||||
final RecordingTessellator run = new RecordingTessellator();
|
||||
final ParticleCaptureTessellator capture = new ParticleCaptureTessellator();
|
||||
|
||||
capture.begin(100, run, GLStateManager.getModelViewMatrix());
|
||||
capture.setColorRGBA(5, 6, 7, 8);
|
||||
feedQuad(capture);
|
||||
final ParticleCaptureTessellator capture = capturedQuad(run);
|
||||
assertFalse(capture.spilled());
|
||||
|
||||
capture.addVertexWithUV(2.0, 2.0, 0.0, 0.5, 0.5);
|
||||
|
||||
+34
-37
@@ -25,6 +25,7 @@ import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrTestSupport.Countin
|
||||
import com.gtnewhorizons.angelica.shadercompat.ShaderGlint;
|
||||
import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource;
|
||||
import net.coderbot.iris.layer.PassOverride;
|
||||
import net.coderbot.iris.uniforms.CapturedStateScope;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.ItemRenderer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
@@ -393,44 +394,40 @@ class RetainedTesrGroupsGLTest {
|
||||
@Test
|
||||
void cpuFallbackRestoresEveryIdAndTintBeforeDrawingAndRestoresTheCaller() {
|
||||
final var state = net.coderbot.iris.uniforms.CapturedRenderingState.INSTANCE;
|
||||
state.pushCurrentEntityAndItem();
|
||||
state.pushCurrentBlockEntity();
|
||||
state.pushCurrentEntityColor();
|
||||
try {
|
||||
final var draws = new ArrayList<long[]>();
|
||||
onBufferSetup = () -> draws.add(new long[] {
|
||||
state.getCurrentRenderedEntity(), state.getCurrentRenderedBlockEntity(), state.getCurrentRenderedItem(),
|
||||
AngelicaBufferSource.packEntityColor(state.getCurrentEntityColor())
|
||||
});
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, instanced, new CountingPipeline(false, false));
|
||||
final TemplateBuffer template = capturedQuad(0.1);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
final long info = com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs.packEntityInfo(45010 + i, 45020 + i, 45030 + i);
|
||||
final int color = AngelicaBufferSource.packAbgr(i / 2f, 0, 0, 1);
|
||||
groups.queue(SCOPE, template, layer, RetainedTesrTestSupport.STREAM, new Matrix4f(), 0, -1, color, info, 45020 + i, null);
|
||||
try (CapturedStateScope ignored = new CapturedStateScope()) {
|
||||
try {
|
||||
final var draws = new ArrayList<long[]>();
|
||||
onBufferSetup = () -> draws.add(new long[] {
|
||||
state.getCurrentRenderedEntity(), state.getCurrentRenderedBlockEntity(), state.getCurrentRenderedItem(),
|
||||
AngelicaBufferSource.packEntityColor(state.getCurrentEntityColor())
|
||||
});
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, instanced, new CountingPipeline(false, false));
|
||||
final TemplateBuffer template = capturedQuad(0.1);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
final long info = com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs.packEntityInfo(45010 + i, 45020 + i, 45030 + i);
|
||||
final int color = AngelicaBufferSource.packAbgr(i / 2f, 0, 0, 1);
|
||||
groups.queue(SCOPE, template, layer, RetainedTesrTestSupport.STREAM, new Matrix4f(), 0, -1, color, info, 45020 + i, null);
|
||||
}
|
||||
state.setCurrentEntityAndItem(81, 82);
|
||||
state.setCurrentBlockEntity(83);
|
||||
state.setCurrentEntityColor(0, 1, 0, 0.5f);
|
||||
draws.clear();
|
||||
assertEquals(GL11.GL_QUADS, template.drawMode);
|
||||
groups.drawLayer(layer);
|
||||
assertEquals(3, draws.size(), "only differing uniform values split the CPU fallback");
|
||||
for (int i = 0; i < 3; i++) {
|
||||
assertArrayEquals(new long[] {45010 + i, 45020 + i, 45030 + i,
|
||||
AngelicaBufferSource.packAbgr(i / 2f, 0, 0, 1)}, draws.get(i));
|
||||
}
|
||||
source.discard();
|
||||
assertEquals(81, state.getCurrentRenderedEntity());
|
||||
assertEquals(82, state.getCurrentRenderedItem());
|
||||
assertEquals(83, state.getCurrentRenderedBlockEntity());
|
||||
assertTrue(state.getCurrentEntityColor().equals(0, 1, 0, 0.5f));
|
||||
} finally {
|
||||
onBufferSetup = null;
|
||||
source.discard();
|
||||
}
|
||||
state.setCurrentEntityAndItem(81, 82);
|
||||
state.setCurrentBlockEntity(83);
|
||||
state.setCurrentEntityColor(0, 1, 0, 0.5f);
|
||||
draws.clear();
|
||||
assertEquals(GL11.GL_QUADS, template.drawMode);
|
||||
groups.drawLayer(layer);
|
||||
assertEquals(3, draws.size(), "only differing uniform values split the CPU fallback");
|
||||
for (int i = 0; i < 3; i++) {
|
||||
assertArrayEquals(new long[] {45010 + i, 45020 + i, 45030 + i,
|
||||
AngelicaBufferSource.packAbgr(i / 2f, 0, 0, 1)}, draws.get(i));
|
||||
}
|
||||
source.discard();
|
||||
assertEquals(81, state.getCurrentRenderedEntity());
|
||||
assertEquals(82, state.getCurrentRenderedItem());
|
||||
assertEquals(83, state.getCurrentRenderedBlockEntity());
|
||||
assertTrue(state.getCurrentEntityColor().equals(0, 1, 0, 0.5f));
|
||||
} finally {
|
||||
onBufferSetup = null;
|
||||
source.discard();
|
||||
state.popCurrentEntityColor();
|
||||
state.popCurrentBlockEntity();
|
||||
state.popCurrentEntityAndItem();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-16
@@ -10,6 +10,7 @@ import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource;
|
||||
import net.coderbot.batchedentityrendering.impl.BufferSegment;
|
||||
import net.coderbot.batchedentityrendering.impl.BufferSourceProbe;
|
||||
import net.coderbot.iris.uniforms.CapturedRenderingState;
|
||||
import net.coderbot.iris.uniforms.CapturedStateScope;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.joml.Matrix4f;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -72,10 +73,7 @@ class RetainedTesrGroupsTest {
|
||||
@Test
|
||||
void retainedGroupsSeparateTheFullIdTupleAndTintAndReuseStableKeys() {
|
||||
final var state = CapturedRenderingState.INSTANCE;
|
||||
state.pushCurrentEntityAndItem();
|
||||
state.pushCurrentBlockEntity();
|
||||
state.pushCurrentEntityColor();
|
||||
try {
|
||||
try (CapturedStateScope ignored = new CapturedStateScope()) {
|
||||
for (int frame = 0; frame < 2; frame++) {
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0);
|
||||
for (int entity : new int[] {11, 12}) {
|
||||
@@ -92,20 +90,13 @@ class RetainedTesrGroupsTest {
|
||||
}
|
||||
assertEquals(16, groups.groupCount(), "only distinct ID/tint combinations allocate groups, including across frames");
|
||||
}
|
||||
} finally {
|
||||
state.popCurrentEntityColor();
|
||||
state.popCurrentBlockEntity();
|
||||
state.popCurrentEntityAndItem();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void instancedGroupsKeepFullIdsAndTintPerInstanceWithoutSplitting() {
|
||||
final var state = CapturedRenderingState.INSTANCE;
|
||||
state.pushCurrentEntityAndItem();
|
||||
state.pushCurrentBlockEntity();
|
||||
state.pushCurrentEntityColor();
|
||||
try {
|
||||
try (CapturedStateScope ignored = new CapturedStateScope()) {
|
||||
groups.beginPass(new Matrix4f(), 0, 0, 0, new InstancedTemplateRenderer(ring), new RetainedTesrTestSupport.CountingPipeline(true, true));
|
||||
for (int i = 0; i < 100; i++) {
|
||||
state.setCurrentEntityAndItem(i, i + 200);
|
||||
@@ -122,10 +113,6 @@ class RetainedTesrGroupsTest {
|
||||
assertEquals(InstancedAttribs.packEntityInfo(i, i + 100, i + 200), group.templateColumns.infos.getLong(i));
|
||||
assertEquals(AngelicaBufferSource.packAbgr(i / 100f, 0, 0, 1), group.templateColumns.overlays.getInt(i));
|
||||
}
|
||||
} finally {
|
||||
state.popCurrentEntityColor();
|
||||
state.popCurrentBlockEntity();
|
||||
state.popCurrentEntityAndItem();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.X_
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
final class RetainedTesrTestSupport {
|
||||
public final class RetainedTesrTestSupport {
|
||||
|
||||
private RetainedTesrTestSupport() {}
|
||||
|
||||
@@ -57,7 +57,7 @@ final class RetainedTesrTestSupport {
|
||||
return byLayer(groups).get(layer);
|
||||
}
|
||||
|
||||
static TemplateBuffer template() {
|
||||
public static TemplateBuffer template() {
|
||||
final int[] data = new int[4 * VERTEX_SIZE];
|
||||
for (int v = 0; v < 4; v++) {
|
||||
final int base = v * VERTEX_SIZE;
|
||||
|
||||
@@ -29,14 +29,17 @@ import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
@@ -76,6 +79,17 @@ class StateSetOverrideGLTest {
|
||||
if (otherProgram != 0) GLStateManager.glDeleteProgram(otherProgram);
|
||||
}
|
||||
|
||||
private static int[] driverBlendFunc() {
|
||||
return new int[] {GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), GL11.glGetInteger(GL14.GL_BLEND_DST_RGB),
|
||||
GL11.glGetInteger(GL14.GL_BLEND_SRC_ALPHA), GL11.glGetInteger(GL14.GL_BLEND_DST_ALPHA)};
|
||||
}
|
||||
|
||||
private static boolean[] driverColorMask() {
|
||||
final ByteBuffer mask = BufferUtils.createByteBuffer(16);
|
||||
GL11.glGetBoolean(GL11.GL_COLOR_WRITEMASK, mask);
|
||||
return new boolean[] {mask.get(0) != 0, mask.get(1) != 0, mask.get(2) != 0, mask.get(3) != 0};
|
||||
}
|
||||
|
||||
/** Vanilla blend is enabled SRC_ALPHA/ONE_MINUS_SRC_ALPHA/ONE/ZERO under an Iris override. */
|
||||
private static void overrideVanillaBlend() {
|
||||
VanillaLayerRig.installBlend();
|
||||
@@ -107,6 +121,10 @@ class StateSetOverrideGLTest {
|
||||
|
||||
final boolean liveEnabledBefore = GLStateManager.getBlendMode().isEnabled();
|
||||
final BlendState liveBefore = new BlendState().set(GLStateManager.getBlendState());
|
||||
final boolean driverEnabledBefore = GL11.glIsEnabled(GL11.GL_BLEND);
|
||||
final int[] driverFuncBefore = driverBlendFunc();
|
||||
assertTrue(driverEnabledBefore, "driver blend enable carries the override");
|
||||
assertArrayEquals(new int[] {GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE}, driverFuncBefore, "driver blend func carries the override");
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.BLEND);
|
||||
final boolean vanillaEnabledBefore = BlendModeStorage.ENABLE_LAYER.getVanilla();
|
||||
@@ -115,6 +133,9 @@ class StateSetOverrideGLTest {
|
||||
setBlendFunc.run();
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertEquals(driverEnabledBefore, GL11.glIsEnabled(GL11.GL_BLEND), "driver blend enable left untouched by the bracket");
|
||||
assertArrayEquals(driverFuncBefore, driverBlendFunc(), "driver blend func left untouched by the bracket");
|
||||
|
||||
assertTrue(BlendModeStorage.ENABLE_LAYER.getVanilla(), "vanilla shadow enable restored to the pre-bracket value");
|
||||
final BlendState vanillaAfter = new BlendState();
|
||||
BlendModeStorage.FUNC_LAYER.readVanilla(vanillaAfter);
|
||||
@@ -176,12 +197,19 @@ class StateSetOverrideGLTest {
|
||||
|
||||
final boolean liveMaskBefore = GLStateManager.getDepthState().isEnabled();
|
||||
final ColorMask liveColorBefore = new ColorMask().set(GLStateManager.getColorMask());
|
||||
final boolean driverDepthBefore = GL11.glGetBoolean(GL11.GL_DEPTH_WRITEMASK);
|
||||
final boolean[] driverColorBefore = driverColorMask();
|
||||
assertFalse(driverDepthBefore, "driver depth mask carries the held-off value");
|
||||
assertFalse(driverColorBefore[0] || driverColorBefore[1] || driverColorBefore[2] || driverColorBefore[3], "driver color mask carries the held-off value");
|
||||
|
||||
final int d = GLStateManager.pushState(StateSet.MASKS);
|
||||
GLStateManager.glDepthMask(false);
|
||||
GLStateManager.glColorMask(true, true, true, true);
|
||||
GLStateManager.popStateTo(d);
|
||||
|
||||
assertEquals(driverDepthBefore, GL11.glGetBoolean(GL11.GL_DEPTH_WRITEMASK), "driver depth mask left untouched by the bracket");
|
||||
assertArrayEquals(driverColorBefore, driverColorMask(), "driver color mask left untouched by the bracket");
|
||||
|
||||
final DepthState vanillaDepthAfter = new DepthState();
|
||||
DepthColorStorage.DEPTH_LAYER.readVanilla(vanillaDepthAfter);
|
||||
assertTrue(vanillaDepthAfter.isEnabled(), "vanilla shadow depth mask restored to the pre-bracket value");
|
||||
|
||||
@@ -33,10 +33,14 @@ 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.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL13;
|
||||
import org.lwjgl.opengl.GL14;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@@ -98,6 +102,12 @@ class TesrLayerStateTest {
|
||||
GLStateManager.glDisable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
private static void assertDriverColorMask(boolean expected) {
|
||||
final ByteBuffer mask = BufferUtils.createByteBuffer(16);
|
||||
GL11.glGetBoolean(GL11.GL_COLOR_WRITEMASK, mask);
|
||||
for (int i = 0; i < 4; i++) assertEquals(expected, mask.get(i) != 0, "driver color mask channel " + i);
|
||||
}
|
||||
|
||||
private static RenderLayer layer(TesrMaterial material) {
|
||||
return RenderLayer.tesr(null, material, PassOverride.NONE, 0f, 0f, ShaderGlint.NO_TINT, DrawState.CULL_BACK, true, true);
|
||||
}
|
||||
@@ -197,10 +207,14 @@ class TesrLayerStateTest {
|
||||
() -> {
|
||||
assertEquals(0, GLStateManager.getActiveProgram(), "program during batch draw");
|
||||
assertEquals(0, GLStateManager.getDrawFramebuffer(), "framebuffer during batch draw");
|
||||
assertEquals(0, GL11.glGetInteger(GL20.GL_CURRENT_PROGRAM), "driver program during batch draw");
|
||||
assertEquals(0, GL11.glGetInteger(GL30.GL_DRAW_FRAMEBUFFER_BINDING), "driver draw framebuffer during batch draw");
|
||||
},
|
||||
() -> {
|
||||
assertEquals(program, GLStateManager.getActiveProgram(), "caller program restored");
|
||||
assertEquals(framebuffer, GLStateManager.getDrawFramebuffer(), "caller framebuffer restored");
|
||||
assertEquals(program, GL11.glGetInteger(GL20.GL_CURRENT_PROGRAM), "driver caller program restored");
|
||||
assertEquals(framebuffer, GL11.glGetInteger(GL30.GL_DRAW_FRAMEBUFFER_BINDING), "driver caller framebuffer restored");
|
||||
},
|
||||
() -> {
|
||||
GLStateManager.glUseProgram(0);
|
||||
@@ -220,8 +234,16 @@ class TesrLayerStateTest {
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
|
||||
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
|
||||
return new BatchScenario(
|
||||
() -> assertEquals(GL11.GL_LINEAR, TextureInfoCache.INSTANCE.getInfo(texture).getMinFilter(), "filter from before the window"),
|
||||
() -> assertEquals(GL11.GL_NEAREST, TextureInfoCache.INSTANCE.getInfo(texture).getMinFilter(), "window filter restored"),
|
||||
() -> {
|
||||
assertEquals(GL11.GL_LINEAR, TextureInfoCache.INSTANCE.getInfo(texture).getMinFilter(), "filter from before the window");
|
||||
assertEquals(GL11.GL_LINEAR, GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER), "driver min filter from before the window");
|
||||
assertEquals(GL11.GL_LINEAR, GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER), "driver mag filter from before the window");
|
||||
},
|
||||
() -> {
|
||||
assertEquals(GL11.GL_NEAREST, TextureInfoCache.INSTANCE.getInfo(texture).getMinFilter(), "window filter restored");
|
||||
assertEquals(GL11.GL_NEAREST, GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER), "driver window min filter restored");
|
||||
assertEquals(GL11.GL_NEAREST, GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER), "driver window mag filter restored");
|
||||
},
|
||||
() -> {
|
||||
TextureUtilHooks.clearSavedFilterTexture();
|
||||
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||
@@ -423,6 +445,14 @@ class TesrLayerStateTest {
|
||||
assertTrue(GLStateManager.getLightingState().isEnabled(), "lit");
|
||||
assertTrue(GLStateManager.getColorMask().red, "color mask pinned on");
|
||||
assertFalse(GLStateManager.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL), "no polygon offset");
|
||||
assertTrue(GL11.glIsEnabled(GL11.GL_DEPTH_TEST), "driver depth test");
|
||||
assertEquals(GL11.GL_LEQUAL, GL11.glGetInteger(GL11.GL_DEPTH_FUNC), "driver depth func");
|
||||
assertTrue(GL11.glGetBoolean(GL11.GL_DEPTH_WRITEMASK), "driver depth write");
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_BLEND), "driver opaque");
|
||||
assertTrue(GL11.glIsEnabled(GL11.GL_CULL_FACE), "driver cull pinned on");
|
||||
assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face pinned to back");
|
||||
assertDriverColorMask(true);
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL), "driver no polygon offset");
|
||||
}),
|
||||
Arguments.of("every material toggle", (Runnable) () -> { },
|
||||
TesrMaterial.builder().noCull().unlit().noDepthWrite().cutout(0.5f).translucent().depthEqual().build(), (Runnable) () -> {
|
||||
@@ -435,6 +465,12 @@ class TesrLayerStateTest {
|
||||
assertEquals(GL11.GL_SRC_ALPHA, GLStateManager.getBlendState().getSrcRgb());
|
||||
assertEquals(GL11.GL_ONE_MINUS_SRC_ALPHA, GLStateManager.getBlendState().getDstRgb());
|
||||
assertEquals(GL11.GL_EQUAL, GLStateManager.getDepthState().getFunc(), "depthEqual");
|
||||
assertFalse(GL11.glIsEnabled(GL11.GL_CULL_FACE), "driver noCull");
|
||||
assertFalse(GL11.glGetBoolean(GL11.GL_DEPTH_WRITEMASK), "driver noDepthWrite");
|
||||
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND), "driver translucent");
|
||||
assertEquals(GL11.GL_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb");
|
||||
assertEquals(GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_DST_RGB), "driver blend dst rgb");
|
||||
assertEquals(GL11.GL_EQUAL, GL11.glGetInteger(GL11.GL_DEPTH_FUNC), "driver depthEqual");
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
+43
-68
@@ -18,12 +18,16 @@ import org.embeddedt.embeddium.impl.render.chunk.vertex.format.ChunkVertexType;
|
||||
import org.embeddedt.embeddium.impl.render.viewport.CameraTransform;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
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.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
@@ -151,6 +155,15 @@ class ShadowVoxelizerTest {
|
||||
return sink;
|
||||
}
|
||||
|
||||
private RecordingSink walkSingleSection(RenderRegion region, CameraTransform camera, boolean faceCulling, int... vertsPerFacing) {
|
||||
final TerrainRenderPass renderPass = pass("solid");
|
||||
final long heap = heap();
|
||||
final SectionRenderDataStorage storage = storage(heap);
|
||||
writeSection(heap, 0, 0, vertsPerFacing);
|
||||
attachStorage(region, renderPass, storage);
|
||||
return walk(lists(renderList(region, 0)), renderPass, camera, faceCulling);
|
||||
}
|
||||
|
||||
private final List<Long> heaps = new ArrayList<>();
|
||||
|
||||
@AfterEach
|
||||
@@ -182,82 +195,53 @@ class ShadowVoxelizerTest {
|
||||
assertArrayEqualsInt(new int[]{116, 4}, sink.ranges.get(4), "section 5 facing 4");
|
||||
}
|
||||
|
||||
@Test
|
||||
void regionOffsetIsCameraRelative() {
|
||||
final TerrainRenderPass renderPass = pass("solid");
|
||||
final RenderRegion region = region(1, 0, 3);
|
||||
final long heap = heap();
|
||||
final SectionRenderDataStorage storage = storage(heap);
|
||||
writeSection(heap, 0, 0, 4, 0, 0, 0, 0, 0, 0);
|
||||
attachStorage(region, renderPass, storage);
|
||||
final CameraTransform camera = new CameraTransform(70.25, 12.5, -3.75);
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("regionOffsets")
|
||||
void regionOffsetIsCameraRelativeFromTheRegionOrigin(String label, int rx, int ry, int rz,
|
||||
double camX, double camY, double camZ, Float literalX) {
|
||||
final RenderRegion region = region(rx, ry, rz);
|
||||
final CameraTransform camera = new CameraTransform(camX, camY, camZ);
|
||||
|
||||
final RecordingSink sink = walk(lists(renderList(region, 0)), renderPass, camera, false);
|
||||
final RecordingSink sink = walkSingleSection(region, camera, false, 4, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
assertEquals(1, sink.regions.size());
|
||||
final float[] offset = sink.regions.get(0);
|
||||
assertEquals(region.getOriginX() - camera.intX - camera.fracX, offset[0], 0f, "x");
|
||||
assertEquals(region.getOriginY() - camera.intY - camera.fracY, offset[1], 0f, "y");
|
||||
assertEquals(region.getOriginZ() - camera.intZ - camera.fracZ, offset[2], 0f, "z");
|
||||
if (literalX != null) {
|
||||
assertEquals(literalX, offset[0], 0f, "region 1 starts at block 128");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void regionOffsetUsesTheRegionOriginNotA256BlockGrid() {
|
||||
final TerrainRenderPass renderPass = pass("solid");
|
||||
final RenderRegion region = region(1, 0, 0);
|
||||
final long heap = heap();
|
||||
final SectionRenderDataStorage storage = storage(heap);
|
||||
writeSection(heap, 0, 0, 4, 0, 0, 0, 0, 0, 0);
|
||||
attachStorage(region, renderPass, storage);
|
||||
|
||||
final RecordingSink sink = walk(lists(renderList(region, 0)), renderPass, new CameraTransform(0, 0, 0), false);
|
||||
|
||||
assertEquals(128f, sink.regions.get(0)[0], 0f, "region 1 starts at block 128");
|
||||
private static Stream<Arguments> regionOffsets() {
|
||||
return Stream.of(
|
||||
Arguments.of("camera relative", 1, 0, 3, 70.25, 12.5, -3.75, null),
|
||||
Arguments.of("region origin is not a 256 block grid", 1, 0, 0, 0.0, 0.0, 0.0, 128f));
|
||||
}
|
||||
|
||||
@Test
|
||||
void contiguousFacingsCollapseToOneRange() {
|
||||
final TerrainRenderPass renderPass = pass("solid");
|
||||
final RenderRegion region = region(0, 0, 0);
|
||||
final long heap = heap();
|
||||
final SectionRenderDataStorage storage = storage(heap);
|
||||
writeSection(heap, 0, 0, 4, 4, 4, 4, 4, 4, 4);
|
||||
attachStorage(region, renderPass, storage);
|
||||
@ParameterizedTest(name = "{0}")
|
||||
@MethodSource("facingRuns")
|
||||
void facingMaskProducesTheExpectedRanges(String label, int[] vertsPerFacing, int[][] expectedRanges) {
|
||||
final RecordingSink sink = walkSingleSection(region(0, 0, 0), new CameraTransform(0, 0, 0), false, vertsPerFacing);
|
||||
|
||||
final RecordingSink sink = walk(lists(renderList(region, 0)), renderPass, new CameraTransform(0, 0, 0), false);
|
||||
|
||||
assertEquals(1, sink.ranges.size(), "seven adjacent facings are one vertex span, not seven dispatches");
|
||||
assertEquals(0, sink.ranges.get(0)[0]);
|
||||
assertEquals(28, sink.ranges.get(0)[1]);
|
||||
assertEquals(expectedRanges.length, sink.ranges.size());
|
||||
for (int i = 0; i < expectedRanges.length; i++) {
|
||||
assertArrayEqualsInt(expectedRanges[i], sink.ranges.get(i), "range " + i);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void gapsInTheFacingMaskSplitRuns() {
|
||||
final TerrainRenderPass renderPass = pass("solid");
|
||||
final RenderRegion region = region(0, 0, 0);
|
||||
final long heap = heap();
|
||||
final SectionRenderDataStorage storage = storage(heap);
|
||||
writeSection(heap, 0, 0, 4, 0, 8, 0, 0, 0, 4);
|
||||
attachStorage(region, renderPass, storage);
|
||||
|
||||
final RecordingSink sink = walk(lists(renderList(region, 0)), renderPass, new CameraTransform(0, 0, 0), false);
|
||||
|
||||
assertEquals(3, sink.ranges.size());
|
||||
assertArrayEqualsInt(new int[]{0, 4}, sink.ranges.get(0), "facing 0");
|
||||
assertArrayEqualsInt(new int[]{4, 8}, sink.ranges.get(1), "facing 2");
|
||||
assertArrayEqualsInt(new int[]{12, 4}, sink.ranges.get(2), "facing 6");
|
||||
private static Stream<Arguments> facingRuns() {
|
||||
return Stream.of(
|
||||
Arguments.of("seven adjacent facings are one vertex span, not seven dispatches",
|
||||
new int[] {4, 4, 4, 4, 4, 4, 4}, new int[][] {{0, 28}}),
|
||||
Arguments.of("gaps in the facing mask split runs",
|
||||
new int[] {4, 0, 8, 0, 0, 0, 4}, new int[][] {{0, 4}, {4, 8}, {12, 4}}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void sectionsWithoutGeometryNeverAnnounceTheRegion() {
|
||||
final TerrainRenderPass renderPass = pass("solid");
|
||||
final RenderRegion region = region(0, 0, 0);
|
||||
final long heap = heap();
|
||||
final SectionRenderDataStorage storage = storage(heap);
|
||||
writeSection(heap, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
attachStorage(region, renderPass, storage);
|
||||
|
||||
final RecordingSink sink = walk(lists(renderList(region, 0)), renderPass, new CameraTransform(0, 0, 0), false);
|
||||
final RecordingSink sink = walkSingleSection(region(0, 0, 0), new CameraTransform(0, 0, 0), false, 0, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
assertTrue(sink.regions.isEmpty(), "an empty region must not open an encoder");
|
||||
assertTrue(sink.ranges.isEmpty());
|
||||
@@ -297,16 +281,7 @@ class ShadowVoxelizerTest {
|
||||
|
||||
@Test
|
||||
void faceCullingNarrowsTheEmittedRanges() {
|
||||
final TerrainRenderPass renderPass = pass("solid");
|
||||
final RenderRegion region = region(0, 0, 0);
|
||||
final long heap = heap();
|
||||
final SectionRenderDataStorage storage = storage(heap);
|
||||
writeSection(heap, 0, 0, 4, 4, 4, 4, 4, 4, 4);
|
||||
attachStorage(region, renderPass, storage);
|
||||
final ChunkRenderListIterable renderLists = lists(renderList(region, 0));
|
||||
final CameraTransform camera = new CameraTransform(500, 200, 500);
|
||||
|
||||
final RecordingSink sink = walk(renderLists, renderPass, camera, true);
|
||||
final RecordingSink sink = walkSingleSection(region(0, 0, 0), new CameraTransform(500, 200, 500), true, 4, 4, 4, 4, 4, 4, 4);
|
||||
|
||||
assertFalse(sink.ranges.isEmpty(), "face culling removed everything; the fixture is not exercising the path");
|
||||
assertTrue(sink.ranges.size() < 7 || sink.ranges.get(0)[1] < 28, "face culling did not narrow anything");
|
||||
|
||||
Loaded 100 of 112 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user