Answer legacy GL queries from tracked state (#2247)

This commit is contained in:
Jason Mitchell authored and GitHub committed 2026-10-10 17:31:02 -07:00
1 parent 2ed838acf1
commit 3978be161d
264 files changed
+7276 -9375

No files matched your search

+2
View File
@@ -10,4 +10,6 @@ on:
jobs:
build-and-test:
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@master
with:
disable-spotless-pr: true
secrets: inherit
@@ -96,6 +96,7 @@ public class Feature {
, GLStateManager.getSecondaryColor() // Current secondary color
, ShaderManager.getNormalStack() // Current normal vector
, ShaderManager.getTexCoordStack() // Current texture coordinates
, GLStateManager.getEdgeFlagState()
// Current color index
// Current raster position
// GL_CURRENT_RASTER_POSITION_VALID flag
@@ -158,6 +159,8 @@ public class Feature {
, GLStateManager.getPolygonOffsetPointState()
, GLStateManager.getPolygonOffsetLineState()
, GLStateManager.getPolygonOffsetFillState()
, GLStateManager.getPointSpriteState()
, GLStateManager.getVertexProgramTwoSideState()
, GLStateManager.getPolygonSmoothState()
, GLStateManager.getPolygonStippleState()
, GLStateManager.getRescaleNormalState()
@@ -178,6 +181,7 @@ public class Feature {
enableBits.add(GLStateManager.getTextures().getTexture1DStates(i));
enableBits.add(GLStateManager.getTextures().getTextureUnitStates(i));
enableBits.add(GLStateManager.getTextures().getTexture3DStates(i));
enableBits.add(GLStateManager.getTextures().getTextureCubeMapStates(i));
}
// Flags GL_TEXTURE_GEN_x where x is S, T, R, or Q
@@ -216,13 +220,7 @@ public class Feature {
// ^^ GL_FOG_MODE value
));
attribToFeatures.put(GL11.GL_HINT_BIT, ImmutableSet.of(
// GL_PERSPECTIVE_CORRECTION_HINT setting
// GL_POINT_SMOOTH_HINT setting
// GL_LINE_SMOOTH_HINT setting
// GL_POLYGON_SMOOTH_HINT setting
// GL_FOG_HINT setting
// GL_GENERATE_MIPMAP_HINT setting
// GL_TEXTURE_COMPRESSION_HINT setting
GLStateManager.getHintState()
));
attribToFeatures.put(GL11.GL_LIGHTING_BIT, ImmutableSet.of(
GLStateManager.getColorMaterial()
@@ -286,6 +284,7 @@ public class Feature {
));
attribToFeatures.put(GL11.GL_POINT_BIT, ImmutableSet.of(
GLStateManager.getPointSmoothState()
, GLStateManager.getPointSpriteState()
, GLStateManager.getPointState()
));
attribToFeatures.put(GL11.GL_POLYGON_BIT, ImmutableSet.of(
@@ -328,6 +327,10 @@ public class Feature {
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
textureAttribs.add(GLStateManager.getTextures().getTextureUnitBindings(i));
textureAttribs.add(GLStateManager.getTextures().getTexEnvState(i));
textureAttribs.add(GLStateManager.getTextures().getTexture1DStates(i));
textureAttribs.add(GLStateManager.getTextures().getTextureUnitStates(i));
textureAttribs.add(GLStateManager.getTextures().getTexture3DStates(i));
textureAttribs.add(GLStateManager.getTextures().getTextureCubeMapStates(i));
}
// Enable bits GL_TEXTURE_GEN_x where x is S, T, R, or Q
@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
import com.gtnewhorizons.angelica.glsm.states.ViewportState;
import org.joml.Matrix4f;
@@ -141,6 +142,7 @@ public class FeedbackManager {
public static void processDrawArrays(int mode, int first, int count) {
computeMVP();
final GLContextState glCtx = GLStateManager.ctx();
if (!glCtx.vaos.isAttribEnabled(0)) return;
final int stride = positionStride(glCtx);
final long posOffset = positionOffset(glCtx);
final int byteCount = count * stride;
@@ -154,6 +156,7 @@ public class FeedbackManager {
public static void processDrawElements(int mode, int indexCount, int type, long eboOffset) {
computeMVP();
final GLContextState glCtx = GLStateManager.ctx();
if (!glCtx.vaos.isAttribEnabled(0)) return;
final int stride = positionStride(glCtx);
final int elementSize = GLTypes.sizeBytes(type);
@@ -210,6 +213,7 @@ public class FeedbackManager {
private static void processDrawElementsImpl(int mode, int count, IndexReader reader) {
computeMVP();
final GLContextState glCtx = GLStateManager.ctx();
if (!glCtx.vaos.isAttribEnabled(0)) return;
final int stride = positionStride(glCtx);
int minIdx = Integer.MAX_VALUE, maxIdx = Integer.MIN_VALUE;
@@ -261,6 +265,14 @@ public class FeedbackManager {
}
private static void readPositionBuffer(long offset, ByteBuffer destination, GLContextState glCtx) {
final VAOManager.Attrib position = glCtx.vaos.attrib(0);
if (position != null && position.clientPointer != null) {
final ByteBuffer source = position.clientPointer.duplicate();
source.position(source.position() + (int) offset);
if (source.remaining() > destination.remaining()) source.limit(source.position() + destination.remaining());
destination.duplicate().put(source);
return;
}
final int positionVbo = glCtx.vaos.attribVBO(0);
final int previousVbo = glCtx.boundVBO;
@@ -47,6 +47,7 @@ import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import java.nio.DoubleBuffer;
@@ -169,6 +170,9 @@ public class GLContextState {
public final BooleanStateStack lineSmoothState = track(new BooleanStateStack(GL11.GL_LINE_SMOOTH));
public final BooleanStateStack lineStippleState = track(new BooleanStateStack(GL11.GL_LINE_STIPPLE, false, true));
public final BooleanStateStack pointSmoothState = track(new BooleanStateStack(GL11.GL_POINT_SMOOTH, false, true));
public final BooleanStateStack pointSpriteState = track(new BooleanStateStack(GL20.GL_POINT_SPRITE, false, true));
public final BooleanStateStack vertexProgramTwoSideState = track(new BooleanStateStack(GL20.GL_VERTEX_PROGRAM_TWO_SIDE, false, true));
public final BooleanStateStack edgeFlagState = track(new BooleanStateStack(GL11.GL_EDGE_FLAG, true, true));
public final BooleanStateStack polygonSmoothState = track(new BooleanStateStack(GL11.GL_POLYGON_SMOOTH));
public final BooleanStateStack polygonStippleState = track(new BooleanStateStack(GL11.GL_POLYGON_STIPPLE, false, true));
public final BooleanStateStack multisampleState = track(new BooleanStateStack(GL13.GL_MULTISAMPLE, true));
@@ -182,6 +186,7 @@ public class GLContextState {
public final BooleanStateStack polygonOffsetFillState = track(new BooleanStateStack(GL11.GL_POLYGON_OFFSET_FILL));
public final LineStateStack lineState = member(new LineStateStack(StackIdAllocator.nextId()));
public final PointStateStack pointState = member(new PointStateStack(StackIdAllocator.nextId()));
public final IntegerStateStack hintState = member(new IntegerStateStack(0, StackIdAllocator.nextId()).restoreBit(StateSet.R_HINT));
public final PolygonStateStack polygonState = member(new PolygonStateStack(StackIdAllocator.nextId()));
public final StencilStateStack stencilState = member(new StencilStateStack(StackIdAllocator.nextId()));
public int stencilBitMask = 0xFFFFFFFF;
@@ -238,10 +243,9 @@ public class GLContextState {
public final PixelStoreState[] clientAttribSavedPixelPack = new PixelStoreState[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
public final int[] clientAttribSavedUnpackBuffer = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
public final int[] clientAttribSavedPackBuffer = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
public final int[] clientAttribSavedTextureUnit = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
public final int[] clientAttribSavedVertexFlags = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
public final boolean[] clientAttribSavedVertexArray = new boolean[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
public final VAOManager.ClientArrays[] clientAttribSavedArrays = new VAOManager.ClientArrays[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH];
public int clientAttribStackPointer = 0;
public float maxPointSize = Float.NaN;
public int restoreChangedMask;
public long restoreUnitChangedMask;
public final BlendState vanillaBlendBefore = new BlendState();
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,167 @@
package com.gtnewhorizons.angelica.glsm;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
final class LegacyQueries {
static final int BOOL = 1;
static final int INT = 2;
static final int FLOAT = 3;
static final int FLOATN = 4;
private static final int COUNT_SHIFT = 3;
private LegacyQueries() {}
static int desc(int pname) {
return switch (pname) {
case GL11.GL_ALPHA_TEST, GL11.GL_AUTO_NORMAL, GL11.GL_COLOR_MATERIAL, GL14.GL_COLOR_SUM, GL11.GL_FOG,
GL11.GL_INDEX_LOGIC_OP, GL11.GL_LIGHTING, GL11.GL_LIGHT0, GL11.GL_LIGHT1, GL11.GL_LIGHT2, GL11.GL_LIGHT3,
GL11.GL_LIGHT4, GL11.GL_LIGHT5, GL11.GL_LIGHT6, GL11.GL_LIGHT7, GL11.GL_LINE_STIPPLE, GL11.GL_MAP1_COLOR_4,
GL11.GL_MAP1_INDEX, GL11.GL_MAP1_NORMAL, GL11.GL_MAP1_TEXTURE_COORD_1, GL11.GL_MAP1_TEXTURE_COORD_2,
GL11.GL_MAP1_TEXTURE_COORD_3, GL11.GL_MAP1_TEXTURE_COORD_4, GL11.GL_MAP1_VERTEX_3, GL11.GL_MAP1_VERTEX_4,
GL11.GL_MAP2_COLOR_4, GL11.GL_MAP2_INDEX, GL11.GL_MAP2_NORMAL, GL11.GL_MAP2_TEXTURE_COORD_1,
GL11.GL_MAP2_TEXTURE_COORD_2, GL11.GL_MAP2_TEXTURE_COORD_3, GL11.GL_MAP2_TEXTURE_COORD_4,
GL11.GL_MAP2_VERTEX_3, GL11.GL_MAP2_VERTEX_4, GL11.GL_NORMALIZE, GL11.GL_POINT_SMOOTH,
GL11.GL_POLYGON_STIPPLE, GL12.GL_RESCALE_NORMAL, GL11.GL_TEXTURE_1D, GL12.GL_TEXTURE_3D,
GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_GEN_S, GL11.GL_TEXTURE_GEN_T, GL11.GL_TEXTURE_GEN_R,
GL11.GL_TEXTURE_GEN_Q, GL11.GL_LIGHT_MODEL_LOCAL_VIEWER, GL11.GL_LIGHT_MODEL_TWO_SIDE,
GL11.GL_VERTEX_ARRAY, GL11.GL_NORMAL_ARRAY, GL11.GL_COLOR_ARRAY, GL11.GL_TEXTURE_COORD_ARRAY,
GL11.GL_INDEX_ARRAY, GL11.GL_EDGE_FLAG_ARRAY, GL14.GL_SECONDARY_COLOR_ARRAY, GL14.GL_FOG_COORDINATE_ARRAY,
GL11.GL_EDGE_FLAG, GL20.GL_POINT_SPRITE, GL20.GL_VERTEX_PROGRAM_TWO_SIDE -> BOOL;
case GL11.GL_ALPHA_TEST_FUNC, GL11.GL_LIST_BASE, GL11.GL_MATRIX_MODE, GL11.GL_SHADE_MODEL,
GL11.GL_COLOR_MATERIAL_FACE, GL11.GL_COLOR_MATERIAL_PARAMETER, GL12.GL_LIGHT_MODEL_COLOR_CONTROL,
GL11.GL_FOG_MODE, GL11.GL_LINE_STIPPLE_PATTERN, GL11.GL_LINE_STIPPLE_REPEAT, GL13.GL_CLIENT_ACTIVE_TEXTURE,
GL20.GL_MAX_TEXTURE_COORDS, GL11.GL_RENDER_MODE, GL11.GL_FOG_HINT, GL11.GL_PERSPECTIVE_CORRECTION_HINT,
GL11.GL_POINT_SMOOTH_HINT, GL14.GL_GENERATE_MIPMAP_HINT, GL11.GL_LINE_SMOOTH_HINT, GL11.GL_POLYGON_SMOOTH_HINT,
GL13.GL_TEXTURE_COMPRESSION_HINT, GL20.GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL20.GL_POINT_SPRITE_COORD_ORIGIN,
GL11.GL_VERTEX_ARRAY_SIZE, GL11.GL_VERTEX_ARRAY_TYPE, GL11.GL_VERTEX_ARRAY_STRIDE, GL15.GL_VERTEX_ARRAY_BUFFER_BINDING,
GL11.GL_NORMAL_ARRAY_TYPE, GL11.GL_NORMAL_ARRAY_STRIDE, GL15.GL_NORMAL_ARRAY_BUFFER_BINDING,
GL11.GL_COLOR_ARRAY_SIZE, GL11.GL_COLOR_ARRAY_TYPE, GL11.GL_COLOR_ARRAY_STRIDE, GL15.GL_COLOR_ARRAY_BUFFER_BINDING,
GL11.GL_INDEX_ARRAY_TYPE, GL11.GL_INDEX_ARRAY_STRIDE, GL15.GL_INDEX_ARRAY_BUFFER_BINDING,
GL11.GL_TEXTURE_COORD_ARRAY_SIZE, GL11.GL_TEXTURE_COORD_ARRAY_TYPE, GL11.GL_TEXTURE_COORD_ARRAY_STRIDE,
GL15.GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, GL11.GL_EDGE_FLAG_ARRAY_STRIDE, GL15.GL_EDGE_FLAG_ARRAY_BUFFER_BINDING,
GL14.GL_SECONDARY_COLOR_ARRAY_SIZE, GL14.GL_SECONDARY_COLOR_ARRAY_TYPE, GL14.GL_SECONDARY_COLOR_ARRAY_STRIDE,
GL15.GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING, GL14.GL_FOG_COORDINATE_ARRAY_TYPE, GL14.GL_FOG_COORDINATE_ARRAY_STRIDE,
GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING -> INT;
case GL11.GL_FOG_DENSITY, GL11.GL_FOG_START, GL11.GL_FOG_END, GL14.GL_POINT_SIZE_MIN, GL14.GL_POINT_SIZE_MAX,
GL14.GL_POINT_FADE_THRESHOLD_SIZE -> FLOAT;
case GL11.GL_ALPHA_TEST_REF -> FLOATN;
case GL14.GL_POINT_DISTANCE_ATTENUATION -> FLOAT | 2 << COUNT_SHIFT;
case GL11.GL_CURRENT_TEXTURE_COORDS -> FLOAT | 3 << COUNT_SHIFT;
case GL11.GL_MODELVIEW_MATRIX, GL11.GL_PROJECTION_MATRIX, GL11.GL_TEXTURE_MATRIX -> FLOATN | 15 << COUNT_SHIFT;
case GL11.GL_CURRENT_NORMAL -> FLOATN | 2 << COUNT_SHIFT;
case GL11.GL_CURRENT_COLOR, GL14.GL_CURRENT_SECONDARY_COLOR, GL11.GL_FOG_COLOR, GL11.GL_LIGHT_MODEL_AMBIENT -> FLOATN | 3 << COUNT_SHIFT;
default -> 0;
};
}
static int type(int desc) {
return desc & 7;
}
static int count(int desc) {
return (desc >> COUNT_SHIFT) + 1;
}
static int floatToInt(float value) {
return (int) (2147483647.0 * value);
}
static int lround(float value) {
return value < 0 ? -Math.round(-value) : Math.round(value);
}
static int toInt(int type, float value) {
return type == FLOATN ? floatToInt(value) : lround(value);
}
private static FloatBuffer fillScratch(GLContextState glCtx, int pname) {
final FloatBuffer scratch = glCtx.queryScratch;
scratch.clear();
GLStateManager.glGetFloat(pname, scratch);
return scratch;
}
private static IllegalStateException uncached(int pname) {
return new IllegalStateException("Legacy pname 0x" + Integer.toHexString(pname) + " has no cached answer for its declared type");
}
static int getInteger(GLContextState glCtx, int pname, int desc) {
final int type = type(desc);
if (count(desc) > 1) {
return toInt(type, fillScratch(glCtx, pname).get(0));
}
return switch (type) {
case BOOL -> GLStateManager.glGetBoolean(glCtx, pname) ? GL11.GL_TRUE : GL11.GL_FALSE;
case FLOAT -> lround(GLStateManager.glGetFloat(glCtx, pname));
case FLOATN -> floatToInt(GLStateManager.glGetFloat(glCtx, pname));
default -> throw uncached(pname);
};
}
static float getFloat(GLContextState glCtx, int pname, int desc) {
if (count(desc) > 1) {
return fillScratch(glCtx, pname).get(0);
}
return switch (type(desc)) {
case BOOL -> GLStateManager.glGetBoolean(glCtx, pname) ? 1.0f : 0.0f;
case INT -> (float) GLStateManager.glGetInteger(glCtx, pname);
default -> throw uncached(pname);
};
}
static boolean getBoolean(GLContextState glCtx, int pname, int desc) {
if (count(desc) > 1) {
return fillScratch(glCtx, pname).get(0) != 0.0f;
}
return switch (type(desc)) {
case INT -> GLStateManager.glGetInteger(glCtx, pname) != 0;
case FLOAT, FLOATN -> GLStateManager.glGetFloat(glCtx, pname) != 0.0f;
default -> throw uncached(pname);
};
}
static boolean getIntegerv(GLContextState glCtx, int pname, IntBuffer params) {
final int desc = desc(pname);
if (desc == 0) return false;
final int pos = params.position();
final int type = type(desc);
final FloatBuffer scratch = fillScratch(glCtx, pname);
for (int i = 0, count = count(desc); i < count; i++) {
params.put(pos + i, toInt(type, scratch.get(i)));
}
return true;
}
static boolean getIntegerv(GLContextState glCtx, int pname, int[] params) {
final int desc = desc(pname);
if (desc == 0) return false;
final int type = type(desc);
final FloatBuffer scratch = fillScratch(glCtx, pname);
for (int i = 0, count = Math.min(count(desc), params.length); i < count; i++) {
params[i] = toInt(type, scratch.get(i));
}
return true;
}
static boolean getBooleanv(GLContextState glCtx, int pname, ByteBuffer params) {
final int desc = desc(pname);
if (desc == 0) return false;
final int pos = params.position();
final FloatBuffer scratch = fillScratch(glCtx, pname);
for (int i = 0, count = count(desc); i < count; i++) {
params.put(pos + i, (byte) (scratch.get(i) != 0.0f ? GL11.GL_TRUE : GL11.GL_FALSE));
}
return true;
}
}
@@ -24,6 +24,7 @@ public final class StateSet {
public static final int R_ACTIVE_UNIT = 1 << 13;
public static final int R_PROGRAM = 1 << 14;
public static final int R_SCISSOR = 1 << 15;
public static final int R_HINT = 1 << 16;
public static final int B_FRAG_COLORBUF = 1;
public static final int B_FRAG_TEXTURE = 1 << 1;
@@ -224,6 +225,9 @@ public final class StateSet {
if ((mask & GL11.GL_POINT_BIT) != 0) {
restore |= R_POINT;
}
if ((mask & GL11.GL_HINT_BIT) != 0) {
restore |= R_HINT;
}
if ((mask & GL11.GL_POLYGON_BIT) != 0) {
restore |= R_POLYGON;
}
@@ -1,13 +1,20 @@
package com.gtnewhorizons.angelica.glsm.ffp;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GLTypes;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GL41;
import javax.annotation.Nullable;
import java.nio.ByteBuffer;
import java.util.Arrays;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
@@ -19,12 +26,116 @@ public final class VAOManager {
final Int2ObjectOpenHashMap<VAOData> eboOwners = new Int2ObjectOpenHashMap<>();
private VAOData current = null;
public static final int CONV_POSITION = 0;
public static final int CONV_NORMAL = 1;
public static final int CONV_COLOR = 2;
public static final int CONV_SECONDARY_COLOR = 3;
public static final int CONV_FOG_COORD = 4;
public static final int CONV_INDEX = 5;
public static final int CONV_EDGE_FLAG = 6;
public static final int CONV_TEX0 = 7;
public static final int TEX_COORD_UNITS = 8;
public static final int CONV_COUNT = CONV_TEX0 + TEX_COORD_UNITS;
private static final int[] CONV_LOCATION = new int[CONV_COUNT];
private static final int[] LOCATION_CONV = new int[MAX_ATTRIBS];
static {
Arrays.fill(CONV_LOCATION, -1);
Arrays.fill(LOCATION_CONV, -1);
mapConventional(CONV_POSITION, Usage.POSITION);
mapConventional(CONV_NORMAL, Usage.NORMAL);
mapConventional(CONV_COLOR, Usage.COLOR);
mapConventional(CONV_TEX0, Usage.PRIMARY_UV);
mapConventional(CONV_TEX0 + 1, Usage.SECONDARY_UV);
}
private static void mapConventional(int conv, Usage usage) {
final int location = usage.getAttributeLocation();
CONV_LOCATION[conv] = location;
LOCATION_CONV[location] = conv;
}
public static int conventionalLocation(int conv) {
return CONV_LOCATION[conv];
}
private static final int T_BYTE = 1, T_UBYTE = 1 << 1, T_SHORT = 1 << 2, T_USHORT = 1 << 3, T_INT = 1 << 4, T_UINT = 1 << 5, T_FLOAT = 1 << 6;
private static final int T_DOUBLE = 1 << 10, T_HALF = 1 << 11, T_FIXED = 1 << 12, T_PACKED = 1 << 13, T_PACKED_FLOAT = 1 << 14;
private static final int T_INTEGER = T_BYTE | T_UBYTE | T_SHORT | T_USHORT | T_INT | T_UINT;
private static final int T_COLOR = T_INTEGER | T_HALF | T_FLOAT | T_DOUBLE | T_PACKED;
private static final int T_GENERIC = T_COLOR | T_FIXED | T_PACKED_FLOAT;
private static final int[] LEGAL_TYPES = new int[CONV_COUNT];
private static final int[] MIN_SIZE = new int[CONV_COUNT];
private static final int[] MAX_SIZE = new int[CONV_COUNT];
static {
legal(CONV_POSITION, T_SHORT | T_INT | T_FLOAT | T_DOUBLE | T_HALF | T_PACKED, 2, 4);
legal(CONV_NORMAL, T_BYTE | T_SHORT | T_INT | T_HALF | T_FLOAT | T_DOUBLE | T_PACKED, 3, 3);
legal(CONV_COLOR, T_COLOR, 3, 4);
legal(CONV_SECONDARY_COLOR, T_COLOR, 3, 4);
legal(CONV_FOG_COORD, T_HALF | T_FLOAT | T_DOUBLE, 1, 1);
legal(CONV_INDEX, T_UBYTE | T_SHORT | T_INT | T_FLOAT | T_DOUBLE, 1, 1);
legal(CONV_EDGE_FLAG, T_UBYTE, 1, 1);
for (int unit = 0; unit < TEX_COORD_UNITS; unit++) {
legal(CONV_TEX0 + unit, T_SHORT | T_INT | T_HALF | T_FLOAT | T_DOUBLE | T_PACKED, 1, 4);
}
}
private static void legal(int conv, int types, int minSize, int maxSize) {
LEGAL_TYPES[conv] = types;
MIN_SIZE[conv] = minSize;
MAX_SIZE[conv] = maxSize;
}
private static int typeBit(int type) {
if (type >= GL11.GL_BYTE && type <= GL30.GL_HALF_FLOAT) return 1 << type - GL11.GL_BYTE;
return switch (type) {
case GL41.GL_FIXED -> T_FIXED;
case GL12.GL_UNSIGNED_INT_2_10_10_10_REV, GL33.GL_INT_2_10_10_10_REV -> T_PACKED;
case GL30.GL_UNSIGNED_INT_10F_11F_11F_REV -> T_PACKED_FLOAT;
default -> 0;
};
}
private static boolean isLegal(int types, int minSize, int maxSize, boolean bgra, int size, int type) {
final int bit = typeBit(type);
if ((types & bit) == 0) return false;
if (size == GL12.GL_BGRA) return bgra && (bit & (T_UBYTE | T_PACKED)) != 0;
return size >= minSize && size <= maxSize;
}
public static boolean isLegalConventional(int conv, int size, int type) {
return isLegal(LEGAL_TYPES[conv], MIN_SIZE[conv], MAX_SIZE[conv], conv == CONV_COLOR || conv == CONV_SECONDARY_COLOR, size, type);
}
public static boolean isLegalGeneric(int index, int size, int type, boolean integer) {
return index >= 0 && index < MAX_ATTRIBS && isLegal(integer ? T_INTEGER : T_GENERIC, 1, 4, !integer, size, type);
}
private int currentVertexFlags = 0;
private int genericEnabled = 0;
private int conventionalEnabled = 0;
private Attrib[] currentAttribs = null;
private Attrib[] currentGeneric = null;
private Attrib[] currentConventional = null;
public int boundEBO = 0;
private int clientSideEnabledCount = 0;
private int arrayVersion = 0;
private int versionClock = 0;
private int bufferEpoch = 0;
private final Int2IntOpenHashMap bufferDeletedAt = new Int2IntOpenHashMap();
private void touch() {
arrayVersion = ++versionClock;
}
private boolean isBufferLive(int buffer, int sinceEpoch) {
return bufferDeletedAt.get(buffer) <= sinceEpoch;
}
public boolean isBufferLive(Attrib a) {
return isBufferLive(a.vboId, a.epoch);
}
public void init(int defaultVAO) {
final VAOData data = new VAOData();
@@ -45,6 +156,10 @@ public final class VAOManager {
// Save old VAO data
if (current != null) {
current.vertexFlags = currentVertexFlags;
current.genericEnabled = genericEnabled;
current.conventionalEnabled = conventionalEnabled;
current.generic = currentGeneric;
current.conventional = currentConventional;
if (current.ebo != boundEBO) {
removeEboOwner(current.ebo, current);
current.ebo = boundEBO;
@@ -52,6 +167,7 @@ public final class VAOManager {
}
current.attribs = currentAttribs;
current.clientSideEnabledCount = clientSideEnabledCount;
current.arrayVersion = arrayVersion;
}
VAOData data = vaoMap.get(vaoId);
@@ -61,9 +177,14 @@ public final class VAOManager {
}
current = data;
currentVertexFlags = data.vertexFlags;
genericEnabled = data.genericEnabled;
conventionalEnabled = data.conventionalEnabled;
currentAttribs = data.attribs;
currentGeneric = data.generic;
currentConventional = data.conventional;
boundEBO = data.ebo;
clientSideEnabledCount = data.clientSideEnabledCount;
arrayVersion = data.arrayVersion;
}
public void onDeleteVertexArray(int vaoId) {
@@ -73,8 +194,25 @@ public final class VAOManager {
if (data == current) current = null;
}
private static boolean detach(Attrib[] slots, int buffer) {
boolean detached = false;
for (final Attrib a : slots) {
if (a != null && a.vboId == buffer) {
a.vboId = 0;
detached = true;
}
}
return detached;
}
public void onDeleteBuffer(int buffer) {
if (buffer == 0) return;
bufferDeletedAt.put(buffer, ++bufferEpoch);
if (currentAttribs != null) {
boolean detached = detach(currentGeneric != null ? currentGeneric : currentAttribs, buffer);
if (currentConventional != null) detached |= detach(currentConventional, buffer);
if (detached) touch();
}
VAOData owner = eboOwners.remove(buffer);
while (owner != null) {
final VAOData next = owner.nextEboOwner;
@@ -112,14 +250,19 @@ public final class VAOManager {
public void enableClientVertexFlag(int flag) { currentVertexFlags |= flag; }
public void disableClientVertexFlag(int flag) { currentVertexFlags &= ~flag; }
public int getVertexFlags() { return currentVertexFlags; }
public void setVertexFlags(int flags) { currentVertexFlags = flags; }
public boolean isConventionalEnabled(int conv) { return (conventionalEnabled >> conv & 1) != 0; }
public static void setCurrentVertexFlags(int flags) { GLStateManager.ctx().vaos.currentVertexFlags = flags; }
public static final class VAOData {
public Attrib[] attribs;
public int vertexFlags;
public int genericEnabled;
public int conventionalEnabled;
public Attrib[] generic;
public Attrib[] conventional;
public int ebo;
public int clientSideEnabledCount;
int arrayVersion;
private VAOData nextEboOwner;
public VAOData() {
@@ -127,92 +270,307 @@ public final class VAOManager {
}
}
public void setAttribute(int index, int size, int type, boolean normalized, int stride, long offset, int vboId) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
final boolean wasClient = a.clientPointer != null;
public boolean setAttribute(int index, int size, int type, boolean normalized, int stride, long offset, int vboId) {
return setGeneric(index, size, type, normalized, false, stride, offset, vboId, null);
}
public boolean setAttribute(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
return setGeneric(index, size, type, normalized, false, stride, 0, 0, pointer);
}
public boolean setIntegerAttribute(int index, int size, int type, int stride, long offset, int vboId) {
return setGeneric(index, size, type, false, true, stride, offset, vboId, null);
}
public boolean setIntegerAttribute(int index, int size, int type, int stride, ByteBuffer pointer) {
return setGeneric(index, size, type, false, true, stride, 0, 0, pointer);
}
public boolean setConventional(int conv, int size, int type, boolean normalized, int stride, long offset, int vboId) {
return setConventional(conv, size, type, normalized, stride, offset, vboId, null);
}
public boolean setConventional(int conv, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
return setConventional(conv, size, type, normalized, stride, 0, 0, pointer);
}
private boolean setGeneric(int index, int size, int type, boolean normalized, boolean integer, int stride, long offset, int vboId, ByteBuffer pointer) {
if (index < 0 || index >= MAX_ATTRIBS) return false;
final Attrib g = genericSlot(index);
store(g, size, type, normalized, integer, stride, offset, vboId, pointer);
if (currentAttribs[index] == g) return true;
if (!isGenericEnabled(index) && isLocationConventionalEnabled(index)) return false;
apply(index, g);
return true;
}
private boolean setConventional(int conv, int size, int type, boolean normalized, int stride, long offset, int vboId, ByteBuffer pointer) {
if (conv < 0 || conv >= CONV_COUNT) return false;
final Attrib c = conventionalSlot(conv);
store(c, size, type, normalized, false, stride, offset, vboId, pointer);
final int index = CONV_LOCATION[conv];
if (index < 0) return false;
if (currentAttribs[index] == c) return true;
if (isGenericEnabled(index)) return false;
apply(index, c);
return true;
}
private void store(Attrib a, int size, int type, boolean normalized, boolean integer, int stride, long offset, int vboId, ByteBuffer pointer) {
final boolean wasClient = a.isClientSide();
a.size = size;
a.type = type;
a.normalized = normalized;
a.integer = integer;
a.stride = stride;
a.offset = offset;
a.vboId = vboId;
a.clientPointer = null;
a.genericPointer = true;
if (wasClient && a.enabled) {
clientSideEnabledCount--;
}
}
public void setAttribute(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
final boolean wasClient = a.clientPointer != null;
a.size = size;
a.type = type;
a.normalized = normalized;
a.stride = stride;
a.offset = 0;
a.vboId = 0;
a.epoch = bufferEpoch;
a.clientPointer = pointer;
a.genericPointer = true;
if (!wasClient && a.enabled) {
clientSideEnabledCount++;
if (wasClient != a.isClientSide()) clientSideEnabledCount += wasClient ? -1 : 1;
touch();
}
private void apply(int index, Attrib source) {
final Attrib old = currentAttribs[index];
if (old != null) {
if (old.isClientSide()) clientSideEnabledCount--;
source.enabled = old.enabled;
old.enabled = false;
if (source.isClientSide()) clientSideEnabledCount++;
}
currentAttribs[index] = source;
}
private boolean isGenericEnabled(int index) {
return (genericEnabled >> index & 1) != 0;
}
private boolean isLocationConventionalEnabled(int index) {
final int conv = LOCATION_CONV[index];
return conv >= 0 && isConventionalEnabled(conv);
}
private Attrib generic(int index) {
return (currentGeneric != null ? currentGeneric : currentAttribs)[index];
}
private Attrib genericSlot(int index) {
final Attrib[] slots = currentGeneric != null ? currentGeneric : currentAttribs;
Attrib g = slots[index];
if (g == null) {
g = new Attrib();
g.genericPointer = true;
slots[index] = g;
if (currentAttribs[index] == null) currentAttribs[index] = g;
}
return g;
}
private Attrib conventionalSlot(int conv) {
if (currentConventional == null) {
currentConventional = new Attrib[CONV_COUNT];
currentGeneric = currentAttribs.clone();
}
Attrib c = currentConventional[conv];
if (c == null) {
c = new Attrib();
currentConventional[conv] = c;
}
return c;
}
private boolean refresh(int index) {
touch();
boolean repoint = false;
final boolean conventional = isLocationConventionalEnabled(index);
final Attrib winner = isGenericEnabled(index) ? genericSlot(index) : conventional ? currentConventional[LOCATION_CONV[index]] : null;
if (winner != null && winner != currentAttribs[index]) {
apply(index, winner);
repoint = true;
}
final boolean enabled = winner != null;
Attrib e = currentAttribs[index];
if (e == null) {
if (!enabled) return repoint;
e = genericSlot(index);
}
if (e.enabled != enabled) {
if (e.clientPointer != null) clientSideEnabledCount += enabled ? 1 : -1;
e.enabled = enabled;
}
return repoint;
}
public boolean enableAttribute(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return false;
genericEnabled |= 1 << index;
return refresh(index);
}
public boolean disableAttribute(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return false;
genericEnabled &= ~(1 << index);
return refresh(index);
}
public boolean setConventionalEnabled(int conv, boolean enabled) {
if (conv < 0 || conv >= CONV_COUNT) return false;
conventionalSlot(conv);
if (enabled) {
conventionalEnabled |= 1 << conv;
} else {
conventionalEnabled &= ~(1 << conv);
}
final int index = CONV_LOCATION[conv];
if (index >= 0) return refresh(index);
touch();
return false;
}
public boolean setDivisor(int index, int divisor) {
if (index < 0 || index >= MAX_ATTRIBS) return true;
final Attrib g = genericSlot(index);
g.divisor = divisor;
touch();
return currentAttribs[index] == g;
}
public @Nullable Attrib conventional(int conv) {
return conv >= 0 && currentConventional != null ? currentConventional[conv] : null;
}
public static final class ClientArrays {
public int boundVAO;
public int boundVBO;
public int boundEBO;
public int clientActiveTextureUnit;
int arrayVersion;
int bufferEpoch;
int vertexFlags;
int genericEnabled;
int conventionalEnabled;
int genericPresent;
int conventionalPresent;
int effectiveGeneric;
final Attrib[] generic = new Attrib[MAX_ATTRIBS];
final Attrib[] conventional = new Attrib[CONV_COUNT];
}
private static void saveSlot(Attrib[] saved, int i, Attrib source) {
if (saved[i] == null) saved[i] = new Attrib();
saved[i].copyPointer(source);
}
public boolean isBufferLive(int buffer, ClientArrays saved) {
return isBufferLive(buffer, saved.bufferEpoch);
}
public void saveClientArrays(ClientArrays out) {
out.arrayVersion = arrayVersion;
out.bufferEpoch = bufferEpoch;
out.vertexFlags = currentVertexFlags;
out.genericEnabled = genericEnabled;
out.conventionalEnabled = conventionalEnabled;
out.genericPresent = 0;
out.conventionalPresent = 0;
out.effectiveGeneric = 0;
for (int i = 0; i < MAX_ATTRIBS; i++) {
final Attrib g = generic(i);
if (g != null) {
out.genericPresent |= 1 << i;
saveSlot(out.generic, i, g);
}
final Attrib e = currentAttribs[i];
if (e != null && e.genericPointer) out.effectiveGeneric |= 1 << i;
}
if (currentConventional == null) return;
for (int i = 0; i < CONV_COUNT; i++) {
final Attrib c = currentConventional[i];
if (c != null) {
out.conventionalPresent |= 1 << i;
saveSlot(out.conventional, i, c);
}
}
}
public boolean isGenericPointerEnabled(int index) {
private final Attrib[] restoreBefore = new Attrib[MAX_ATTRIBS];
public int restoreClientArrays(ClientArrays in) {
currentVertexFlags = in.vertexFlags;
if (arrayVersion == in.arrayVersion) return 0;
for (int i = 0; i < MAX_ATTRIBS; i++) {
if (restoreBefore[i] == null) restoreBefore[i] = new Attrib();
final Attrib e = currentAttribs[i];
if (e == null) {
restoreBefore[i].clearPointer();
restoreBefore[i].enabled = false;
} else {
restoreBefore[i].copyPointer(e);
restoreBefore[i].enabled = e.enabled;
}
}
genericEnabled = in.genericEnabled;
conventionalEnabled = in.conventionalEnabled;
for (int i = 0; i < MAX_ATTRIBS; i++) {
if ((in.genericPresent >> i & 1) != 0) {
genericSlot(i).copyPointer(in.generic[i]);
} else {
final Attrib g = generic(i);
if (g != null) g.clearPointer();
}
}
if (currentConventional != null || in.conventionalPresent != 0) {
for (int i = 0; i < CONV_COUNT; i++) {
if ((in.conventionalPresent >> i & 1) != 0) {
conventionalSlot(i).copyPointer(in.conventional[i]);
} else if (currentConventional != null && currentConventional[i] != null) {
currentConventional[i].clearPointer();
}
}
}
clientSideEnabledCount = 0;
for (int i = 0; i < MAX_ATTRIBS; i++) {
final int conv = LOCATION_CONV[i];
final Attrib g = generic(i);
final Attrib c = conventional(conv);
final boolean conventional = isLocationConventionalEnabled(i);
Attrib e;
if (isGenericEnabled(i)) {
e = genericSlot(i);
} else if (conventional) {
e = conventionalSlot(conv);
} else {
e = c == null || (in.effectiveGeneric >> i & 1) != 0 ? g : c;
}
if (g != null) g.enabled = false;
if (c != null) c.enabled = false;
currentAttribs[i] = e;
if (e != null) {
e.enabled = isGenericEnabled(i) || conventional;
if (e.isClientSide()) clientSideEnabledCount++;
}
}
int changed = 0;
for (int i = 0; i < MAX_ATTRIBS; i++) {
final Attrib e = currentAttribs[i];
final Attrib before = restoreBefore[i];
if (e == null) {
if (before.enabled) changed |= 0x10000 << i;
} else {
if (e.enabled != before.enabled) changed |= 0x10000 << i;
if (!e.samePointer(before)) changed |= 1 << i;
}
before.clientPointer = null;
}
arrayVersion = in.arrayVersion;
return changed;
}
public boolean isAttribEnabled(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return false;
final Attrib a = currentAttribs[index];
return a != null && a.genericPointer && a.enabled;
}
public void markConventional(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = currentAttribs[index];
if (a != null) a.genericPointer = false;
}
public void setEnabled(int index, boolean enabled) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
if (a.clientPointer != null && a.enabled != enabled) {
clientSideEnabledCount += enabled ? 1 : -1;
}
a.enabled = enabled;
}
public void enableAttribute(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
if (a.clientPointer != null && !a.enabled) {
clientSideEnabledCount++;
}
a.enabled = true;
}
public void disableAttribute(int index) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = getAttrib(index);
if (a.clientPointer != null && a.enabled) {
clientSideEnabledCount--;
}
a.enabled = false;
}
// Lazily allocate each attrib. Most VAOs don't even have 4 attribs, let alone 16.
private Attrib getAttrib(int index) {
Attrib a = currentAttribs[index];
if (a == null) {
a = new Attrib();
currentAttribs[index] = a;
}
return a;
}
public boolean isClientSideAttrib(int index) {
final Attrib attrib = currentAttribs[index];
return attrib != null && attrib.isClientSide();
return a != null && a.enabled;
}
public Attrib attrib(int index) {
@@ -286,7 +644,11 @@ public final class VAOManager {
if (clientArraysVBOOffsets[i] < 0) continue;
final VAOManager.Attrib a = currentAttribs[i];
RENDER_BACKEND.bufferSubData(GL15.GL_ARRAY_BUFFER, clientArraysVBOOffsets[i], a.clientPointer.duplicate());
RENDER_BACKEND.vertexAttribPointer(i, a.size, a.type, a.normalized, a.stride, (long) clientArraysVBOOffsets[i]);
if (a.integer) {
RENDER_BACKEND.vertexAttribIPointer(i, a.size, a.type, a.stride, (long) clientArraysVBOOffsets[i]);
} else {
RENDER_BACKEND.vertexAttribPointer(i, a.size, a.type, a.normalized, a.stride, (long) clientArraysVBOOffsets[i]);
}
}
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
@@ -298,9 +660,12 @@ public final class VAOManager {
public int size;
public int type;
public boolean normalized;
public boolean integer;
public int stride;
public long offset;
public int vboId;
public int divisor;
int epoch;
public ByteBuffer clientPointer;
public boolean genericPointer;
@@ -308,16 +673,35 @@ public final class VAOManager {
return enabled && clientPointer != null;
}
public void reset() {
enabled = false;
void copyPointer(Attrib source) {
size = source.size;
type = source.type;
normalized = source.normalized;
integer = source.integer;
stride = source.stride;
offset = source.offset;
vboId = source.vboId;
divisor = source.divisor;
epoch = source.epoch;
clientPointer = source.clientPointer;
}
boolean samePointer(Attrib other) {
return size == other.size && type == other.type && normalized == other.normalized && integer == other.integer
&& stride == other.stride && offset == other.offset && vboId == other.vboId && divisor == other.divisor
&& clientPointer == other.clientPointer;
}
void clearPointer() {
size = 0;
type = 0;
normalized = false;
integer = false;
stride = 0;
offset = 0;
vboId = 0;
divisor = 0;
clientPointer = null;
genericPointer = false;
}
public int effectiveStride() {
@@ -143,6 +143,7 @@ public class GLSMRedirector {
private static final String OpenGlHelper = "net/minecraft/client/renderer/OpenGlHelper";
private static final String EXTBlendFunc = "org/lwjgl/opengl/EXTBlendFuncSeparate";
private static final String EXTSecondaryColor = "org/lwjgl/opengl/EXTSecondaryColor";
private static final String EXTFogCoord = "org/lwjgl/opengl/EXTFogCoord";
private static final String ARBMultiTexture = "org/lwjgl/opengl/ARBMultitexture";
private static final String ARBShaderObjects = "org/lwjgl/opengl/ARBShaderObjects";
private static final String ARBInstancedArrays = "org/lwjgl/opengl/ARBInstancedArrays";
@@ -378,6 +379,8 @@ public class GLSMRedirector {
.add("glEnableClientState")
.add("glDisableClientState")
.add("glInterleavedArrays")
.add("glEdgeFlagPointer")
.add("glGetPointer")
.add("glPushClientAttrib")
.add("glPopClientAttrib")
.add("glViewport")
@@ -408,11 +411,16 @@ public class GLSMRedirector {
.add("glMultiDrawArrays")
.add("glPointParameterf")
.add("glPointParameteri")
.add("glPointParameter")
.add("glPointParameterfv", "glPointParameter")
.add("glPointParameteriv", "glPointParameter")
// Removed in core profile - emulated by the FFP shaders, calling through would abort the JVM
.add("glSecondaryColor3f")
.add("glSecondaryColor3d")
.add("glSecondaryColor3b")
.add("glSecondaryColor3ub");
.add("glSecondaryColor3ub")
.add("glSecondaryColorPointer")
.add("glFogCoordPointer");
final var gl15 = RedirectMap.newMap()
.add("glGenBuffers")
.add("glBindBuffer")
@@ -604,7 +612,9 @@ public class GLSMRedirector {
.add("glSecondaryColor3bEXT", "glSecondaryColor3b")
.add("glSecondaryColor3fEXT", "glSecondaryColor3f")
.add("glSecondaryColor3dEXT", "glSecondaryColor3d")
.add("glSecondaryColor3ubEXT", "glSecondaryColor3ub"));
.add("glSecondaryColor3ubEXT", "glSecondaryColor3ub")
.add("glSecondaryColorPointerEXT", "glSecondaryColorPointer"));
methodRedirects.put(EXTFogCoord, RedirectMap.newMap().add("glFogCoordPointerEXT", "glFogCoordPointer"));
// ARB
methodRedirects.put(ARBMultiTexture, RedirectMap.newMap()
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.states;
import lombok.Getter;
import lombok.Setter;
import org.lwjgl.opengl.GL20;
/**
* Tracks GL point state: size.
@@ -10,10 +11,24 @@ import lombok.Setter;
@Getter @Setter
public class PointState implements ISettableState<PointState> {
private float size = 1.0f;
private float minSize = 0.0f;
private float maxSize = Float.NaN;
private float fadeThreshold = 1.0f;
private float attenuationConstant = 1.0f;
private float attenuationLinear = 0.0f;
private float attenuationQuadratic = 0.0f;
private int spriteCoordOrigin = GL20.GL_UPPER_LEFT;
@Override
public PointState set(PointState state) {
this.size = state.size;
this.minSize = state.minSize;
this.maxSize = state.maxSize;
this.fadeThreshold = state.fadeThreshold;
this.attenuationConstant = state.attenuationConstant;
this.attenuationLinear = state.attenuationLinear;
this.attenuationQuadratic = state.attenuationQuadratic;
this.spriteCoordOrigin = state.spriteCoordOrigin;
return this;
}
@@ -21,7 +36,14 @@ public class PointState implements ISettableState<PointState> {
public boolean sameAs(Object state) {
if (this == state) return true;
if (!(state instanceof PointState pointState)) return false;
return Float.compare(size, pointState.size) == 0;
return Float.compare(size, pointState.size) == 0
&& Float.compare(minSize, pointState.minSize) == 0
&& Float.compare(maxSize, pointState.maxSize) == 0
&& Float.compare(fadeThreshold, pointState.fadeThreshold) == 0
&& Float.compare(attenuationConstant, pointState.attenuationConstant) == 0
&& Float.compare(attenuationLinear, pointState.attenuationLinear) == 0
&& Float.compare(attenuationQuadratic, pointState.attenuationQuadratic) == 0
&& spriteCoordOrigin == pointState.spriteCoordOrigin;
}
@Override
@@ -9,6 +9,7 @@ import lombok.Getter;
import org.joml.Matrix4fStack;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import java.util.ArrayList;
import java.util.List;
@@ -19,6 +20,7 @@ public class TextureUnitArray {
private final TextureUnitBooleanStateStack[] states;
private final TextureUnitBooleanStateStack[] texture1DStates;
private final TextureUnitBooleanStateStack[] texture3DStates;
private final TextureUnitBooleanStateStack[] textureCubeMapStates;
private final TextureUnitBooleanStateStack[] texGenSStates;
private final TextureUnitBooleanStateStack[] texGenTStates;
private final TextureUnitBooleanStateStack[] texGenRStates;
@@ -36,6 +38,7 @@ public class TextureUnitArray {
states = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texture1DStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texture3DStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
textureCubeMapStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texGenSStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texGenTStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texGenRStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
@@ -49,6 +52,7 @@ public class TextureUnitArray {
states[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_2D, i));
texture1DStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_1D, i));
texture3DStates[i] = track(new TextureUnitBooleanStateStack(GL12.GL_TEXTURE_3D, i));
textureCubeMapStates[i] = track(new TextureUnitBooleanStateStack(GL13.GL_TEXTURE_CUBE_MAP, i));
texGenSStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_S, i));
texGenTStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_T, i));
texGenRStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_R, i));
@@ -90,6 +94,10 @@ public class TextureUnitArray {
return texture3DStates[index];
}
public TextureUnitBooleanStateStack getTextureCubeMapStates(int index) {
return textureCubeMapStates[index];
}
public TextureUnitBooleanStateStack getTexGenSStates(int index) {
return texGenSStates[index];
}
@@ -127,6 +135,7 @@ public class TextureUnitArray {
if (!states[i].sameAs(textureUnitArray.states[i])) return false;
if (!texture1DStates[i].sameAs(textureUnitArray.texture1DStates[i])) return false;
if (!texture3DStates[i].sameAs(textureUnitArray.texture3DStates[i])) return false;
if (!textureCubeMapStates[i].sameAs(textureUnitArray.textureCubeMapStates[i])) return false;
if (!texGenSStates[i].sameAs(textureUnitArray.texGenSStates[i])) return false;
if (!texGenTStates[i].sameAs(textureUnitArray.texGenTStates[i])) return false;
if (!texGenRStates[i].sameAs(textureUnitArray.texGenRStates[i])) return false;
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.glsm;
import org.junit.jupiter.api.Test;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import java.nio.FloatBuffer;
@@ -14,29 +13,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
@GLCoreTest
class ArrayUniformUploadGLTest {
private static int compile(int type, String src) {
final int s = GL20.glCreateShader(type);
GL20.glShaderSource(s, src);
GL20.glCompileShader(s);
assertEquals(GL11.GL_TRUE, GL20.glGetShaderi(s, GL20.GL_COMPILE_STATUS), () -> "shader compile failed: " + GL20.glGetShaderInfoLog(s, 4096));
return s;
}
private static int link(String vs, String fs) {
final int v = compile(GL20.GL_VERTEX_SHADER, vs);
final int f = compile(GL20.GL_FRAGMENT_SHADER, fs);
final int p = GL20.glCreateProgram();
GL20.glAttachShader(p, v);
GL20.glAttachShader(p, f);
GL20.glLinkProgram(p);
assertEquals(GL11.GL_TRUE, GL20.glGetProgrami(p, GL20.GL_LINK_STATUS), () -> "link failed: " + GL20.glGetProgramInfoLog(p, 4096));
GL20.glDeleteShader(v);
GL20.glDeleteShader(f);
return p;
}
private static final String VS = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }\n";
@Test
void glUniform2FloatBufferUploadsWholeArray() {
// Runtime index keeps the whole array active so element locations stay queryable.
@@ -45,7 +21,7 @@ class ArrayUniformUploadGLTest {
+ "uniform int idx;\n"
+ "out vec4 o;\n"
+ "void main(){ o = vec4(cosmicuvs[idx], cosmicuvs[(idx+1)%10]); }\n";
final int p = link(VS, fs);
final int p = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, fs);
try {
GL20.glUseProgram(p);
@@ -66,7 +42,7 @@ class ArrayUniformUploadGLTest {
}
} finally {
GL20.glUseProgram(0);
GL20.glDeleteProgram(p);
GLStateManager.glDeleteProgram(p);
}
}
@@ -77,7 +53,7 @@ class ArrayUniformUploadGLTest {
+ "uniform int idx;\n"
+ "out vec4 o;\n"
+ "void main(){ o = vec4(float(vals[idx])); }\n";
final int p = link(VS, fs);
final int p = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, fs);
try {
GL20.glUseProgram(p);
@@ -96,7 +72,7 @@ class ArrayUniformUploadGLTest {
}
} finally {
GL20.glUseProgram(0);
GL20.glDeleteProgram(p);
GLStateManager.glDeleteProgram(p);
}
}
}
@@ -5,30 +5,24 @@ import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
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;
import static org.junit.jupiter.api.Assertions.assertTrue;
class CaptureGateTest {
@Test
void gateOffWhenNothingAttached() {
assertFalse(CaptureGate.gateValue(false, false, false));
}
@Test
void gateOnWhenForced() {
assertTrue(CaptureGate.gateValue(true, false, false));
}
@Test
void gateOnWhenToolAttached() {
assertTrue(CaptureGate.gateValue(false, true, false));
}
@Test
void gateOnWhenTracyConnected() {
assertTrue(CaptureGate.gateValue(false, false, true));
@ParameterizedTest(name = "{0}")
@CsvSource({
"nothing attached, false, false, false, false",
"forced, true, false, false, true",
"tool attached, false, true, false, true",
"tracy connected, false, false, true, true"
})
void gateIsOnWhenAnySourceIs(String name, boolean forced, boolean toolAttached, boolean tracyConnected, boolean expected) {
assertEquals(expected, CaptureGate.gateValue(forced, toolAttached, tracyConnected));
}
@Test
@@ -5,9 +5,12 @@ import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
import org.junit.jupiter.api.Test;
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;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -17,66 +20,104 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class CompatShaderTransformerTest {
@Test
void testModelViewMatrixReplacement() {
String src = """
#version 120
void main() {
vec4 pos = gl_ModelViewMatrix * gl_Vertex;
gl_Position = gl_ProjectionMatrix * pos;
}
""";
String result = CompatShaderTransformer.transform(src, false);
assertTrue(result.contains("angelica_ModelViewMatrix"));
assertTrue(result.contains("angelica_ProjectionMatrix"));
assertFalse(result.contains("gl_ModelViewMatrix"));
assertFalse(result.contains("gl_ProjectionMatrix"));
assertTrue(result.contains("#version 330 core"));
private static String[] strings(String... values) {
return values;
}
@Test
void testNormalMatrixReplacement() {
String src = """
#version 120
void main() {
vec3 n = gl_NormalMatrix * gl_Normal;
}
""";
String result = CompatShaderTransformer.transform(src, false);
assertTrue(result.contains("uniform mat3 angelica_NormalMatrix"));
assertFalse(result.contains("gl_NormalMatrix"));
private static void assertRewrite(boolean fragment, String src, String[] present, String[] absent) {
final String result = CompatShaderTransformer.transform(src, fragment);
for (final String s : present) {
assertTrue(result.contains(s), () -> "expected '" + s + "' in\n\n" + result);
}
for (final String s : absent) {
assertFalse(result.contains(s), () -> "'" + s + "' must not remain in\n\n" + result);
}
}
@Test
void testModelViewProjectionMatrixExpression() {
String src = """
#version 120
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""";
String result = CompatShaderTransformer.transform(src, false);
assertFalse(result.contains("gl_ModelViewProjectionMatrix"));
assertTrue(result.contains("angelica_ProjectionMatrix"));
static Stream<Arguments> matrixBuiltins() {
return Stream.of(
Arguments.of("modelview and projection matrices", false, """
#version 120
void main() {
vec4 pos = gl_ModelViewMatrix * gl_Vertex;
gl_Position = gl_ProjectionMatrix * pos;
}
""", strings("angelica_ModelViewMatrix", "angelica_ProjectionMatrix", "#version 330 core"), strings("gl_ModelViewMatrix", "gl_ProjectionMatrix")),
Arguments.of("normal matrix", false, """
#version 120
void main() {
vec3 n = gl_NormalMatrix * gl_Normal;
}
""", strings("uniform mat3 angelica_NormalMatrix"), strings("gl_NormalMatrix")),
Arguments.of("modelview-projection matrix", false, """
#version 120
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""", strings("angelica_ProjectionMatrix"), strings("gl_ModelViewProjectionMatrix")),
Arguments.of("inverse matrices", false, """
#version 120
void main() {
vec4 pos = gl_ModelViewMatrixInverse * vec4(0.0, 0.0, 0.0, 1.0);
vec4 proj = gl_ProjectionMatrixInverse * vec4(0.0, 0.0, -1.0, 1.0);
}
""", strings(), strings("gl_ModelViewMatrixInverse", "gl_ProjectionMatrixInverse")),
Arguments.of("texture matrix", false, """
#version 120
void main() {
vec4 lmCoord = gl_TextureMatrix[1] * gl_MultiTexCoord1;
}
""", strings(), strings("gl_TextureMatrix")),
Arguments.of("several builtins in one fragment shader", true, """
#version 120
uniform sampler2D tex;
void main() {
vec3 n = gl_NormalMatrix * gl_Normal;
vec4 pos = gl_ModelViewMatrix * gl_Vertex;
gl_Position = gl_ProjectionMatrix * pos;
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")));
}
@Test
void testTextureFunctionRenames() {
String src = """
#version 120
uniform sampler2D tex;
void main() {
vec4 color = texture2D(tex, vec2(0.0));
}
""";
@ParameterizedTest(name = "{0}")
@MethodSource("matrixBuiltins")
void matrixBuiltinsAreRewritten(String name, boolean fragment, String src, String[] present, String[] absent) {
assertRewrite(fragment, src, present, absent);
}
String result = CompatShaderTransformer.transform(src, false);
// texture2D -> texture
assertFalse(result.contains("texture2D"));
assertTrue(result.contains("texture"));
static Stream<Arguments> textureFunctions() {
return Stream.of(
Arguments.of("texture2D", false, """
#version 120
uniform sampler2D tex;
void main() {
vec4 color = texture2D(tex, vec2(0.0));
}
""", strings("texture"), strings("texture2D")),
Arguments.of("shadow2D", true, """
#version 120
uniform sampler2DShadow shadowMap;
void main() {
float s = shadow2D(shadowMap, vec3(0.0)).r;
gl_FragColor = vec4(s);
}
""", strings("texture"), strings("shadow2D(")),
Arguments.of("texelFetch3D", true, """
#version 120
uniform sampler3D vol;
void main() {
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
@@ -95,32 +136,26 @@ class CompatShaderTransformerTest {
assertTrue(result.contains("layout"));
}
@Test
void testVersionUpgradeTo330() {
String src = """
#version 110
void main() {
gl_FragColor = vec4(1.0);
}
""";
String result = CompatShaderTransformer.transform(src, true);
assertTrue(result.contains("#version 330 core"));
assertFalse(result.contains("#version 110"));
static Stream<Arguments> versionDirectives() {
return Stream.of(
Arguments.of("110 becomes 330 core", true, """
#version 110
void main() {
gl_FragColor = vec4(1.0);
}
""", strings("#version 330 core"), strings("#version 110")),
Arguments.of("compatibility profile is stripped", false, """
#version 330 compatibility
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""", strings("#version 330 core"), strings("compatibility")));
}
@Test
void testCompatibilityProfileStripped() {
String src = """
#version 330 compatibility
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""";
String result = CompatShaderTransformer.transform(src, false);
assertTrue(result.contains("#version 330 core"));
assertFalse(result.contains("compatibility"));
@ParameterizedTest(name = "{0}")
@MethodSource("versionDirectives")
void versionIsUpgradedToCore(String name, boolean fragment, String src, String[] present, String[] absent) {
assertRewrite(fragment, src, present, absent);
}
@Test
@@ -139,60 +174,6 @@ class CompatShaderTransformerTest {
assertEquals(src, result);
}
@Test
void testCombinedBuiltinsInSingleShader() {
String src = """
#version 120
uniform sampler2D tex;
void main() {
vec3 n = gl_NormalMatrix * gl_Normal;
vec4 pos = gl_ModelViewMatrix * gl_Vertex;
gl_Position = gl_ProjectionMatrix * pos;
vec4 color = texture2D(tex, vec2(0.0));
gl_FragColor = color;
}
""";
String result = CompatShaderTransformer.transform(src, true);
assertTrue(result.contains("angelica_ModelViewMatrix"));
assertTrue(result.contains("angelica_ProjectionMatrix"));
assertTrue(result.contains("angelica_NormalMatrix"));
assertFalse(result.contains("gl_ModelViewMatrix"));
assertFalse(result.contains("gl_ProjectionMatrix"));
assertFalse(result.contains("gl_NormalMatrix"));
assertFalse(result.contains("texture2D"));
assertFalse(result.contains("gl_FragColor"));
assertTrue(result.contains("#version 330 core"));
}
@Test
void testInverseMatrixReplacement() {
String src = """
#version 120
void main() {
vec4 pos = gl_ModelViewMatrixInverse * vec4(0.0, 0.0, 0.0, 1.0);
vec4 proj = gl_ProjectionMatrixInverse * vec4(0.0, 0.0, -1.0, 1.0);
}
""";
String result = CompatShaderTransformer.transform(src, false);
assertFalse(result.contains("gl_ModelViewMatrixInverse"));
assertFalse(result.contains("gl_ProjectionMatrixInverse"));
}
@Test
void testTextureMatrixReplacement() {
String src = """
#version 120
void main() {
vec4 lmCoord = gl_TextureMatrix[1] * gl_MultiTexCoord1;
}
""";
String result = CompatShaderTransformer.transform(src, false);
assertFalse(result.contains("gl_TextureMatrix"));
}
@Test
void testFogStructAccess() {
String src = """
@@ -211,34 +192,27 @@ class CompatShaderTransformerTest {
assertTrue(result.contains("uniform vec4 angelica_FogColor"), "Fog color uniform should be injected");
}
@Test
void testFogFragCoordVertex() {
String src = """
#version 120
void main() {
gl_FogFragCoord = length(gl_ModelViewMatrix * gl_Vertex);
}
""";
String result = CompatShaderTransformer.transform(src, false);
assertTrue(result.contains("out float angelica_FogFragCoord"), "Vertex shader should get 'out' declaration");
assertTrue(result.contains("angelica_FogFragCoord"), "gl_FogFragCoord should be renamed");
assertFalse(result.contains("gl_FogFragCoord"), "gl_FogFragCoord should not remain");
static Stream<Arguments> fogFragCoord() {
return Stream.of(
Arguments.of("vertex shader declares an out", false, """
#version 120
void main() {
gl_FogFragCoord = length(gl_ModelViewMatrix * gl_Vertex);
}
""", strings("out float angelica_FogFragCoord"), strings("gl_FogFragCoord")),
Arguments.of("fragment shader declares an in", true, """
#version 120
void main() {
float f = gl_FogFragCoord;
gl_FragColor = vec4(f);
}
""", strings("in float angelica_FogFragCoord"), strings("gl_FogFragCoord")));
}
@Test
void testFogFragCoordFragment() {
String src = """
#version 120
void main() {
float f = gl_FogFragCoord;
gl_FragColor = vec4(f);
}
""";
String result = CompatShaderTransformer.transform(src, true);
assertTrue(result.contains("in float angelica_FogFragCoord"), "Fragment shader should get 'in' declaration");
assertFalse(result.contains("gl_FogFragCoord"), "gl_FogFragCoord should not remain");
@ParameterizedTest(name = "{0}")
@MethodSource("fogFragCoord")
void fogFragCoordBecomesAVarying(String name, boolean fragment, String src, String[] present, String[] absent) {
assertRewrite(fragment, src, present, absent);
}
@Test
@@ -255,23 +229,6 @@ class CompatShaderTransformerTest {
assertFalse(result.contains("gl_FrontColor"), "gl_FrontColor should not remain");
}
@Test
void testShadow2DRename() {
String src = """
#version 120
uniform sampler2DShadow shadowMap;
void main() {
float s = shadow2D(shadowMap, vec3(0.0)).r;
gl_FragColor = vec4(s);
}
""";
String result = CompatShaderTransformer.transform(src, true);
assertFalse(result.contains("shadow2D("), "shadow2D should be renamed");
// shadow2D -> texture with vec4 wrapping
assertTrue(result.contains("texture"), "shadow2D should be renamed to texture");
}
@Test
void testReservedWordTextureAsVariable() {
// Shader declares 'texture' as a sampler variable - this is valid in GLSL 120
@@ -321,155 +278,81 @@ class CompatShaderTransformerTest {
"Alpha test discard should be appended to main()");
}
@Test
void testTexelFetch3DTriggersTransformation() {
String src = """
#version 120
uniform sampler3D vol;
void main() {
vec4 v = texelFetch3D(vol, ivec3(0), 0);
gl_FragColor = v;
}
""";
String result = CompatShaderTransformer.transform(src, true);
// texelFetch3D should be renamed to texelFetch
assertFalse(result.contains("texelFetch3D"), "texelFetch3D should be renamed");
assertTrue(result.contains("texelFetch"), "texelFetch3D should become texelFetch");
static Stream<Arguments> vertexAttributes() {
return Stream.of(
Arguments.of("gl_Vertex at location 0", false, """
#version 120
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""", strings("in vec4 angelica_Vertex", "location = 0"), strings("gl_Vertex")),
Arguments.of("gl_Color at location 1", false, """
#version 120
varying vec4 v_Color;
void main() {
v_Color = gl_Color;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""", strings("in vec4 angelica_Color", "location = 1"), strings("gl_Color")),
Arguments.of("gl_MultiTexCoord0 at location 2", false, """
#version 120
void main() {
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""", strings("in vec4 angelica_MultiTexCoord0", "location = 2"), strings("gl_MultiTexCoord0")),
Arguments.of("gl_MultiTexCoord1 at location 3", false, """
#version 120
void main() {
vec4 lm = gl_TextureMatrix[1] * gl_MultiTexCoord1;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""", strings("angelica_MultiTexCoord1", "location = 3"), strings("gl_MultiTexCoord1")),
Arguments.of("gl_Normal at location 4", false, """
#version 120
void main() {
vec3 n = gl_NormalMatrix * gl_Normal;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""", strings("in vec3 angelica_Normal", "location = 4"), strings("gl_Normal")));
}
@Test
void testVertexAttributeGlVertex() {
String src = """
#version 120
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""";
String result = CompatShaderTransformer.transform(src, false);
assertFalse(result.contains("gl_Vertex"), "gl_Vertex should be replaced");
assertTrue(result.contains("angelica_Vertex"), "should become angelica_Vertex");
assertTrue(result.contains("in vec4 angelica_Vertex"), "should have in declaration");
assertTrue(result.contains("location = 0"), "should use attribute location 0");
@ParameterizedTest(name = "{0}")
@MethodSource("vertexAttributes")
void vertexAttributeBuiltinsGetFixedLocations(String name, boolean fragment, String src, String[] present, String[] absent) {
assertRewrite(fragment, src, present, absent);
}
@Test
void testVertexAttributeGlColorInVertexShader() {
// gl_Color in a vertex shader is the per-vertex color attribute, not the fragment varying
String src = """
#version 120
varying vec4 v_Color;
void main() {
v_Color = gl_Color;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""";
String result = CompatShaderTransformer.transform(src, false);
assertTrue(result.contains("angelica_Color"), "gl_Color -> angelica_Color (vertex attribute)");
assertTrue(result.contains("in vec4 angelica_Color"), "should have in declaration");
assertTrue(result.contains("location = 1"), "should use attribute location 1");
assertFalse(result.contains("gl_Color"), "gl_Color should not remain");
static Stream<Arguments> texCoordVaryings() {
return Stream.of(
Arguments.of("vertex shader declares an out", false, """
#version 120
void main() {
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""", strings("out vec4 angelica_TexCoord0"), strings("gl_TexCoord")),
Arguments.of("fragment shader declares an in", true, """
#version 120
void main() {
vec2 uv = gl_TexCoord[0].xy;
gl_FragColor = vec4(uv, 0.0, 1.0);
}
""", strings("in vec4 angelica_TexCoord0"), strings("gl_TexCoord")),
Arguments.of("one varying per index", false, """
#version 120
void main() {
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = gl_MultiTexCoord1;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""", strings("out vec4 angelica_TexCoord0", "out vec4 angelica_TexCoord1"), strings("gl_TexCoord")));
}
@Test
void testVertexAttributeMultiTexCoord0() {
String src = """
#version 120
void main() {
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""";
String result = CompatShaderTransformer.transform(src, false);
assertFalse(result.contains("gl_MultiTexCoord0"), "gl_MultiTexCoord0 should be replaced");
assertTrue(result.contains("angelica_MultiTexCoord0"), "should become angelica_MultiTexCoord0");
assertTrue(result.contains("in vec4 angelica_MultiTexCoord0"), "should have in declaration");
assertTrue(result.contains("location = 2"), "should use attribute location 2");
}
@Test
void testVertexAttributeMultiTexCoord1() {
String src = """
#version 120
void main() {
vec4 lm = gl_TextureMatrix[1] * gl_MultiTexCoord1;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""";
String result = CompatShaderTransformer.transform(src, false);
assertFalse(result.contains("gl_MultiTexCoord1"), "gl_MultiTexCoord1 should be replaced");
assertTrue(result.contains("angelica_MultiTexCoord1"), "should become angelica_MultiTexCoord1");
assertTrue(result.contains("location = 3"), "should use attribute location 3");
}
@Test
void testVertexAttributeGlNormal() {
String src = """
#version 120
void main() {
vec3 n = gl_NormalMatrix * gl_Normal;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""";
String result = CompatShaderTransformer.transform(src, false);
assertFalse(result.contains("gl_Normal"), "gl_Normal should be replaced");
assertTrue(result.contains("angelica_Normal"), "should become angelica_Normal");
assertTrue(result.contains("in vec3 angelica_Normal"), "should have in vec3 declaration");
assertTrue(result.contains("location = 4"), "should use attribute location 4");
}
@Test
void testTexCoordVaryingVertexShader() {
String src = """
#version 120
void main() {
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""";
String result = CompatShaderTransformer.transform(src, false);
assertFalse(result.contains("gl_TexCoord"), "gl_TexCoord should be replaced");
assertTrue(result.contains("angelica_TexCoord0"), "should become angelica_TexCoord0");
assertTrue(result.contains("out vec4 angelica_TexCoord0"), "vertex shader should have 'out' declaration");
}
@Test
void testTexCoordVaryingFragmentShader() {
String src = """
#version 120
void main() {
vec2 uv = gl_TexCoord[0].xy;
gl_FragColor = vec4(uv, 0.0, 1.0);
}
""";
String result = CompatShaderTransformer.transform(src, true);
assertFalse(result.contains("gl_TexCoord"), "gl_TexCoord should be replaced");
assertTrue(result.contains("angelica_TexCoord0"), "should become angelica_TexCoord0");
assertTrue(result.contains("in vec4 angelica_TexCoord0"), "fragment shader should have 'in' declaration");
}
@Test
void testTexCoordMultipleIndices() {
String src = """
#version 120
void main() {
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = gl_MultiTexCoord1;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
""";
String result = CompatShaderTransformer.transform(src, false);
assertTrue(result.contains("out vec4 angelica_TexCoord0"), "should have TexCoord0 out");
assertTrue(result.contains("out vec4 angelica_TexCoord1"), "should have TexCoord1 out");
assertFalse(result.contains("gl_TexCoord"), "no gl_TexCoord should remain");
@ParameterizedTest(name = "{0}")
@MethodSource("texCoordVaryings")
void texCoordBecomesPerIndexVaryings(String name, boolean fragment, String src, String[] present, String[] absent) {
assertRewrite(fragment, src, present, absent);
}
@Test
@@ -533,7 +416,6 @@ class CompatShaderTransformerTest {
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");
assertTrue(result.contains("angelica_SceneColor"), "should reference angelica_SceneColor");
}
@Test
@@ -9,6 +9,7 @@ import org.lwjgl.opengl.GL11;
import java.nio.FloatBuffer;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.assertMatrixEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -64,12 +65,6 @@ class DisplayListCompilationParityTest {
return buf;
}
private static void assertMatrixEquals(FloatBuffer expected, FloatBuffer actual, String message) {
for (int i = 0; i < 16; i++) {
assertEquals(expected.get(i), actual.get(i), 0.001f, message + " (element " + i + ")");
}
}
@BeforeEach
void setup() {
if (DisplayListManager.isRecording()) {
@@ -3,11 +3,9 @@ package com.gtnewhorizons.angelica.glsm;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import java.nio.FloatBuffer;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.matrix;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -48,12 +46,6 @@ class DisplayListCompilationTest {
GLStateManager.glLoadIdentity();
}
private static FloatBuffer matrix(int pname) {
final FloatBuffer buf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(pname, buf);
return buf;
}
@Test
void glsm_compileAndExecute_matrixModeWithTransforms() {
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
@@ -102,69 +94,4 @@ class DisplayListCompilationTest {
assertFalse(DisplayListManager.isRecording(), "frame-begin backstop must clear a leaked open list");
}
@Test
void rawGL_compile_immediateModeWithTransforms() {
GL11.glLoadIdentity();
GL11.glTranslatef(100.0f, 0.0f, 0.0f);
testList = GL11.glGenLists(1);
GL11.glNewList(testList, GL11.GL_COMPILE);
GL11.glPushMatrix();
GL11.glTranslatef(50.0f, 25.0f, 0.0f);
GL11.glScalef(0.5f, 0.5f, 1.0f);
GL11.glBegin(GL11.GL_QUADS);
GL11.glVertex3f(0.0f, 0.0f, 0.0f);
GL11.glVertex3f(16.0f, 0.0f, 0.0f);
GL11.glVertex3f(16.0f, 16.0f, 0.0f);
GL11.glVertex3f(0.0f, 16.0f, 0.0f);
GL11.glEnd();
GL11.glPopMatrix();
GL11.glEndList();
final FloatBuffer afterCompile = BufferUtils.createFloatBuffer(16);
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, afterCompile);
assertEquals(100.0f, afterCompile.get(12), 0.001f, "After GL_COMPILE: matrix unchanged");
GL11.glCallList(testList);
final FloatBuffer afterPlayback = BufferUtils.createFloatBuffer(16);
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, afterPlayback);
assertEquals(100.0f, afterPlayback.get(12), 0.001f, "After playback: base position preserved");
assertEquals(1.0f, afterPlayback.get(0), 0.001f, "After playback: no scale (popped)");
GL11.glLoadIdentity();
}
@Test
void rawGL_compileAndExecute_immediateModeExecutesImmediately() {
GL11.glLoadIdentity();
testList = GL11.glGenLists(1);
GL11.glNewList(testList, GL11.GL_COMPILE_AND_EXECUTE);
GL11.glScalef(0.5f, 0.5f, 0.5f);
final FloatBuffer during = BufferUtils.createFloatBuffer(16);
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, during);
assertEquals(0.5f, during.get(0), 0.001f, "During COMPILE_AND_EXECUTE: scale applied");
GL11.glBegin(GL11.GL_QUADS);
GL11.glVertex3f(0.0f, 0.0f, 0.0f);
GL11.glVertex3f(1.0f, 0.0f, 0.0f);
GL11.glVertex3f(1.0f, 1.0f, 0.0f);
GL11.glVertex3f(0.0f, 1.0f, 0.0f);
GL11.glEnd();
GL11.glEndList();
final FloatBuffer after = BufferUtils.createFloatBuffer(16);
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, after);
assertEquals(0.5f, after.get(0), 0.001f, "After COMPILE_AND_EXECUTE: scale remains");
GL11.glLoadIdentity();
}
}
@@ -1,16 +1,15 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.glsm.testutil.TestThreads;
import org.junit.jupiter.api.Test;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -39,25 +38,14 @@ class DisplayListIdAllocatorTest {
}
@Test
void zeroNeverReturned() {
DisplayListIDAllocator alloc = new DisplayListIDAllocator(1);
// Allocate enough IDs to fill the first word (32 IDs). None should be 0.
for (int i = 0; i < 31; i++) {
int id = alloc.allocRange(1);
assertNotEquals(0, id, "ID 0 must never be returned");
assertTrue(id > 0, "All returned IDs must be positive");
void singleAllocsAreSequentialFromOne() {
// Sequential from 1 across several words: 0 is never handed out and no id repeats
final DisplayListIDAllocator alloc = new DisplayListIDAllocator(1);
for (int expected = 1; expected <= 500; expected++) {
assertEquals(expected, alloc.allocRange(1));
}
}
@Test
void singleAllocReturnsSequential() {
DisplayListIDAllocator alloc = new DisplayListIDAllocator();
// First alloc should be 1 (0 is reserved)
assertEquals(1, alloc.allocRange(1));
assertEquals(2, alloc.allocRange(1));
assertEquals(3, alloc.allocRange(1));
}
@Test
void isAllocatedTracksState() {
DisplayListIDAllocator alloc = new DisplayListIDAllocator();
@@ -159,9 +147,9 @@ class DisplayListIdAllocatorTest {
@Test
void freeZeroIsNoOp() {
DisplayListIDAllocator alloc = new DisplayListIDAllocator();
// Should not throw
alloc.free(0);
alloc.free(-1);
assertEquals(1, alloc.allocRange(1), "freeing the reserved id 0 must not make it allocatable");
}
@Test
@@ -205,17 +193,6 @@ class DisplayListIdAllocatorTest {
}
}
@Test
void noDoubleAllocation() {
DisplayListIDAllocator alloc = new DisplayListIDAllocator();
// Allocate many IDs and verify uniqueness
Set<Integer> seen = new HashSet<>();
for (int i = 0; i < 500; i++) {
int id = alloc.allocRange(1);
assertTrue(seen.add(id), "Duplicate ID returned: " + id);
}
}
@Test
void rangeAfterSingleAllocs() {
DisplayListIDAllocator alloc = new DisplayListIDAllocator();
@@ -54,9 +54,7 @@ class DisplayListMatrixRestorationTest extends DisplayListTestFixture {
}
private static float translation(int target) {
FloatBuffer b = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(target, b);
return b.get(12);
return GlTestSupport.matrix(target).get(12);
}
@Test
@@ -1,10 +1,17 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
import net.minecraft.client.renderer.Tessellator;
import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import java.nio.FloatBuffer;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
@@ -62,4 +69,58 @@ public final class DisplayListTestHelper {
assertNotNull(compiled, "Display list should be compiled");
return compiled;
}
/** Calls {@code list} on an identity modelview and returns the resulting matrix. The caller's matrix is restored. */
public static FloatBuffer modelviewAfterCall(int list) {
GLStateManager.glPushMatrix();
try {
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(list);
return GlTestSupport.modelview();
} finally {
GLStateManager.glPopMatrix();
}
}
public static void assertMatrixEquals(Matrix4f expected, FloatBuffer actual, String message) {
final FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16);
expected.get(expectedBuf);
for (int i = 0; i < 16; i++) {
assertEquals(expectedBuf.get(i), actual.get(i), 0.0001f, message + " (element " + i + ")");
}
}
public static void assertModelviewAfterCall(int list, Matrix4f expected, String message) {
assertMatrixEquals(expected, modelviewAfterCall(list), message);
}
/** Immediate-mode quad from the origin to (size, size) at z = 0. */
public static void immediateQuad(float size) {
GLStateManager.glBegin(GL11.GL_QUADS);
GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f);
GLStateManager.glVertex3f(size, 0.0f, 0.0f);
GLStateManager.glVertex3f(size, size, 0.0f);
GLStateManager.glVertex3f(0.0f, size, 0.0f);
GLStateManager.glEnd();
}
/** Textured quad written straight into the vanilla Tessellator's raw buffer and handed to the draw interceptor. */
public static void rawQuad(float size) {
final Tessellator tess = Tessellator.instance;
final float[][] corners = { { 0.0f, 0.0f }, { size, 0.0f }, { size, size }, { 0.0f, size } };
tess.rawBuffer = new int[4 * 8];
for (int i = 0; i < 4; i++) {
final int base = i * 8;
tess.rawBuffer[base] = Float.floatToRawIntBits(corners[i][0]);
tess.rawBuffer[base + 1] = Float.floatToRawIntBits(corners[i][1]);
tess.rawBuffer[base + 3] = Float.floatToRawIntBits(i == 1 || i == 2 ? 1.0f : 0.0f);
tess.rawBuffer[base + 4] = Float.floatToRawIntBits(i >= 2 ? 1.0f : 0.0f);
}
tess.isDrawing = true;
tess.drawMode = GL11.GL_QUADS;
tess.hasTexture = true;
tess.rawBufferIndex = 4 * 8;
tess.vertexCount = 4;
TessellatorManager.interceptDraw(tess);
}
}
@@ -3,12 +3,16 @@ package com.gtnewhorizons.angelica.glsm;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import java.nio.FloatBuffer;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -97,68 +101,23 @@ class FeedbackManagerTest {
assertEquals(42.0f, buf.get(1), "Second token should be the pass-through value");
}
@Test
void testTriangleFeedback_GL2D() {
final FloatBuffer buf = BufferUtils.createFloatBuffer(128);
FeedbackManager.glFeedbackBuffer(GL11.GL_2D, buf);
FeedbackManager.glRenderMode(GL11.GL_FEEDBACK);
// Identity MV/proj, viewport (0,0,800,600)
GLStateManager.getModelViewMatrix().identity();
GLStateManager.getProjectionMatrix().identity();
GLStateManager.getViewportState().setViewPort(0, 0, 800, 600);
GLStateManager.getViewportState().setDepthRange(0.0, 1.0);
// Upload 3 vertices forming a triangle
final int vao = GL30.glGenVertexArrays();
GLStateManager.glBindVertexArray(vao);
// NDC vertices: (0,0,0), (1,0,0), (0,1,0)
// With identity MVP and viewport 800x600:
// NDC(0,0) → window(400, 300)
// NDC(1,0) → window(800, 300)
// NDC(0,1) → window(400, 600)
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 count = FeedbackManager.glRenderMode(GL11.GL_RENDER);
// GL_2D: POLYGON_TOKEN, 3, x0, y0, x1, y1, x2, y2 = 8 floats
assertEquals(8, count, "Triangle in GL_2D should produce 8 floats");
assertEquals(GL11.GL_POLYGON_TOKEN, buf.get(0), 0.001f);
assertEquals(3.0f, buf.get(1), 0.001f);
// Vertex 0: NDC(0,0) → window(400, 300)
assertEquals(400.0f, buf.get(2), 0.01f);
assertEquals(300.0f, buf.get(3), 0.01f);
// Vertex 1: NDC(1,0) → window(800, 300)
assertEquals(800.0f, buf.get(4), 0.01f);
assertEquals(300.0f, buf.get(5), 0.01f);
// Vertex 2: NDC(0,1) → window(400, 600)
assertEquals(400.0f, buf.get(6), 0.01f);
assertEquals(600.0f, buf.get(7), 0.01f);
// Cleanup
GLStateManager.glDisableVertexAttribArray(0);
GLStateManager.glBindVertexArray(0);
GL15.glDeleteBuffers(vbo);
GLStateManager.glDeleteVertexArrays(vao);
// Identity MVP on an 800x600 viewport: NDC (0,0) -> window (400,300), (1,0) -> (800,300), (0,1) -> (400,600)
static Stream<Arguments> primitives() {
return Stream.of(
Arguments.of("triangle", GL11.GL_TRIANGLES,
new float[] { 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f, 0f },
new float[] { GL11.GL_POLYGON_TOKEN, 3f, 400f, 300f, 800f, 300f, 400f, 600f }),
Arguments.of("polygon", GL11.GL_POLYGON,
new float[] { 0f, 0f, 0f, 1f, 0f, 0f, 1f, 1f, 0f, 0f, 1f, 0f },
new float[] { GL11.GL_POLYGON_TOKEN, 4f, 400f, 300f, 800f, 300f, 800f, 600f, 400f, 600f }),
Arguments.of("quad strip", GL11.GL_QUAD_STRIP,
new float[] { 0f, 0f, 0f, 0f, 1f, 0f, 1f, 0f, 0f, 1f, 1f, 0f },
new float[] { GL11.GL_POLYGON_TOKEN, 4f, 400f, 300f, 400f, 600f, 800f, 600f, 800f, 300f }));
}
@Test
void testPolygonFeedback_GL2D() {
@ParameterizedTest(name = "{0}")
@MethodSource("primitives")
void testPrimitiveFeedback_GL2D(String name, int mode, float[] verts, float[] expected) {
final FloatBuffer buf = BufferUtils.createFloatBuffer(128);
FeedbackManager.glFeedbackBuffer(GL11.GL_2D, buf);
FeedbackManager.glRenderMode(GL11.GL_FEEDBACK);
@@ -171,12 +130,6 @@ class FeedbackManagerTest {
final int vao = GL30.glGenVertexArrays();
GLStateManager.glBindVertexArray(vao);
final float[] verts = {
0f, 0f, 0f,
1f, 0f, 0f,
1f, 1f, 0f,
0f, 1f, 0f
};
final FloatBuffer vertBuf = BufferUtils.createFloatBuffer(verts.length);
vertBuf.put(verts).flip();
@@ -186,72 +139,15 @@ class FeedbackManagerTest {
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0);
GLStateManager.glEnableVertexAttribArray(0);
FeedbackManager.processDrawArrays(GL11.GL_POLYGON, 0, 4);
FeedbackManager.processDrawArrays(mode, 0, verts.length / 3);
final int count = FeedbackManager.glRenderMode(GL11.GL_RENDER);
assertEquals(10, count, "Polygon in GL_2D should produce 10 floats");
assertEquals(GL11.GL_POLYGON_TOKEN, buf.get(0), 0.001f);
assertEquals(4.0f, buf.get(1), 0.001f);
assertEquals(400.0f, buf.get(2), 0.01f);
assertEquals(300.0f, buf.get(3), 0.01f);
assertEquals(800.0f, buf.get(4), 0.01f);
assertEquals(300.0f, buf.get(5), 0.01f);
assertEquals(800.0f, buf.get(6), 0.01f);
assertEquals(600.0f, buf.get(7), 0.01f);
assertEquals(400.0f, buf.get(8), 0.01f);
assertEquals(600.0f, buf.get(9), 0.01f);
GLStateManager.glDisableVertexAttribArray(0);
GLStateManager.glBindVertexArray(0);
GL15.glDeleteBuffers(vbo);
GLStateManager.glDeleteVertexArrays(vao);
}
@Test
void testQuadStripFeedback_GL2D() {
final FloatBuffer buf = BufferUtils.createFloatBuffer(128);
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 float[] verts = {
0f, 0f, 0f,
0f, 1f, 0f,
1f, 0f, 0f,
1f, 1f, 0f
};
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(GL11.GL_QUAD_STRIP, 0, 4);
final int count = FeedbackManager.glRenderMode(GL11.GL_RENDER);
assertEquals(10, count, "Quad strip in GL_2D should produce 10 floats");
assertEquals(GL11.GL_POLYGON_TOKEN, buf.get(0), 0.001f);
assertEquals(4.0f, buf.get(1), 0.001f);
assertEquals(400.0f, buf.get(2), 0.01f);
assertEquals(300.0f, buf.get(3), 0.01f);
assertEquals(400.0f, buf.get(4), 0.01f);
assertEquals(600.0f, buf.get(5), 0.01f);
assertEquals(800.0f, buf.get(6), 0.01f);
assertEquals(600.0f, buf.get(7), 0.01f);
assertEquals(800.0f, buf.get(8), 0.01f);
assertEquals(300.0f, buf.get(9), 0.01f);
// 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);
@@ -22,7 +22,6 @@ class GLSM_BlendFuncUnknownState_GLTest {
@BeforeEach
void requireBufferBlending() {
assumeTrue(RenderSystem.supportsBufferBlending());
drainGlErrors();
}
@AfterEach
@@ -30,11 +29,6 @@ class GLSM_BlendFuncUnknownState_GLTest {
if (!RenderSystem.supportsBufferBlending()) return;
GLStateManager.tryBlendFuncSeparate(GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ZERO);
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
drainGlErrors();
}
private static void drainGlErrors() {
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
private static void seedVanillaSeparate() {
@@ -59,7 +53,6 @@ class GLSM_BlendFuncUnknownState_GLTest {
assertBufferBlend(buffer, VANILLA_SRC_RGB, VANILLA_DST_RGB, VANILLA_SRC_ALPHA, VANILLA_DST_ALPHA);
}
assertFalse(GLStateManager.getBlendState().isFuncUnknown(), "forward must clear the unknown flag");
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
}
@Test
@@ -3,11 +3,13 @@ package com.gtnewhorizons.angelica.glsm;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import static com.gtnewhorizons.angelica.util.GLSMUtil.resetGLState;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
@@ -51,46 +53,28 @@ class GLSM_BlendUnknownState_UnitTest {
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND), "GL blend should be re-enabled after setUnknownState");
}
@Test
void testDisableBufferBlendInvalidatesCache() {
GLStateManager.enableBlend();
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND));
@ParameterizedTest(name = "global blend enabled={0}")
@ValueSource(booleans = {true, false})
void perBufferBlendToggleInvalidatesCache(boolean enabled) {
setBlend(enabled);
assertEquals(enabled, GL11.glIsEnabled(GL11.GL_BLEND));
// Use RenderSystem which should call setUnknownState internally
RenderSystem.disableBufferBlend(0);
// Flips buffer 0 behind the cache and marks it unknown, so the repeated global call must reach the driver
if (enabled) {
RenderSystem.disableBufferBlend(0);
} else {
RenderSystem.enableBufferBlend(0);
}
// Now re-enable via GLSM — should issue the GL call
GLStateManager.enableBlend();
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND), "Blend should be re-enabled after disableBufferBlend");
setBlend(enabled);
assertEquals(enabled, GL11.glIsEnabled(GL11.GL_BLEND), "global blend state must be re-issued after a per-buffer toggle");
}
@Test
void testEnableBufferBlendInvalidatesCache() {
// Start with blend disabled
GLStateManager.disableBlend();
assertFalse(GL11.glIsEnabled(GL11.GL_BLEND));
// Per-buffer enable bypasses cache
RenderSystem.enableBufferBlend(0);
// Disable via GLSM — should issue the GL call despite cache saying disabled
GLStateManager.disableBlend();
assertFalse(GL11.glIsEnabled(GL11.GL_BLEND), "Blend should be disabled after enableBufferBlend + disableBlend");
}
@Test
void testUnknownStateClearedAfterSetEnabled() {
GLStateManager.enableBlend();
// Mark unknown
GLStateManager.getBlendMode().setUnknownState();
// First setEnabled clears the unknown flag
GLStateManager.enableBlend();
// Second setEnabled should use normal caching (no redundant GL call)
// Verify state is consistent
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND));
assertTrue(GLStateManager.getBlendMode().isEnabled());
private static void setBlend(boolean enabled) {
if (enabled) {
GLStateManager.enableBlend();
} else {
GLStateManager.disableBlend();
}
}
}
@@ -0,0 +1,672 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL33;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.CLIENT_ARRAY_CAPS;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.assertEnableRoundTrip;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.drainClientAttribStack;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.hex;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeFalse;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
/** Conventional client arrays and the client attrib stack, emulated on a core-profile vertex array object. */
@GLCoreTest
public class GLSM_ClientArrays_GLTest {
private int savedVao;
private int vao;
private int vbo;
@BeforeEach
void bindFreshVertexArray() {
savedVao = GLStateManager.getBoundVAO();
vao = GLStateManager.glGenVertexArrays();
vbo = GLStateManager.glGenBuffers();
GLStateManager.glBindVertexArray(vao);
}
@AfterEach
void deleteVertexArray() {
GLStateManager.glBindVertexArray(savedVao);
GLStateManager.glDeleteVertexArrays(vao);
GLStateManager.glDeleteBuffers(vbo);
}
private static boolean attribHasSize3(Usage usage) {
final VAOManager.Attrib attrib = GLStateManager.ctx().vaos.attrib(usage.getAttributeLocation());
return attrib != null && attrib.size == 3;
}
private static boolean positionArrayEnabled() {
final VAOManager.Attrib position = GLStateManager.ctx().vaos.attrib(Usage.POSITION.getAttributeLocation());
return position != null && position.enabled;
}
private static boolean colorArrayEnabled() {
return (GLStateManager.ctx().vaos.getVertexFlags() & VertexFlags.COLOR_BIT) != 0;
}
private static int driverAttrib(int index, int pname) {
final IntBuffer out = BufferUtils.createIntBuffer(16);
GL20.glGetVertexAttrib(index, pname, out);
return out.get(0);
}
private static void assertClientArray(String name, int sizePname, int size, int typePname, int type, int stridePname, int stride, int bindingPname, int binding) {
if (sizePname != 0) assertEquals(size, GLStateManager.glGetInteger(sizePname), name + " size");
if (typePname != 0) assertEquals(type, GLStateManager.glGetInteger(typePname), name + " type");
assertEquals(stride, GLStateManager.glGetInteger(stridePname), name + " stride");
assertEquals(binding, GLStateManager.glGetInteger(bindingPname), name + " buffer binding");
}
private static void assertVertexArray(String name, int size, int type, int stride, int binding) {
assertClientArray(name, GL11.GL_VERTEX_ARRAY_SIZE, size, GL11.GL_VERTEX_ARRAY_TYPE, type, GL11.GL_VERTEX_ARRAY_STRIDE, stride, GL15.GL_VERTEX_ARRAY_BUFFER_BINDING, binding);
}
private static void assertTexCoordArray(String name, int size, int type, int stride, int binding) {
assertClientArray(name, GL11.GL_TEXTURE_COORD_ARRAY_SIZE, size, GL11.GL_TEXTURE_COORD_ARRAY_TYPE, type, GL11.GL_TEXTURE_COORD_ARRAY_STRIDE, stride,
GL15.GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, binding);
}
@Test
void vertexArrayStateRestoredOnlyWithVertexArrayBit() {
final int position = Usage.POSITION.getAttributeLocation();
try {
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
final int flagsBefore = GLStateManager.ctx().vaos.getVertexFlags();
assertFalse(positionArrayEnabled());
assertFalse(colorArrayEnabled());
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
assertTrue(positionArrayEnabled());
assertTrue(colorArrayEnabled());
GLStateManager.glPopClientAttrib();
assertFalse(positionArrayEnabled(), "pop disables an array enabled inside the push");
assertEquals(GL11.GL_FALSE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
assertFalse(colorArrayEnabled());
assertEquals(flagsBefore, GLStateManager.ctx().vaos.getVertexFlags());
assertEquals(0, GLStateManager.getClientActiveTextureUnit());
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glPopClientAttrib();
assertTrue(positionArrayEnabled(), "pop re-enables an array disabled inside the push");
assertEquals(GL11.GL_TRUE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
GLStateManager.glPopClientAttrib();
assertTrue(positionArrayEnabled(), "a pixel-store push must not restore array enables");
assertTrue(colorArrayEnabled());
assertEquals(1, GLStateManager.getClientActiveTextureUnit());
} finally {
drainClientAttribStack();
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY);
}
}
@Test
void clientActiveTextureRoundTrips() {
try {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
assertEquals(GLStateManager.MAX_TEXTURE_COORDS, GLStateManager.glGetInteger(GL20.GL_MAX_TEXTURE_COORDS));
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
assertEquals(GL13.GL_TEXTURE1, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE));
GLStateManager.glClientActiveTexture(0);
assertEquals(GL13.GL_TEXTURE1, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE));
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0 + GLStateManager.MAX_TEXTURE_UNITS);
assertEquals(GL13.GL_TEXTURE1, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE));
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0 + GLStateManager.MAX_TEXTURE_COORDS);
assertEquals(GL13.GL_TEXTURE1, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE), "units past GL_MAX_TEXTURE_COORDS are rejected");
assertEquals(1, GLStateManager.getClientActiveTextureUnit());
GLStateManager.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0L);
final VAOManager.Attrib attrib = GLStateManager.ctx().vaos.attrib(Usage.SECONDARY_UV.getAttributeLocation());
assertNotNull(attrib);
assertEquals(2, attrib.size);
assumeTrue(GLStateManager.MAX_TEXTURE_COORDS > 5);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE5);
assertEquals(GL13.GL_TEXTURE5, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE));
GLStateManager.glTexCoordPointer(3, GL11.GL_FLOAT, 0, 0L);
assertEquals(3, GLStateManager.glGetInteger(GL11.GL_TEXTURE_COORD_ARRAY_SIZE));
assertFalse(attribHasSize3(Usage.PRIMARY_UV));
assertFalse(attribHasSize3(Usage.SECONDARY_UV));
} finally {
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
}
@Test
void conventionalPointerSurvivesGenericOverride() {
final int position = Usage.POSITION.getAttributeLocation();
try {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L);
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glVertexAttribPointer(position, 2, GL11.GL_FLOAT, false, 8, 4L);
assertEquals(3, VAOManager.get(position).size, "disabled generic pointer must not displace an enabled conventional array");
assertEquals(3, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE));
GLStateManager.glEnableVertexAttribArray(position);
assertEquals(2, VAOManager.get(position).size, "enabled generic attribute wins");
assertTrue(VAOManager.get(position).genericPointer);
assertEquals(2, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE));
assertEquals(8, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_STRIDE));
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GLStateManager.glDisableVertexAttribArray(position);
assertEquals(3, VAOManager.get(position).size, "conventional pointer must come back when the generic attribute is disabled");
assertEquals(12, VAOManager.get(position).stride);
assertFalse(VAOManager.get(position).genericPointer);
assertEquals(3, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE));
assertEquals(12, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_STRIDE));
assertEquals(vbo, driverAttrib(position, GL15.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING));
assertEquals(GL11.GL_TRUE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED), "conventional array stays enabled at draw level");
assertTrue(GLStateManager.glIsEnabled(GL11.GL_VERTEX_ARRAY));
assertEquals(0, GLStateManager.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING), "repoint must restore the array buffer binding");
} finally {
GLStateManager.glDisableVertexAttribArray(position);
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
}
@Test
void conventionalPointerSetUnderEnabledGenericAppliesOnceGenericIsDisabled() {
final int position = Usage.POSITION.getAttributeLocation();
try {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLStateManager.glVertexAttribPointer(position, 2, GL11.GL_FLOAT, false, 8, 0L);
GLStateManager.glEnableVertexAttribArray(position);
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L);
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
assertEquals(2, VAOManager.get(position).size);
assertEquals(2, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE));
GLStateManager.glDisableVertexAttribArray(position);
assertEquals(3, VAOManager.get(position).size);
assertEquals(3, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE));
assertEquals(GL11.GL_TRUE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
assertEquals(GL11.GL_FALSE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
assertFalse(positionArrayEnabled());
} finally {
GLStateManager.glDisableVertexAttribArray(position);
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
}
@Test
void drawVertexFlagsDoNotAnswerClientArrayEnables() {
try {
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
VAOManager.setCurrentVertexFlags(VertexFlags.COLOR_BIT | VertexFlags.NORMAL_BIT | VertexFlags.TEXTURE_BIT);
assertFalse(GLStateManager.glIsEnabled(GL11.GL_COLOR_ARRAY));
assertFalse(GLStateManager.glIsEnabled(GL11.GL_NORMAL_ARRAY));
assertFalse(GLStateManager.glIsEnabled(GL11.GL_TEXTURE_COORD_ARRAY));
GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
VAOManager.setCurrentVertexFlags(0);
assertTrue(GLStateManager.glIsEnabled(GL11.GL_COLOR_ARRAY));
} finally {
GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY);
VAOManager.setCurrentVertexFlags(0);
}
}
@Test
void clientArrayPointerQueriesAreAnsweredFromCache() {
final int vaoB = GLStateManager.glGenVertexArrays();
final IntBuffer ints = BufferUtils.createIntBuffer(16);
try {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
assertVertexArray("default vertex", 4, GL11.GL_FLOAT, 0, 0);
assertClientArray("default normal", 0, 0, GL11.GL_NORMAL_ARRAY_TYPE, GL11.GL_FLOAT, GL11.GL_NORMAL_ARRAY_STRIDE, 0, GL15.GL_NORMAL_ARRAY_BUFFER_BINDING, 0);
assertClientArray("default color", GL11.GL_COLOR_ARRAY_SIZE, 4, GL11.GL_COLOR_ARRAY_TYPE, GL11.GL_FLOAT, GL11.GL_COLOR_ARRAY_STRIDE, 0, GL15.GL_COLOR_ARRAY_BUFFER_BINDING, 0);
assertClientArray("default secondary color", GL14.GL_SECONDARY_COLOR_ARRAY_SIZE, 3, GL14.GL_SECONDARY_COLOR_ARRAY_TYPE, GL11.GL_FLOAT,
GL14.GL_SECONDARY_COLOR_ARRAY_STRIDE, 0, GL15.GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING, 0);
assertClientArray("default fog coord", 0, 0, GL14.GL_FOG_COORDINATE_ARRAY_TYPE, GL11.GL_FLOAT, GL14.GL_FOG_COORDINATE_ARRAY_STRIDE, 0,
GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING, 0);
assertClientArray("default index", 0, 0, GL11.GL_INDEX_ARRAY_TYPE, GL11.GL_FLOAT, GL11.GL_INDEX_ARRAY_STRIDE, 0, GL15.GL_INDEX_ARRAY_BUFFER_BINDING, 0);
assertClientArray("default edge flag", 0, 0, 0, 0, GL11.GL_EDGE_FLAG_ARRAY_STRIDE, 0, GL15.GL_EDGE_FLAG_ARRAY_BUFFER_BINDING, 0);
assertTexCoordArray("default texcoord", 4, GL11.GL_FLOAT, 0, 0);
assertNull(GLStateManager.glGetPointer(GL11.GL_VERTEX_ARRAY_POINTER, 4));
GLStateManager.glVertexPointer(3, GL11.GL_SHORT, 20, 8L);
GLStateManager.glNormalPointer(GL11.GL_BYTE, 16, 4L);
GLStateManager.glColorPointer(4, GL11.GL_UNSIGNED_BYTE, 16, 12L);
GLStateManager.glSecondaryColorPointer(3, GL11.GL_FLOAT, 12, 0L);
GLStateManager.glFogCoordPointer(GL11.GL_FLOAT, 4, 0L);
GLStateManager.glEdgeFlagPointer(7, 0L);
GLStateManager.glTexCoordPointer(2, GL11.GL_FLOAT, 8, 0L);
assertVertexArray("vertex", 3, GL11.GL_SHORT, 20, vbo);
assertClientArray("normal", 0, 0, GL11.GL_NORMAL_ARRAY_TYPE, GL11.GL_BYTE, GL11.GL_NORMAL_ARRAY_STRIDE, 16, GL15.GL_NORMAL_ARRAY_BUFFER_BINDING, vbo);
assertClientArray("color", GL11.GL_COLOR_ARRAY_SIZE, 4, GL11.GL_COLOR_ARRAY_TYPE, GL11.GL_UNSIGNED_BYTE, GL11.GL_COLOR_ARRAY_STRIDE, 16,
GL15.GL_COLOR_ARRAY_BUFFER_BINDING, vbo);
assertClientArray("secondary color", GL14.GL_SECONDARY_COLOR_ARRAY_SIZE, 3, GL14.GL_SECONDARY_COLOR_ARRAY_TYPE, GL11.GL_FLOAT,
GL14.GL_SECONDARY_COLOR_ARRAY_STRIDE, 12, GL15.GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING, vbo);
assertClientArray("fog coord", 0, 0, GL14.GL_FOG_COORDINATE_ARRAY_TYPE, GL11.GL_FLOAT, GL14.GL_FOG_COORDINATE_ARRAY_STRIDE, 4,
GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING, vbo);
assertClientArray("edge flag", 0, 0, 0, 0, GL11.GL_EDGE_FLAG_ARRAY_STRIDE, 7, GL15.GL_EDGE_FLAG_ARRAY_BUFFER_BINDING, vbo);
assertTexCoordArray("texcoord unit 0", 2, GL11.GL_FLOAT, 8, vbo);
assertEquals(3.0f, GLStateManager.glGetFloat(GL11.GL_VERTEX_ARRAY_SIZE));
GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_STRIDE, ints);
assertEquals(20, ints.get(0));
assertEquals(8L, MemoryUtilities.memAddress(GLStateManager.glGetPointer(GL11.GL_VERTEX_ARRAY_POINTER, 4)));
assertEquals(12L, MemoryUtilities.memAddress(GLStateManager.glGetPointer(GL11.GL_COLOR_ARRAY_POINTER, 4)));
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0 + GLStateManager.MAX_TEXTURE_COORDS - 1);
assertTexCoordArray("unset texcoord unit", 4, GL11.GL_FLOAT, 0, 0);
GLStateManager.glTexCoordPointer(3, GL11.GL_SHORT, 6, 0L);
assertTexCoordArray("last texcoord unit", 3, GL11.GL_SHORT, 6, vbo);
assertFalse(attribHasSize3(Usage.PRIMARY_UV));
assertFalse(attribHasSize3(Usage.SECONDARY_UV));
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
assertTexCoordArray("texcoord unit 0 again", 2, GL11.GL_FLOAT, 8, vbo);
GLStateManager.glBindVertexArray(vaoB);
assertVertexArray("other vao", 4, GL11.GL_FLOAT, 0, 0);
final FloatBuffer client = BufferUtils.createFloatBuffer(12);
GLStateManager.glVertexPointer(2, 0, client);
assertVertexArray("client pointer", 2, GL11.GL_FLOAT, 0, 0);
assertEquals(MemoryUtilities.memAddress(client), MemoryUtilities.memAddress(GLStateManager.glGetPointer(GL11.GL_VERTEX_ARRAY_POINTER, 4)));
GLStateManager.glBindVertexArray(vao);
assertVertexArray("first vao again", 3, GL11.GL_SHORT, 20, vbo);
} finally {
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GLStateManager.glDeleteVertexArrays(vaoB);
}
}
@Test
void interleavedArraysSetClientStateWithoutAnActiveFfpProgram() {
try {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
assumeFalse(GLStateManager.ctx().ffp.isActive());
GLStateManager.glInterleavedArrays(GL11.GL_C4UB_V3F, 0, 0L);
assertTrue(GLStateManager.glIsEnabled(GL11.GL_VERTEX_ARRAY));
assertTrue(GLStateManager.glIsEnabled(GL11.GL_COLOR_ARRAY));
assertFalse(GLStateManager.glIsEnabled(GL11.GL_NORMAL_ARRAY));
assertVertexArray("interleaved vertex", 3, GL11.GL_FLOAT, 16, vbo);
assertEquals(GL11.GL_UNSIGNED_BYTE, GLStateManager.glGetInteger(GL11.GL_COLOR_ARRAY_TYPE));
} finally {
for (final int cap : CLIENT_ARRAY_CAPS) {
GLStateManager.glDisableClientState(cap);
}
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
}
@Test
void clientVertexArrayBitRestoresPointersEnablesAndBindings() {
final int vaoB = GLStateManager.glGenVertexArrays();
final int vbo2 = GLStateManager.glGenBuffers();
final int ebo = GLStateManager.glGenBuffers();
final int position = Usage.POSITION.getAttributeLocation();
final int color = Usage.COLOR.getAttributeLocation();
final int normal = Usage.NORMAL.getAttributeLocation();
try {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L);
GLStateManager.glColorPointer(4, GL11.GL_UNSIGNED_BYTE, 16, 12L);
GLStateManager.glFogCoordPointer(GL11.GL_FLOAT, 4, 0L);
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo2);
GLStateManager.glVertexPointer(2, GL11.GL_SHORT, 4, 4L);
GLStateManager.glFogCoordPointer(GL11.GL_FLOAT, 8, 0L);
GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY);
GLStateManager.glEnableClientState(GL11.GL_NORMAL_ARRAY);
GLStateManager.glVertexAttribPointer(5, 1, GL11.GL_FLOAT, false, 4, 0L);
GLStateManager.glEnableVertexAttribArray(5);
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
GLStateManager.glBindVertexArray(vaoB);
GLStateManager.glPopClientAttrib();
assertEquals(vao, GLStateManager.getBoundVAO());
assertEquals(vbo, GLStateManager.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING));
assertEquals(ebo, GLStateManager.glGetInteger(GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING));
assertEquals(ebo, GL11.glGetInteger(GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING));
assertEquals(GL13.GL_TEXTURE0, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE));
assertVertexArray("restored vertex", 3, GL11.GL_FLOAT, 12, vbo);
assertEquals(4, GLStateManager.glGetInteger(GL14.GL_FOG_COORDINATE_ARRAY_STRIDE));
assertEquals(vbo, GLStateManager.glGetInteger(GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING));
assertTrue(GLStateManager.glIsEnabled(GL11.GL_COLOR_ARRAY));
assertFalse(GLStateManager.glIsEnabled(GL11.GL_NORMAL_ARRAY));
assertTrue(colorArrayEnabled());
assertEquals(3, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE));
assertEquals(GL11.GL_FLOAT, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_TYPE));
assertEquals(12, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_STRIDE));
assertEquals(vbo, driverAttrib(position, GL15.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING));
assertEquals(GL11.GL_TRUE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
assertEquals(GL11.GL_TRUE, driverAttrib(color, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
assertEquals(GL11.GL_FALSE, driverAttrib(normal, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
assertEquals(GL11.GL_FALSE, driverAttrib(5, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
GLStateManager.glBindVertexArray(vaoB);
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
GLStateManager.glBindVertexArray(vao);
GLStateManager.glDeleteVertexArrays(vaoB);
GLStateManager.glPopClientAttrib();
assertEquals(vao, GLStateManager.getBoundVAO(), "a deleted VAO must not be restored");
assertVertexArray("after dead-vao pop", 3, GL11.GL_FLOAT, 12, vbo);
} finally {
drainClientAttribStack();
GLStateManager.glBindVertexArray(vao);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
for (final int cap : CLIENT_ARRAY_CAPS) {
GLStateManager.glDisableClientState(cap);
}
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GLStateManager.glDeleteVertexArrays(vaoB);
GLStateManager.glDeleteBuffers(vbo2);
GLStateManager.glDeleteBuffers(ebo);
}
}
@Test
void enabledGenericAttributeWinsWithoutAPointer() {
final int position = Usage.POSITION.getAttributeLocation();
try {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L);
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
assertFalse(VAOManager.get(position).genericPointer);
GLStateManager.glEnableVertexAttribArray(position);
assertTrue(VAOManager.get(position).genericPointer, "generic 0 supersedes position on its enable bit alone");
GLStateManager.glDisableVertexAttribArray(position);
assertFalse(VAOManager.get(position).genericPointer);
assertEquals(3, VAOManager.get(position).size);
} finally {
GLStateManager.glDisableVertexAttribArray(position);
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
}
@Test
void deletedBufferIsDetachedFromTheBoundVertexArray() {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 4L);
GLStateManager.glFogCoordPointer(GL11.GL_FLOAT, 4, 0L);
assertEquals(vbo, GLStateManager.glGetInteger(GL15.GL_VERTEX_ARRAY_BUFFER_BINDING));
GLStateManager.glDeleteBuffers(vbo);
assertEquals(0, GLStateManager.glGetInteger(GL15.GL_VERTEX_ARRAY_BUFFER_BINDING));
assertEquals(0, GLStateManager.glGetInteger(GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING));
assertEquals(12, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_STRIDE), "offset and stride survive the detach");
assertEquals(3, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_SIZE));
}
@Test
void shadowedPointerIsNotRepointedAtAReusedBufferName() {
final int other = GLStateManager.glGenVertexArrays();
final int conventional = GLStateManager.glGenBuffers();
final int generic = GLStateManager.glGenBuffers();
final int position = Usage.POSITION.getAttributeLocation();
int reused = 0;
try {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, conventional);
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L);
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, generic);
GLStateManager.glVertexAttribPointer(position, 2, GL11.GL_FLOAT, false, 8, 0L);
GLStateManager.glEnableVertexAttribArray(position);
GLStateManager.glBindVertexArray(other);
GLStateManager.glDeleteBuffers(conventional);
reused = GLStateManager.glGenBuffers();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, reused);
assumeTrue(reused == conventional, "driver did not reuse the buffer name");
GLStateManager.glBindVertexArray(vao);
GLStateManager.glDisableVertexAttribArray(position);
assertEquals(generic, driverAttrib(position, GL15.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING), "a pointer set before the delete must not attach to the new buffer");
} finally {
GLStateManager.glBindVertexArray(vao);
GLStateManager.glDisableVertexAttribArray(position);
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GLStateManager.glDeleteVertexArrays(other);
GLStateManager.glDeleteBuffers(generic);
if (reused != 0) GLStateManager.glDeleteBuffers(reused);
}
}
@Test
void popKeepsCurrentArraysWhenTheSavedArrayBufferWasDeleted() {
final int vbo2 = GLStateManager.glGenBuffers();
try {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L);
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo2);
GLStateManager.glVertexPointer(2, GL11.GL_SHORT, 4, 0L);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
GLStateManager.glDeleteBuffers(vbo);
GLStateManager.glPopClientAttrib();
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(GL13.GL_TEXTURE0, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE));
} finally {
drainClientAttribStack();
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GLStateManager.glDeleteBuffers(vbo2);
}
}
@Test
void clientAttribStackIsNotCompiledIntoDisplayLists() {
final int other = GLStateManager.glGenVertexArrays();
final int list = GLStateManager.glGenLists(1);
try {
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
GLStateManager.glPopClientAttrib();
GLStateManager.glEndList();
GLStateManager.glBindVertexArray(other);
GLStateManager.glCallList(list);
assertEquals(other, GLStateManager.getBoundVAO(), "replaying the list must not rebind the vertex array that was bound while compiling");
} finally {
drainClientAttribStack();
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glDeleteVertexArrays(other);
}
}
@Test
void popRestoresDivisorAndStateWithoutAnAttributeLocation() {
try {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLStateManager.glVertexAttribPointer(5, 1, GL11.GL_FLOAT, false, 4, 0L);
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
GLStateManager.glPopClientAttrib();
assertEquals(0, driverAttrib(5, GL33.GL_VERTEX_ATTRIB_ARRAY_DIVISOR));
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
GLStateManager.glVertexAttribDivisor(5, 3);
assertEquals(3, driverAttrib(5, GL33.GL_VERTEX_ATTRIB_ARRAY_DIVISOR));
GLStateManager.glPopClientAttrib();
assertEquals(0, driverAttrib(5, GL33.GL_VERTEX_ATTRIB_ARRAY_DIVISOR));
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
GLStateManager.glEnableClientState(GL14.GL_FOG_COORDINATE_ARRAY);
GLStateManager.glPopClientAttrib();
assertFalse(GLStateManager.glIsEnabled(GL14.GL_FOG_COORDINATE_ARRAY));
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
GLStateManager.glEnableVertexAttribArray(5);
GLStateManager.glPopClientAttrib();
assertEquals(GL11.GL_FALSE, driverAttrib(5, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED));
} finally {
drainClientAttribStack();
GLStateManager.glDisableVertexAttribArray(5);
GLStateManager.glDisableClientState(GL14.GL_FOG_COORDINATE_ARRAY);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
}
@Test
void interleavedArraysFromAClientBufferRegisterClientPointers() {
final FloatBuffer data = BufferUtils.createFloatBuffer(64);
final int position = Usage.POSITION.getAttributeLocation();
try {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
data.position(2);
GLStateManager.glInterleavedArrays(GL11.GL_C4UB_V3F, 0, data);
final long base = MemoryUtilities.memAddress(data);
assertNotNull(VAOManager.get(position).clientPointer);
assertEquals(base + 4, MemoryUtilities.memAddress(VAOManager.get(position).clientPointer));
assertEquals(base + 4, MemoryUtilities.memAddress(GLStateManager.glGetPointer(GL11.GL_VERTEX_ARRAY_POINTER, 4)));
assertEquals(base, MemoryUtilities.memAddress(GLStateManager.glGetPointer(GL11.GL_COLOR_ARRAY_POINTER, 4)));
assertEquals(0, GLStateManager.glGetInteger(GL15.GL_VERTEX_ARRAY_BUFFER_BINDING));
assertEquals(16, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_STRIDE));
assertTrue(GLStateManager.ctx().vaos.hasAnyClientSideEnabledAttrib());
} finally {
for (final int cap : CLIENT_ARRAY_CAPS) {
GLStateManager.glDisableClientState(cap);
}
}
}
@Test
void enableAndDisableRouteClientArrayCapsToClientState() {
for (final int cap : CLIENT_ARRAY_CAPS) {
GLStateManager.glEnable(cap);
assertTrue(GLStateManager.glIsEnabled(cap), hex(cap));
GLStateManager.glDisable(cap);
assertFalse(GLStateManager.glIsEnabled(cap), hex(cap));
}
GLStateManager.glEnable(GL11.GL_VERTEX_ARRAY);
assertTrue(positionArrayEnabled());
GLStateManager.glDisable(GL11.GL_VERTEX_ARRAY);
}
@Test
void illegalPointerArgumentsAreIgnored() {
try {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L);
GLStateManager.glNormalPointer(GL11.GL_FLOAT, 12, 0L);
GLStateManager.glVertexPointer(1, GL11.GL_FLOAT, 4, 0L);
GLStateManager.glVertexPointer(3, GL11.GL_UNSIGNED_BYTE, 3, 0L);
GLStateManager.glNormalPointer(GL11.GL_UNSIGNED_BYTE, 3, 0L);
assertVertexArray("vertex after illegal calls", 3, GL11.GL_FLOAT, 12, vbo);
assertEquals(GL11.GL_FLOAT, GLStateManager.glGetInteger(GL11.GL_NORMAL_ARRAY_TYPE));
GLStateManager.glColorPointer(GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, 4, 0L);
assertEquals(GL12.GL_BGRA, GLStateManager.glGetInteger(GL11.GL_COLOR_ARRAY_SIZE));
GLStateManager.glColorPointer(GL12.GL_BGRA, GL11.GL_FLOAT, 16, 0L);
assertEquals(GL11.GL_UNSIGNED_BYTE, GLStateManager.glGetInteger(GL11.GL_COLOR_ARRAY_TYPE));
GLStateManager.glVertexAttribPointer(VAOManager.MAX_ATTRIBS, 3, GL11.GL_FLOAT, false, 0, 0L);
GLStateManager.glEnableVertexAttribArray(VAOManager.MAX_ATTRIBS);
GLStateManager.glDisableVertexAttribArray(VAOManager.MAX_ATTRIBS);
GLStateManager.glVertexAttribPointer(5, 5, GL11.GL_FLOAT, false, 0, 0L);
assertNull(VAOManager.get(5));
} finally {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
}
@Test
void clientArrayEnablesAreAnsweredFromCache() {
final int vaoB = GLStateManager.glGenVertexArrays();
try {
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
for (final int cap : CLIENT_ARRAY_CAPS) {
assertFalse(GLStateManager.glIsEnabled(cap), "default isEnabled " + hex(cap));
assertEnableRoundTrip(cap, true);
}
GLStateManager.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
assertFalse(GLStateManager.glIsEnabled(GL11.GL_TEXTURE_COORD_ARRAY), "texcoord array enable is per client texture unit");
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
assertTrue(GLStateManager.glIsEnabled(GL11.GL_TEXTURE_COORD_ARRAY));
GLStateManager.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
GLStateManager.glEnableClientState(GL11.GL_INDEX_ARRAY);
GLStateManager.glBindVertexArray(vaoB);
assertFalse(GLStateManager.glIsEnabled(GL11.GL_INDEX_ARRAY), "array enables are per vertex array object");
GLStateManager.glBindVertexArray(vao);
assertTrue(GLStateManager.glIsEnabled(GL11.GL_INDEX_ARRAY));
GLStateManager.glDisableClientState(GL11.GL_INDEX_ARRAY);
GLStateManager.glBindVertexArray(vaoB);
GLStateManager.glEnableVertexAttribArray(0);
assertFalse(GLStateManager.glIsEnabled(GL11.GL_VERTEX_ARRAY), "an enabled generic attribute 0 is not GL_VERTEX_ARRAY");
GLStateManager.glDisableVertexAttribArray(0);
GLStateManager.glBindVertexArray(vao);
} finally {
GLStateManager.glBindVertexArray(vaoB);
GLStateManager.glDisableVertexAttribArray(0);
GLStateManager.glBindVertexArray(vao);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
for (final int cap : CLIENT_ARRAY_CAPS) {
GLStateManager.glDisableClientState(cap);
}
GLStateManager.glDeleteVertexArrays(vaoB);
}
}
}
@@ -76,7 +76,6 @@ public class GLSM_CoreRemovedCaps_GLTest {
assertFalse(GLStateManager.glIsEnabled(cap));
} finally {
GLStateManager.glDisable(cap);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
}
@@ -91,7 +90,6 @@ public class GLSM_CoreRemovedCaps_GLTest {
assertFalse(GLStateManager.glIsEnabled(cap));
} finally {
GLStateManager.glDisable(cap);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
}
@@ -105,7 +103,6 @@ public class GLSM_CoreRemovedCaps_GLTest {
assertSecondaryColor(0.25F, 0.5F, 0.75F);
} finally {
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
}
@@ -121,7 +118,6 @@ public class GLSM_CoreRemovedCaps_GLTest {
assertSecondaryColor(0.125F, 0.25F, 0.375F);
} finally {
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
}
@@ -135,7 +131,6 @@ public class GLSM_CoreRemovedCaps_GLTest {
assertFalse(GLStateManager.glIsEnabled(GL14.GL_COLOR_SUM));
} finally {
GLStateManager.glDisable(GL14.GL_COLOR_SUM);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
}
@@ -156,7 +151,6 @@ public class GLSM_CoreRemovedCaps_GLTest {
} finally {
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
}
@@ -176,7 +170,6 @@ public class GLSM_CoreRemovedCaps_GLTest {
} finally {
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glDisable(GL11.GL_LINE_STIPPLE);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
}
}
@@ -0,0 +1,62 @@
package com.gtnewhorizons.angelica.glsm;
import org.junit.jupiter.api.Test;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL30;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
@GLCoreTest
public class GLSM_DeferredTextureDelete_GLTest {
private static int immutableTexture() {
assumeTrue(GLStateManager.capabilities.OpenGL42, "no glTexStorage2D on this context");
final int tex = GLStateManager.glGenTextures();
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
GLStateManager.glTexStorage2D(GL11.GL_TEXTURE_2D, 1, GL30.GL_R8, 4, 4);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
return tex;
}
@Test
void deletingAnImmutableTextureRaisesNoError() {
final int tex = immutableTexture();
GLStateManager.glDeleteTextures(tex);
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
}
@Test
void deletingARescuedImmutableTextureRaisesNoError() {
final int tex = immutableTexture();
GLStateManager.glDeleteTextures(tex);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
GLStateManager.glDeleteTextures(tex);
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
}
@Test
void deletingA2DTextureWhileA3DTextureIsBoundLeavesNothingBoundTo2D() {
final int t2d = GLStateManager.glGenTextures();
final int t3d = GLStateManager.glGenTextures();
try {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, t2d);
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, t3d);
GLStateManager.glDeleteTextures(t2d);
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
assertEquals(0, GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D), "the deleted texture must not stay bound");
assertEquals(t3d, GL11.glGetInteger(GL12.GL_TEXTURE_BINDING_3D));
} finally {
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, 0);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GLStateManager.glDeleteTextures(t3d);
}
}
}
@@ -6,18 +6,12 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.floats;
import static org.junit.jupiter.api.Assertions.assertEquals;
@GLCoreTest
public class GLSM_DirtyTracking_UnitTest {
static final FloatBuffer f4b = ByteBuffer.allocateDirect(4 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
static final FloatBuffer f3b = ByteBuffer.allocateDirect(3 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
@BeforeEach
void pushTrackedState() {
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT | GL11.GL_FOG_BIT | GL11.GL_CURRENT_BIT);
@@ -30,38 +24,31 @@ public class GLSM_DirtyTracking_UnitTest {
@Test
void materialBumpsOnlyOnChange() {
newf4b(0.1F, 0.2F, 0.3F, 0.4F);
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, f4b);
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, floats(0.1F, 0.2F, 0.3F, 0.4F));
final int gen = GLStateManager.getLightingGeneration();
newf4b(0.1F, 0.2F, 0.3F, 0.4F);
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, f4b);
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, floats(0.1F, 0.2F, 0.3F, 0.4F));
assertEquals(gen, GLStateManager.getLightingGeneration(), "identical material must not bump lightingGeneration");
newf4b(0.9F, 0.2F, 0.3F, 0.4F);
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, f4b);
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, floats(0.9F, 0.2F, 0.3F, 0.4F));
assertEquals(gen + 1, GLStateManager.getLightingGeneration(), "changed material must bump lightingGeneration once");
}
@Test
void lightModelBumpsOnlyOnChange() {
newf4b(0.5F, 0.5F, 0.5F, 1.0F);
GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, f4b);
GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, floats(0.5F, 0.5F, 0.5F, 1.0F));
final int gen = GLStateManager.getLightingGeneration();
newf4b(0.5F, 0.5F, 0.5F, 1.0F);
GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, f4b);
GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, floats(0.5F, 0.5F, 0.5F, 1.0F));
assertEquals(gen, GLStateManager.getLightingGeneration(), "identical light model ambient must not bump");
}
@Test
void lightColorBumpsOnlyOnChange() {
newf4b(0.25F, 0.5F, 0.75F, 1.0F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, f4b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, floats(0.25F, 0.5F, 0.75F, 1.0F));
final int gen = GLStateManager.getLightingGeneration();
newf4b(0.25F, 0.5F, 0.75F, 1.0F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, f4b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, floats(0.25F, 0.5F, 0.75F, 1.0F));
assertEquals(gen, GLStateManager.getLightingGeneration(), "identical light diffuse must not bump");
}
@@ -72,17 +59,14 @@ public class GLSM_DirtyTracking_UnitTest {
GLStateManager.glLoadIdentity();
GLStateManager.glTranslatef(1.0F, 2.0F, 3.0F);
newf4b(0.1F, 0.2F, 0.3F, 1.0F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.1F, 0.2F, 0.3F, 1.0F));
final int gen = GLStateManager.getLightingGeneration();
newf4b(0.1F, 0.2F, 0.3F, 1.0F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.1F, 0.2F, 0.3F, 1.0F));
assertEquals(gen, GLStateManager.getLightingGeneration(), "same position under same modelview must not bump");
GLStateManager.glTranslatef(5.0F, 0.0F, 0.0F);
newf4b(0.1F, 0.2F, 0.3F, 1.0F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.1F, 0.2F, 0.3F, 1.0F));
assertEquals(gen + 1, GLStateManager.getLightingGeneration(), "same raw position under changed modelview must bump");
GLStateManager.glPopMatrix();
@@ -128,18 +112,15 @@ public class GLSM_DirtyTracking_UnitTest {
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
newf3b(0.0F, 0.0F, -1.0F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f3b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, floats(0.0F, 0.0F, -1.0F));
final int gen = GLStateManager.getLightingGeneration();
GLStateManager.glTranslatef(5.0F, 0.0F, 0.0F);
newf3b(0.0F, 0.0F, -1.0F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f3b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, floats(0.0F, 0.0F, -1.0F));
assertEquals(gen, GLStateManager.getLightingGeneration(), "spot direction under translation-only must not bump");
GLStateManager.glRotatef(90.0F, 0.0F, 1.0F, 0.0F);
newf3b(0.0F, 0.0F, -1.0F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f3b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, floats(0.0F, 0.0F, -1.0F));
assertEquals(gen + 1, GLStateManager.getLightingGeneration(), "same raw spot direction under changed 3x3 must bump");
GLStateManager.glPopMatrix();
@@ -158,16 +139,4 @@ public class GLSM_DirtyTracking_UnitTest {
assertEquals(0.0F, diffuse.y, "green channel must be 0");
assertEquals(1.0F, diffuse.z, "blue channel must be 1");
}
static void newf4b(float x, float y, float z, float w) {
f4b.clear();
f4b.put(x).put(y).put(z).put(w);
f4b.flip();
}
static void newf3b(float x, float y, float z) {
f3b.clear();
f3b.put(x).put(y).put(z);
f3b.flip();
}
}
@@ -2,6 +2,8 @@ package com.gtnewhorizons.angelica.glsm;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.lwjgl.opengl.GL11;
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
@@ -28,106 +30,26 @@ class GLSM_DisplayList_FormatOptimization_Test {
GLStateManager.glNormal3f(0.0f, 0.0f, 1.0f);
}
@Test
void testPositionOnlyFormat() {
// Create a display list with only position data (no color, texture, brightness, or normals)
// Use GLStateManager for all calls to ensure they go through GLSM (test env doesn't redirect GL11)
private static final float[][] CORNERS = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
private static final float[][] COLORS = {{1, 1, 1, 1}, {1, 0, 0, 1}, {0, 1, 0, 1}, {0, 0, 1, 1}};
@ParameterizedTest(name = "normals={0}")
@ValueSource(booleans = {false, true})
void colorTextureFormatLeavesLastColorCurrent(boolean normals) {
displayList = GL11.glGenLists(1);
GLStateManager.glNewList(displayList, GL11.GL_COMPILE);
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);
for (int i = 0; i < 4; i++) {
if (normals) GLStateManager.glNormal3f(0, 0, 1);
GLStateManager.glColor4f(COLORS[i][0], COLORS[i][1], COLORS[i][2], COLORS[i][3]);
GLStateManager.glTexCoord2f(CORNERS[i][0], CORNERS[i][1]);
GLStateManager.glVertex3f(CORNERS[i][0], CORNERS[i][1], 0);
}
GLStateManager.glEnd();
GLStateManager.glEndList();
// Call the list to ensure it doesn't crash (verifies compilation succeeded)
GLStateManager.glCallList(displayList);
}
@Test
void testPositionTextureFormat() {
// Create a display list with position and texture coordinates
displayList = GL11.glGenLists(1);
GLStateManager.glNewList(displayList, GL11.GL_COMPILE);
GLStateManager.glBegin(GL11.GL_QUADS);
GLStateManager.glTexCoord2f(0, 0);
GLStateManager.glVertex3f(0, 0, 0);
GLStateManager.glTexCoord2f(1, 0);
GLStateManager.glVertex3f(1, 0, 0);
GLStateManager.glTexCoord2f(1, 1);
GLStateManager.glVertex3f(1, 1, 0);
GLStateManager.glTexCoord2f(0, 1);
GLStateManager.glVertex3f(0, 1, 0);
GLStateManager.glEnd();
GLStateManager.glEndList();
// Call the list to ensure it doesn't crash (verifies compilation succeeded)
GLStateManager.glCallList(displayList);
}
@Test
void testPositionColorTextureFormat() {
// Create a display list with position, color, and texture
displayList = GL11.glGenLists(1);
GLStateManager.glNewList(displayList, GL11.GL_COMPILE);
GLStateManager.glBegin(GL11.GL_QUADS);
GLStateManager.glColor4f(1, 1, 1, 1);
GLStateManager.glTexCoord2f(0, 0);
GLStateManager.glVertex3f(0, 0, 0);
GLStateManager.glColor4f(1, 0, 0, 1);
GLStateManager.glTexCoord2f(1, 0);
GLStateManager.glVertex3f(1, 0, 0);
GLStateManager.glColor4f(0, 1, 0, 1);
GLStateManager.glTexCoord2f(1, 1);
GLStateManager.glVertex3f(1, 1, 0);
GLStateManager.glColor4f(0, 0, 1, 1);
GLStateManager.glTexCoord2f(0, 1);
GLStateManager.glVertex3f(0, 1, 0);
GLStateManager.glEnd();
GLStateManager.glEndList();
// Call the list and verify color is restored to last value (blue)
GLStateManager.glCallList(displayList);
verifyState(GL11.GL_CURRENT_COLOR, new float[]{0f, 0f, 1f, 1f},
"After playback, color should be blue (last color in immediate mode block)");
}
@Test
void testFullFormat() {
// Create a display list with all attributes: position, color, texture, and normal
displayList = GL11.glGenLists(1);
GLStateManager.glNewList(displayList, GL11.GL_COMPILE);
GLStateManager.glBegin(GL11.GL_QUADS);
GLStateManager.glNormal3f(0, 0, 1);
GLStateManager.glColor4f(1, 1, 1, 1);
GLStateManager.glTexCoord2f(0, 0);
GLStateManager.glVertex3f(0, 0, 0);
GLStateManager.glNormal3f(0, 0, 1);
GLStateManager.glColor4f(1, 0, 0, 1);
GLStateManager.glTexCoord2f(1, 0);
GLStateManager.glVertex3f(1, 0, 0);
GLStateManager.glNormal3f(0, 0, 1);
GLStateManager.glColor4f(0, 1, 0, 1);
GLStateManager.glTexCoord2f(1, 1);
GLStateManager.glVertex3f(1, 1, 0);
GLStateManager.glNormal3f(0, 0, 1);
GLStateManager.glColor4f(0, 0, 1, 1);
GLStateManager.glTexCoord2f(0, 1);
GLStateManager.glVertex3f(0, 1, 0);
GLStateManager.glEnd();
GLStateManager.glEndList();
// Call the list and verify color is restored to last value (blue)
GLStateManager.glCallList(displayList);
verifyState(GL11.GL_CURRENT_COLOR, new float[]{0f, 0f, 1f, 1f},
"After playback, color should be blue (last color in immediate mode block)");
@@ -2,6 +2,8 @@ package com.gtnewhorizons.angelica.glsm;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer;
@@ -15,6 +17,23 @@ public class GLSM_DisplayList_PixelStore_Test {
private static final int SRC = 8;
private static final int SUB = 4;
enum Upload {
TEX_SUB_IMAGE_2D {
@Override
void run(ByteBuffer src) {
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
}
},
TEX_IMAGE_2D {
@Override
void run(ByteBuffer src) {
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SUB, SUB, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
}
};
abstract void run(ByteBuffer src);
}
@AfterEach
void resetUnpackState() {
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4);
@@ -96,8 +115,9 @@ public class GLSM_DisplayList_PixelStore_Test {
GLStateManager.glDeleteLists(list, 1);
}
@Test
void stridedTexSubImage2DCompile() {
@ParameterizedTest
@EnumSource(Upload.class)
void stridedUploadCompile(Upload upload) {
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
final ByteBuffer src = makeSource(SRC);
@@ -105,7 +125,7 @@ public class GLSM_DisplayList_PixelStore_Test {
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
upload.run(src);
GLStateManager.glEndList();
assertEquals(0, red(readback(SUB), SUB, 0, 0), "GL_COMPILE must not execute the upload");
@@ -146,58 +166,15 @@ public class GLSM_DisplayList_PixelStore_Test {
GLStateManager.glDeleteTextures(tex);
}
@Test
void stridedTexImage2DCompile() {
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
final ByteBuffer src = makeSource(SRC);
setStridedUnpack();
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SUB, SUB, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
GLStateManager.glEndList();
setUnpack(0, 0, 0);
GLStateManager.glCallList(list);
assertStridedSubImage(readback(SUB));
assertUnpack(0, 0, 0);
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glDeleteTextures(tex);
}
@Test
void defaultRecordedTexSubImage2DReplaysWithDefaults() {
@ParameterizedTest
@EnumSource(Upload.class)
void defaultRecordedUploadReplaysWithDefaults(Upload upload) {
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
final ByteBuffer src = makeSource(SUB);
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
GLStateManager.glEndList();
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
setStridedUnpack();
GLStateManager.glCallList(list);
assertTightSubImage(readback(SUB));
assertEquals(1, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT), "live state must survive replay");
assertUnpack(SRC, 1, 2);
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glDeleteTextures(tex);
}
@Test
void defaultRecordedTexImage2DReplaysWithDefaults() {
final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder()));
final ByteBuffer src = makeSource(SUB);
final int list = GLStateManager.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SUB, SUB, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src);
upload.run(src);
GLStateManager.glEndList();
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
@@ -1,14 +1,14 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
import com.gtnewhorizons.angelica.glsm.recording.GLCommand;
import it.unimi.dsi.fastutil.ints.IntList;
import net.minecraft.client.renderer.Tessellator;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.lwjgl.opengl.GL11;
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.immediateQuad;
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.rawQuad;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -27,34 +27,6 @@ class GLSM_DisplayList_RawTessellatorTransform_Test {
GLStateManager.glLoadIdentity();
}
private static void rawQuad(float size) {
final Tessellator tess = Tessellator.instance;
final float[][] corners = { { 0.0f, 0.0f }, { size, 0.0f }, { size, size }, { 0.0f, size } };
tess.rawBuffer = new int[4 * 8];
for (int i = 0; i < 4; i++) {
final int base = i * 8;
tess.rawBuffer[base] = Float.floatToRawIntBits(corners[i][0]);
tess.rawBuffer[base + 1] = Float.floatToRawIntBits(corners[i][1]);
tess.rawBuffer[base + 3] = Float.floatToRawIntBits(i == 1 || i == 2 ? 1.0f : 0.0f);
tess.rawBuffer[base + 4] = Float.floatToRawIntBits(i >= 2 ? 1.0f : 0.0f);
}
tess.isDrawing = true;
tess.drawMode = GL11.GL_QUADS;
tess.hasTexture = true;
tess.rawBufferIndex = 4 * 8;
tess.vertexCount = 4;
TessellatorManager.interceptDraw(tess);
}
private static void immediateQuad(float size) {
GLStateManager.glBegin(GL11.GL_QUADS);
GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f);
GLStateManager.glVertex3f(size, 0.0f, 0.0f);
GLStateManager.glVertex3f(size, size, 0.0f);
GLStateManager.glVertex3f(0.0f, size, 0.0f);
GLStateManager.glEnd();
}
@Test
void pendingTransformIsEmittedBeforeARawDraw() {
testList = GLStateManager.glGenLists(1);
@@ -1,19 +1,25 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
import com.gtnewhorizons.angelica.glsm.recording.GLCommand;
import net.minecraft.client.renderer.Tessellator;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.IntList;
import org.joml.Matrix4f;
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 org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import java.nio.FloatBuffer;
import java.util.stream.Stream;
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.assertMatrixEquals;
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.immediateQuad;
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.modelviewAfterCall;
import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.rawQuad;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -70,12 +76,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
"No individual transform commands should remain in optimized list");
// Verify behavioral correctness: matrix state should match expected
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
final FloatBuffer actualBuf = modelviewAfterCall(testList);
Matrix4f expected = new Matrix4f()
.translate(1.0f, 2.0f, 3.0f)
@@ -84,15 +85,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
.translate(-0.5f, -0.5f, -0.5f)
.rotate((float) Math.toRadians(30.0f), 1.0f, 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 after collapsed transforms", i));
}
GLStateManager.glPopMatrix();
assertMatrixEquals(expected, actualBuf, "mismatch after collapsed transforms");
}
@Test
@@ -121,12 +114,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
assertEquals(1, popCount, "Should have exactly 1 PopMatrix");
// Verify behavioral correctness
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
final FloatBuffer actualBuf = modelviewAfterCall(testList);
// Expected: translate(10,0,0) then translate(0,3,0) = translate(10,3,0)
// The inner push/pop isolates the (5,0,0) translate and rotation
@@ -134,15 +122,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
.translate(10.0f, 0.0f, 0.0f)
.translate(0.0f, 3.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 - push/pop should isolate inner transforms", i));
}
GLStateManager.glPopMatrix();
assertMatrixEquals(expected, actualBuf, "mismatch - push/pop should isolate inner transforms");
}
@Test
@@ -164,12 +144,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
GLStateManager.glEndList();
// Verify behavioral correctness
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
final FloatBuffer actualBuf = modelviewAfterCall(testList);
// Final state: translate(1,0,0) + translate(5,0,0) = translate(6,0,0)
// Level 1 and 2 transforms are isolated by push/pop
@@ -182,8 +157,6 @@ class GLSM_DisplayList_TransformCollapsing_Test {
assertEquals(expectedBuf.get(12), actualBuf.get(12), 0.0001f,
"X translation should be 6.0 (1+5, inner transforms isolated)");
GLStateManager.glPopMatrix(); // Balance the push from line 185
}
@Test
@@ -219,12 +192,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
"MultMatrix should be emitted BEFORE CallList (transform flush at barrier)");
// Verify behavioral correctness
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
final FloatBuffer actualBuf = modelviewAfterCall(testList);
// Build expected: translate(10,0,0) * rotate(90,Y) * translate(100,0,0) * translate(1,0,0)
// After 90-degree Y rotation, X axis points into -Z, so nested translate goes along -Z
@@ -234,15 +202,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
.translate(100.0f, 0.0f, 0.0f)
.translate(1.0f, 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 - nested list should see parent's transformed state", i));
}
GLStateManager.glPopMatrix();
assertMatrixEquals(expected, actualBuf, "mismatch - nested list should see parent's transformed state");
}
@Test
@@ -271,63 +231,33 @@ class GLSM_DisplayList_TransformCollapsing_Test {
"Identity-equivalent transforms should not emit MultMatrix");
}
@Test
void testTransformBetweenDrawsCreatesSeparateBatches() {
// This test verifies that draws with different transforms are not batched together
// Note: Without actual Tessellator integration, we can only verify command structure
testList = GL11.glGenLists(1);
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
// Simulate: Transform A, Draw, Transform B, Draw
// The draws should NOT be in the same batch
GLStateManager.glTranslatef(1.0f, 0.0f, 0.0f);
// (Would have a draw here in real usage)
GLStateManager.glTranslatef(2.0f, 0.0f, 0.0f);
// (Would have another draw here)
GLStateManager.glEndList();
// Verify the transforms are accumulated correctly
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
// Total translation should be 3.0 (1+2)
assertEquals(3.0f, actualBuf.get(12), 0.0001f,
"Consecutive translates should accumulate");
GLStateManager.glPopMatrix();
static Stream<Arguments> discardedByLoadIdentity() {
return Stream.of(
Arguments.of("accumulated translate", (Runnable) () -> GLStateManager.glTranslatef(100.0f, 100.0f, 100.0f)),
Arguments.of("loaded matrix", (Runnable) () -> {
final FloatBuffer loadBuf = BufferUtils.createFloatBuffer(16);
new Matrix4f().translate(50.0f, 60.0f, 70.0f).rotate((float) Math.toRadians(45.0f), 1.0f, 1.0f, 0.0f).get(loadBuf);
GLStateManager.glLoadMatrix(loadBuf);
}));
}
@Test
void testLoadIdentityResetsAccumulation() {
// LoadIdentity should reset accumulated transforms
@ParameterizedTest(name = "{0}")
@MethodSource("discardedByLoadIdentity")
void testLoadIdentityDiscardsEarlierTransforms(String name, Runnable earlier) {
testList = GL11.glGenLists(1);
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
GLStateManager.glTranslatef(100.0f, 100.0f, 100.0f); // Big translate
GLStateManager.glLoadIdentity(); // Reset
GLStateManager.glTranslatef(1.0f, 2.0f, 3.0f); // New translate
earlier.run();
GLStateManager.glLoadIdentity();
GLStateManager.glTranslatef(1.0f, 2.0f, 3.0f);
GLStateManager.glEndList();
// Verify: final state should only have the second translate
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
final FloatBuffer actualBuf = modelviewAfterCall(testList);
assertEquals(1.0f, actualBuf.get(12), 0.0001f, "X should be 1.0 (LoadIdentity reset)");
assertEquals(2.0f, actualBuf.get(13), 0.0001f, "Y should be 2.0 (LoadIdentity reset)");
assertEquals(3.0f, actualBuf.get(14), 0.0001f, "Z should be 3.0 (LoadIdentity reset)");
GLStateManager.glPopMatrix();
}
@Test
@@ -359,121 +289,14 @@ class GLSM_DisplayList_TransformCollapsing_Test {
"Translate + MultMatrix + Translate should collapse to 1 MultMatrix");
// Verify behavioral correctness
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
final FloatBuffer actualBuf = modelviewAfterCall(testList);
Matrix4f expected = new Matrix4f()
.translate(1.0f, 0.0f, 0.0f)
.rotate((float) Math.toRadians(90.0f), 0.0f, 1.0f, 0.0f)
.translate(2.0f, 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 MultMatrix accumulation", i));
}
GLStateManager.glPopMatrix();
}
// ==================== Batcher Flush Ordering Tests ====================
@Test
void testDrawsBeforePushMatrixAreEmittedBeforePush() {
// This test verifies that draws occurring before a PushMatrix are flushed
// before the PushMatrix command, not delayed until later.
// This was a bug where the batcher wasn't flushed at PushMatrix.
// Create a list with: Transform, (implicit draw would go here), PushMatrix, Transform, PopMatrix
testList = GL11.glGenLists(1);
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
// Outer transform
GLStateManager.glTranslatef(1.0f, 0.0f, 0.0f);
// Note: If there was a draw here, it should be emitted BEFORE PushMatrix
GLStateManager.glPushMatrix();
GLStateManager.glTranslatef(5.0f, 0.0f, 0.0f); // Inner transform
GLStateManager.glPopMatrix();
// Final transform after pop
GLStateManager.glTranslatef(2.0f, 0.0f, 0.0f);
GLStateManager.glEndList();
// Verify the command structure
CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList);
assertNotNull(compiled, "Display list should be compiled");
IntList opcodes = compiled.getCommandOpcodes();
// Find indices of key commands
int pushIndex = opcodes.indexOf(GLCommand.PUSH_MATRIX);
int popIndex = opcodes.indexOf(GLCommand.POP_MATRIX);
// Count MultMatrix commands before first PushMatrix
int multMatrixBeforePushCount = 0;
for (int i = 0; i < opcodes.size() && i < pushIndex; i++) {
if (opcodes.get(i) == GLCommand.TRANSLATE) {
multMatrixBeforePushCount++;
}
}
assertTrue(pushIndex >= 0, "Should have PushMatrix command");
assertTrue(popIndex >= 0, "Should have PopMatrix command");
assertTrue(pushIndex < popIndex, "PushMatrix should come before PopMatrix");
// The outer translate(1,0,0) should be emitted as MultMatrix before PushMatrix
assertTrue(multMatrixBeforePushCount >= 1,
"Transform before PushMatrix should be emitted before the Push");
// Verify behavioral correctness: final state should be translate(1,0,0) + translate(2,0,0) = translate(3,0,0)
// The inner translate(5,0,0) is isolated by push/pop
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
assertEquals(3.0f, actualBuf.get(12), 0.0001f,
"X translation should be 3.0 (1+2, inner transform isolated by push/pop)");
GLStateManager.glPopMatrix();
}
@Test
void testDrawsBeforePopMatrixAreEmittedBeforePop() {
// Verify draws inside a push/pop block are emitted before the pop
testList = GL11.glGenLists(1);
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
GLStateManager.glPushMatrix();
GLStateManager.glTranslatef(10.0f, 0.0f, 0.0f);
// Draws here should be emitted before PopMatrix
GLStateManager.glPopMatrix();
GLStateManager.glEndList();
CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList);
IntList opcodes = compiled.getCommandOpcodes();
int pushIndex = opcodes.indexOf(GLCommand.PUSH_MATRIX);
int popIndex = opcodes.indexOf(GLCommand.POP_MATRIX);
assertTrue(pushIndex >= 0, "Should have PushMatrix");
assertTrue(popIndex >= 0, "Should have PopMatrix");
assertTrue(pushIndex < popIndex, "Push should come before Pop");
// The inner translate should be emitted between push and pop (or collapsed to residual)
// At minimum, the structure should be valid
assertTrue(popIndex > pushIndex, "PopMatrix should follow PushMatrix");
assertMatrixEquals(expected, actualBuf, "mismatch for MultMatrix accumulation");
}
// ==================== LoadMatrix Tests ====================
@@ -509,22 +332,9 @@ class GLSM_DisplayList_TransformCollapsing_Test {
"Should have exactly 1 LoadMatrix command (can't be absorbed)");
// Verify behavioral correctness: matrix should equal the loaded matrix
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
final FloatBuffer actualBuf = modelviewAfterCall(testList);
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16);
loadedMatrix.get(expectedBuf);
for (int i = 0; i < 16; i++) {
assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f,
String.format("Matrix[%d] should match loaded matrix (not multiplied)", i));
}
GLStateManager.glPopMatrix();
assertMatrixEquals(loadedMatrix, actualBuf, "should match loaded matrix (not multiplied)");
}
@Test
@@ -547,62 +357,11 @@ class GLSM_DisplayList_TransformCollapsing_Test {
GLStateManager.glEndList();
// Verify behavioral correctness
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
final FloatBuffer actualBuf = modelviewAfterCall(testList);
// Expected: loadedMatrix * translate(10,0,0)
Matrix4f expected = new Matrix4f(loadedMatrix).translate(10.0f, 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] - transform after LoadMatrix should be relative to loaded matrix", i));
}
GLStateManager.glPopMatrix();
}
@Test
void testLoadMatrixThenLoadIdentity() {
// LoadMatrix followed by LoadIdentity should result in identity
testList = GL11.glGenLists(1);
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
// Load a complex matrix
Matrix4f loadedMatrix = new Matrix4f()
.translate(50.0f, 60.0f, 70.0f)
.rotate((float) Math.toRadians(45.0f), 1.0f, 1.0f, 0.0f);
FloatBuffer loadBuf = BufferUtils.createFloatBuffer(16);
loadedMatrix.get(loadBuf);
loadBuf.rewind();
GLStateManager.glLoadMatrix(loadBuf);
// Then reset to identity
GLStateManager.glLoadIdentity();
// Then apply a simple translate
GLStateManager.glTranslatef(1.0f, 2.0f, 3.0f);
GLStateManager.glEndList();
// Verify: final state should just be translate(1,2,3)
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
assertEquals(1.0f, actualBuf.get(12), 0.0001f, "X should be 1.0");
assertEquals(2.0f, actualBuf.get(13), 0.0001f, "Y should be 2.0");
assertEquals(3.0f, actualBuf.get(14), 0.0001f, "Z should be 3.0");
GLStateManager.glPopMatrix();
assertMatrixEquals(expected, actualBuf, "transform after LoadMatrix should be relative to loaded matrix");
}
@Test
@@ -628,17 +387,10 @@ class GLSM_DisplayList_TransformCollapsing_Test {
GLStateManager.glEndList();
// Verify: final state should be matrix2
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf);
final FloatBuffer actualBuf = modelviewAfterCall(testList);
assertEquals(0.0f, actualBuf.get(12), 0.0001f, "X should be 0.0 (from second LoadMatrix)");
assertEquals(20.0f, actualBuf.get(13), 0.0001f, "Y should be 20.0 (from second LoadMatrix)");
GLStateManager.glPopMatrix();
}
// ==================== Immediate Mode with Transforms Tests ====================
@@ -652,12 +404,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
GLStateManager.glPushMatrix();
GLStateManager.glScalef(0.25f, 0.25f, 0.25f);
GLStateManager.glBegin(GL11.GL_QUADS);
GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f);
GLStateManager.glVertex3f(2.0f, 0.0f, 0.0f);
GLStateManager.glVertex3f(2.0f, 2.0f, 0.0f);
GLStateManager.glVertex3f(0.0f, 2.0f, 0.0f);
GLStateManager.glEnd();
immediateQuad(2.0f);
GLStateManager.glPopMatrix();
GLStateManager.glEndList();
@@ -683,12 +430,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
GLStateManager.glTranslatef(100.0f, 50.0f, 0.0f);
GLStateManager.glScalef(0.5f, 0.5f, 1.0f);
GLStateManager.glBegin(GL11.GL_QUADS);
GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f);
GLStateManager.glVertex3f(16.0f, 0.0f, 0.0f);
GLStateManager.glVertex3f(16.0f, 16.0f, 0.0f);
GLStateManager.glVertex3f(0.0f, 16.0f, 0.0f);
GLStateManager.glEnd();
immediateQuad(16.0f);
GLStateManager.glEndList();
@@ -700,20 +442,13 @@ class GLSM_DisplayList_TransformCollapsing_Test {
assertEquals(0, counts.getOrDefault(GLCommand.SCALE, 0), "No individual Scale");
// Verify matrix values after playback
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
FloatBuffer matrixBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, matrixBuf);
final FloatBuffer matrixBuf = modelviewAfterCall(testList);
assertEquals(0.5f, matrixBuf.get(0), 0.0001f, "Scale X");
assertEquals(0.5f, matrixBuf.get(5), 0.0001f, "Scale Y");
assertEquals(1.0f, matrixBuf.get(10), 0.0001f, "Scale Z");
assertEquals(100.0f, matrixBuf.get(12), 0.0001f, "Translate X");
assertEquals(50.0f, matrixBuf.get(13), 0.0001f, "Translate Y");
GLStateManager.glPopMatrix();
}
// ==================== Cross-Mode Transform Collapsing Tests ====================
@@ -806,12 +541,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GLStateManager.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
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.glEnd();
immediateQuad(1.0f);
GLStateManager.glEndList();
@@ -826,16 +556,9 @@ class GLSM_DisplayList_TransformCollapsing_Test {
assertTrue(opcodes.indexOf(GLCommand.MULT_MATRIX) > opcodes.indexOf(GLCommand.DRAW_RANGE),
"MultMatrix after DrawRange for mode " + mode);
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
FloatBuffer matrixBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, matrixBuf);
final FloatBuffer matrixBuf = modelviewAfterCall(testList);
assertEquals(0.5f, matrixBuf.get(0), 0.0001f, "Scale X for mode " + mode);
assertEquals(0.5f, matrixBuf.get(5), 0.0001f, "Scale Y for mode " + mode);
GLStateManager.glPopMatrix();
}
}
@@ -846,38 +569,16 @@ class GLSM_DisplayList_TransformCollapsing_Test {
GLStateManager.glScalef(0.5f, 0.5f, 0.5f);
final Tessellator tess = Tessellator.instance;
final float[][] corners = { { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f } };
tess.rawBuffer = new int[4 * 8];
for (int i = 0; i < 4; i++) {
final int base = i * 8;
tess.rawBuffer[base] = Float.floatToRawIntBits(corners[i][0]);
tess.rawBuffer[base + 1] = Float.floatToRawIntBits(corners[i][1]);
tess.rawBuffer[base + 3] = Float.floatToRawIntBits(i == 1 || i == 2 ? 1.0f : 0.0f);
tess.rawBuffer[base + 4] = Float.floatToRawIntBits(i >= 2 ? 1.0f : 0.0f);
}
tess.isDrawing = true;
tess.drawMode = GL11.GL_QUADS;
tess.hasTexture = true;
tess.rawBufferIndex = 4 * 8;
tess.vertexCount = 4;
TessellatorManager.interceptDraw(tess);
rawQuad(1.0f);
GLStateManager.glEndList();
CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList);
assertNotNull(compiled);
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
FloatBuffer matrixBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, matrixBuf);
final FloatBuffer matrixBuf = modelviewAfterCall(testList);
assertEquals(0.5f, matrixBuf.get(0), 0.0001f, "Scale X after playback");
assertEquals(0.5f, matrixBuf.get(5), 0.0001f, "Scale Y after playback");
GLStateManager.glPopMatrix();
}
// ==================== Optimizer Push/Pop Reset Tests ====================
@@ -898,12 +599,7 @@ class GLSM_DisplayList_TransformCollapsing_Test {
GLStateManager.glTranslatef(10.0f, 0.0f, 0.0f);
GLStateManager.glRotatef(45.0f, 0.0f, 1.0f, 0.0f);
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.glEnd();
immediateQuad(1.0f);
GLStateManager.glPopMatrix();
GLStateManager.glEndList();
@@ -930,18 +626,11 @@ class GLSM_DisplayList_TransformCollapsing_Test {
assertTrue(drawIndex < popIndex, "DRAW before POP");
// Verify final matrix after pop = T1
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
GLStateManager.glCallList(testList);
FloatBuffer matrixBuf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, matrixBuf);
final FloatBuffer matrixBuf = modelviewAfterCall(testList);
assertEquals(2.0f, matrixBuf.get(0), 0.0001f, "Scale X after pop");
assertEquals(2.0f, matrixBuf.get(5), 0.0001f, "Scale Y after pop");
assertEquals(5.0f, matrixBuf.get(12), 0.0001f, "Translate X after pop");
GLStateManager.glPopMatrix();
}
@Test
@@ -1,7 +1,16 @@
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.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL14;
import java.nio.FloatBuffer;
import java.util.function.DoubleSupplier;
import java.util.stream.Stream;
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -9,128 +18,77 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
@GLCompatTest
class GLSM_DisplayList_UnitTest {
@Test
void testDisplayListCompileCall() {
verifyState(GL11.GL_LIGHTING, false, "GL_LIGHTING Initial State");
int list = GL11.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glEnable(GL11.GL_LIGHTING);
GLStateManager.glEndList();
verifyState(GL11.GL_LIGHTING, false, "GL_LIGHTING Post Display List Compile");
GLStateManager.glCallList(list);
verifyState(GL11.GL_LIGHTING, true, "GL_LIGHTING Post Display List Call");
// Reset state that we changed during test
GLStateManager.glDisable(GL11.GL_LIGHTING);
/** One recordable state command: {@code apply} moves the driver value read by {@code driver} to {@code applied}. */
private static Arguments state(String name, Runnable apply, Runnable reset, DoubleSupplier driver, double applied) {
return Arguments.of(name, apply, reset, driver, applied);
}
@Test
void testDisplayListCompileExecute() {
verifyState(GL11.GL_LIGHTING, false, "GL_LIGHTING Initial State");
int list = GL11.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
GLStateManager.glEnable(GL11.GL_LIGHTING);
GLStateManager.glEndList();
verifyState(GL11.GL_LIGHTING, true, "GL_LIGHTING Post Display List Compile and Execute");
// Reset state that we changed during test
GLStateManager.glDisable(GL11.GL_LIGHTING);
private static double lighting() {
final boolean enabled = GLStateManager.glIsEnabled(GL11.GL_LIGHTING);
verifyState(GL11.GL_LIGHTING, enabled, "driver must agree with the GLSM cache");
return enabled ? 1.0 : 0.0;
}
// ==================== CullFace Tests ====================
@Test
void testCullFaceInDisplayList() {
// Get initial cull face mode
int initialMode = GL11.glGetInteger(GL11.GL_CULL_FACE_MODE);
int list = GL11.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glCullFace(GL11.GL_FRONT);
GLStateManager.glEndList();
// After compile, mode should be unchanged
assertEquals(initialMode, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "CullFace mode should be unchanged after GL_COMPILE");
// Call the display list
GLStateManager.glCallList(list);
// Now mode should be GL_FRONT
assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "CullFace mode should be GL_FRONT after calling display list");
// Cleanup
GLStateManager.glCullFace(GL11.GL_BACK); // Reset to default
GLStateManager.glDeleteLists(list, 1);
private static double blendColorRed() {
final FloatBuffer color = BufferUtils.createFloatBuffer(16);
GL11.glGetFloat(GL14.GL_BLEND_COLOR, color);
return color.get(0);
}
@Test
void testCullFaceCompileAndExecute() {
// Get initial cull face mode
int initialMode = GL11.glGetInteger(GL11.GL_CULL_FACE_MODE);
int list = GL11.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
GLStateManager.glCullFace(GL11.GL_FRONT_AND_BACK);
GLStateManager.glEndList();
// After compile and execute, mode should be changed
assertEquals(GL11.GL_FRONT_AND_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "CullFace mode should be GL_FRONT_AND_BACK after GL_COMPILE_AND_EXECUTE");
// Cleanup
GLStateManager.glCullFace(GL11.GL_BACK);
GLStateManager.glDeleteLists(list, 1);
static Stream<Arguments> states() {
return Stream.of(
state("glEnable(GL_LIGHTING)", () -> GLStateManager.glEnable(GL11.GL_LIGHTING), () -> GLStateManager.glDisable(GL11.GL_LIGHTING),
GLSM_DisplayList_UnitTest::lighting, 1.0),
state("glCullFace", () -> GLStateManager.glCullFace(GL11.GL_FRONT), () -> GLStateManager.glCullFace(GL11.GL_BACK),
() -> GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), GL11.GL_FRONT),
state("glLogicOp", () -> GLStateManager.glLogicOp(GL11.GL_XOR), () -> GLStateManager.glLogicOp(GL11.GL_COPY),
() -> GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE), GL11.GL_XOR),
state("glClearDepth", () -> GLStateManager.glClearDepth(0.5), () -> GLStateManager.glClearDepth(1.0),
() -> GL11.glGetDouble(GL11.GL_DEPTH_CLEAR_VALUE), 0.5),
state("glBlendColor", () -> GLStateManager.glBlendColor(0.5f, 0.4f, 0.6f, 0.8f), () -> GLStateManager.glBlendColor(0f, 0f, 0f, 0f),
GLSM_DisplayList_UnitTest::blendColorRed, 0.5),
state("glFrontFace", () -> GLStateManager.glFrontFace(GL11.GL_CW), () -> GLStateManager.glFrontFace(GL11.GL_CCW),
() -> GL11.glGetInteger(GL11.GL_FRONT_FACE), GL11.GL_CW),
state("glHint", () -> GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST), () -> GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_DONT_CARE),
() -> GL11.glGetInteger(GL11.GL_LINE_SMOOTH_HINT), GL11.GL_NICEST));
}
// ==================== LogicOp Tests ====================
@ParameterizedTest(name = "{0}")
@MethodSource("states")
void compiledStateChangeAppliesOnlyWhenTheListIsCalled(String name, Runnable apply, Runnable reset, DoubleSupplier driver, double applied) {
final double initial = driver.getAsDouble();
final int list = GL11.glGenLists(1);
try {
GLStateManager.glNewList(list, GL11.GL_COMPILE);
apply.run();
GLStateManager.glEndList();
assertEquals(initial, driver.getAsDouble(), 0.0001, name + " must not execute under GL_COMPILE");
@Test
void testLogicOpInDisplayList() {
// Get initial logic op
int initialOp = GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE);
int list = GL11.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glLogicOp(GL11.GL_XOR);
GLStateManager.glEndList();
// After compile, op should be unchanged
assertEquals(initialOp, GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE), "LogicOp should be unchanged after GL_COMPILE");
// Call the display list
GLStateManager.glCallList(list);
// Now op should be GL_XOR
assertEquals(GL11.GL_XOR, GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE), "LogicOp should be GL_XOR after calling display list");
// Cleanup
GLStateManager.glLogicOp(GL11.GL_COPY); // Reset to default
GLStateManager.glDeleteLists(list, 1);
GLStateManager.glCallList(list);
assertEquals(applied, driver.getAsDouble(), 0.0001, name + " must apply when the list is called");
} finally {
reset.run();
GLStateManager.glDeleteLists(list, 1);
}
}
@Test
void testLogicOpCompileAndExecute() {
int list = GL11.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
GLStateManager.glLogicOp(GL11.GL_INVERT);
GLStateManager.glEndList();
// After compile and execute, op should be changed
assertEquals(GL11.GL_INVERT, GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE), "LogicOp should be GL_INVERT after GL_COMPILE_AND_EXECUTE");
// Cleanup
GLStateManager.glLogicOp(GL11.GL_COPY);
GLStateManager.glDeleteLists(list, 1);
@ParameterizedTest(name = "{0}")
@MethodSource("states")
void compileAndExecuteAppliesStateChangeImmediately(String name, Runnable apply, Runnable reset, DoubleSupplier driver, double applied) {
final int list = GL11.glGenLists(1);
try {
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
apply.run();
GLStateManager.glEndList();
assertEquals(applied, driver.getAsDouble(), 0.0001, name + " must apply under GL_COMPILE_AND_EXECUTE");
} finally {
reset.run();
GLStateManager.glDeleteLists(list, 1);
}
}
@Test
void testMultipleStateChangesInDisplayList() {
// Test that multiple state commands work together
int list = GL11.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glCullFace(GL11.GL_FRONT);
@@ -138,173 +96,15 @@ class GLSM_DisplayList_UnitTest {
GLStateManager.glEnable(GL11.GL_LIGHTING);
GLStateManager.glEndList();
// Call the list
GLStateManager.glCallList(list);
// Verify all states changed
assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "CullFace should be GL_FRONT");
assertEquals(GL11.GL_AND, GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE), "LogicOp should be GL_AND");
verifyState(GL11.GL_LIGHTING, true, "GL_LIGHTING should be enabled");
// Cleanup
GLStateManager.glCullFace(GL11.GL_BACK);
GLStateManager.glLogicOp(GL11.GL_COPY);
GLStateManager.glDisable(GL11.GL_LIGHTING);
GLStateManager.glDeleteLists(list, 1);
}
// ==================== ClearDepth Tests ====================
@Test
void testClearDepthInDisplayList() {
// Get initial clear depth
double initialDepth = GL11.glGetDouble(GL11.GL_DEPTH_CLEAR_VALUE);
int list = GL11.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glClearDepth(0.5);
GLStateManager.glEndList();
// After compile, depth should be unchanged
assertEquals(initialDepth, GL11.glGetDouble(GL11.GL_DEPTH_CLEAR_VALUE), 0.0001, "ClearDepth should be unchanged after GL_COMPILE");
// Call the display list
GLStateManager.glCallList(list);
// Now depth should be 0.5
assertEquals(0.5, GL11.glGetDouble(GL11.GL_DEPTH_CLEAR_VALUE), 0.0001, "ClearDepth should be 0.5 after calling display list");
// Cleanup
GLStateManager.glClearDepth(1.0); // Reset to default
GLStateManager.glDeleteLists(list, 1);
}
@Test
void testClearDepthCompileAndExecute() {
int list = GL11.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
GLStateManager.glClearDepth(0.25);
GLStateManager.glEndList();
// After compile and execute, depth should be changed
assertEquals(0.25, GL11.glGetDouble(GL11.GL_DEPTH_CLEAR_VALUE), 0.0001, "ClearDepth should be 0.25 after GL_COMPILE_AND_EXECUTE");
// Cleanup
GLStateManager.glClearDepth(1.0);
GLStateManager.glDeleteLists(list, 1);
}
// ==================== BlendColor Tests ====================
@Test
void testBlendColorInDisplayList() {
int list = GL11.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glBlendColor(0.2f, 0.4f, 0.6f, 0.8f);
GLStateManager.glEndList();
// Call the display list - should not throw
GLStateManager.glCallList(list);
// Cleanup
GLStateManager.glBlendColor(0f, 0f, 0f, 0f);
GLStateManager.glDeleteLists(list, 1);
}
@Test
void testBlendColorCompileAndExecute() {
int list = GL11.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
GLStateManager.glBlendColor(0.5f, 0.5f, 0.5f, 1.0f);
GLStateManager.glEndList();
// Should not throw - display list was executed
GLStateManager.glCallList(list);
// Cleanup
GLStateManager.glBlendColor(0f, 0f, 0f, 0f);
GLStateManager.glDeleteLists(list, 1);
}
// ==================== FrontFace Tests ====================
@Test
void testFrontFaceInDisplayList() {
// Get initial front face
int initialMode = GL11.glGetInteger(GL11.GL_FRONT_FACE);
int list = GL11.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glFrontFace(GL11.GL_CW);
GLStateManager.glEndList();
// After compile, mode should be unchanged
assertEquals(initialMode, GL11.glGetInteger(GL11.GL_FRONT_FACE), "FrontFace should be unchanged after GL_COMPILE");
// Call the display list
GLStateManager.glCallList(list);
// Now mode should be GL_CW
assertEquals(GL11.GL_CW, GL11.glGetInteger(GL11.GL_FRONT_FACE), "FrontFace should be GL_CW after calling display list");
// Cleanup
GLStateManager.glFrontFace(GL11.GL_CCW); // Reset to default
GLStateManager.glDeleteLists(list, 1);
}
@Test
void testFrontFaceCompileAndExecute() {
int list = GL11.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
GLStateManager.glFrontFace(GL11.GL_CW);
GLStateManager.glEndList();
// After compile and execute, mode should be changed
assertEquals(GL11.GL_CW, GL11.glGetInteger(GL11.GL_FRONT_FACE), "FrontFace should be GL_CW after GL_COMPILE_AND_EXECUTE");
// Cleanup
GLStateManager.glFrontFace(GL11.GL_CCW);
GLStateManager.glDeleteLists(list, 1);
}
// ==================== Hint Tests ====================
@Test
void testHintInDisplayList() {
// Get initial hint value
int initialHint = GL11.glGetInteger(GL11.GL_LINE_SMOOTH_HINT);
int list = GL11.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST);
GLStateManager.glEndList();
// After compile, hint should be unchanged
assertEquals(initialHint, GL11.glGetInteger(GL11.GL_LINE_SMOOTH_HINT), "Hint should be unchanged after GL_COMPILE");
// Call the display list
GLStateManager.glCallList(list);
// Now hint should be GL_NICEST
assertEquals(GL11.GL_NICEST, GL11.glGetInteger(GL11.GL_LINE_SMOOTH_HINT), "Hint should be GL_NICEST after calling display list");
// Cleanup
GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_DONT_CARE); // Reset to default
GLStateManager.glDeleteLists(list, 1);
}
@Test
void testHintCompileAndExecute() {
int list = GL11.glGenLists(1);
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_FASTEST);
GLStateManager.glEndList();
// After compile and execute, hint should be changed
assertEquals(GL11.GL_FASTEST, GL11.glGetInteger(GL11.GL_LINE_SMOOTH_HINT), "Hint should be GL_FASTEST after GL_COMPILE_AND_EXECUTE");
// Cleanup
GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_DONT_CARE);
GLStateManager.glDeleteLists(list, 1);
}
}
@@ -3,12 +3,16 @@ package com.gtnewhorizons.angelica.glsm;
import org.joml.Matrix4f;
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.MethodSource;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -20,37 +24,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
@GLCompatTest
public class GLSM_GLU_UnitTest {
private static final FloatBuffer buffer = BufferUtils.createFloatBuffer(16);
private Matrix4f getModelviewMatrix() {
buffer.clear();
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, buffer);
return new Matrix4f(buffer);
return new Matrix4f(GlTestSupport.modelview());
}
private Matrix4f getProjectionMatrix() {
buffer.clear();
GLStateManager.glGetFloat(GL11.GL_PROJECTION_MATRIX, buffer);
return new Matrix4f(buffer);
return new Matrix4f(GlTestSupport.matrix(GL11.GL_PROJECTION_MATRIX));
}
private void assertMatrixEquals(Matrix4f expected, Matrix4f actual, String message) {
assertTrue(expected.equals(actual, 0.001f), () -> message + "\nExpected:\n" + expected + "\nActual:\n" + actual);
}
@Test
void gluPerspective_matchesJOML() {
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
GLStateManager.glLoadIdentity();
GLStateManager.gluPerspective(60.0f, 1.5f, 0.1f, 1000.0f);
Matrix4f expected = new Matrix4f().perspective((float) Math.toRadians(60.0f), 1.5f, 0.1f, 1000.0f);
assertMatrixEquals(expected, getProjectionMatrix(), "gluPerspective standard");
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
}
@Test
void gluPerspective_accumulates() {
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
@@ -67,40 +52,6 @@ public class GLSM_GLU_UnitTest {
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
}
@Test
void gluLookAt_matchesJOML() {
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
GLStateManager.glLoadIdentity();
GLStateManager.gluLookAt(
0.0f, 0.0f, 5.0f, // eye
0.0f, 0.0f, 0.0f, // center
0.0f, 1.0f, 0.0f); // up
Matrix4f expected = new Matrix4f().lookAt(
0.0f, 0.0f, 5.0f,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f);
assertMatrixEquals(expected, getModelviewMatrix(), "gluLookAt simple");
}
@Test
void gluLookAt_offAxis() {
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
GLStateManager.glLoadIdentity();
GLStateManager.gluLookAt(
3.0f, 4.0f, 5.0f,
1.0f, 2.0f, 0.0f,
0.0f, 1.0f, 0.0f);
Matrix4f expected = new Matrix4f().lookAt(
3.0f, 4.0f, 5.0f,
1.0f, 2.0f, 0.0f,
0.0f, 1.0f, 0.0f);
assertMatrixEquals(expected, getModelviewMatrix(), "gluLookAt off-axis");
}
@Test
void gluLookAt_accumulates() {
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
@@ -208,25 +159,23 @@ public class GLSM_GLU_UnitTest {
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
}
@Test
void gluErrorString_glErrors() {
assertEquals("No error", GLStateManager.gluErrorString(GL11.GL_NO_ERROR));
assertEquals("Invalid enum", GLStateManager.gluErrorString(GL11.GL_INVALID_ENUM));
assertEquals("Invalid value", GLStateManager.gluErrorString(GL11.GL_INVALID_VALUE));
assertEquals("Invalid operation", GLStateManager.gluErrorString(GL11.GL_INVALID_OPERATION));
assertEquals("Out of memory", GLStateManager.gluErrorString(GL11.GL_OUT_OF_MEMORY));
static Stream<Arguments> errorStrings() {
return Stream.of(
Arguments.of(GL11.GL_NO_ERROR, "No error"),
Arguments.of(GL11.GL_INVALID_ENUM, "Invalid enum"),
Arguments.of(GL11.GL_INVALID_VALUE, "Invalid value"),
Arguments.of(GL11.GL_INVALID_OPERATION, "Invalid operation"),
Arguments.of(GL11.GL_OUT_OF_MEMORY, "Out of memory"),
Arguments.of(100900, "Invalid enum (glu)"),
Arguments.of(100901, "Invalid value (glu)"),
Arguments.of(100902, "Out of memory (glu)"),
Arguments.of(99999, "Unknown error: 99999"));
}
@Test
void gluErrorString_gluErrors() {
assertEquals("Invalid enum (glu)", GLStateManager.gluErrorString(100900));
assertEquals("Invalid value (glu)", GLStateManager.gluErrorString(100901));
assertEquals("Out of memory (glu)", GLStateManager.gluErrorString(100902));
}
@Test
void gluErrorString_unknownCode() {
assertEquals("Unknown error: 99999", GLStateManager.gluErrorString(99999));
@ParameterizedTest(name = "{1}")
@MethodSource("errorStrings")
void gluErrorString(int code, String expected) {
assertEquals(expected, GLStateManager.gluErrorString(code));
}
@Test
@@ -2,11 +2,13 @@ package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.glsm.states.ImageUnitBinding;
import org.junit.jupiter.api.Test;
import org.lwjgl.opengl.ContextCapabilities;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL42;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -16,17 +18,12 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
@GLCoreTest
public class GLSM_ImageUnitBindingCache_UnitTest {
private static void drainErrors() {
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
private static void unbind(int unit) {
GLStateManager.glBindImageTexture(unit, 0, 0, false, 0, GL15.GL_READ_ONLY, 0);
GLStateManager.glBindImageTexture(unit, 0, 0, false, 0, GL15.GL_READ_ONLY, GL30.GL_R8);
}
@Test
void bindThenQueryReturnsFullTuple() {
drainErrors();
final int tex = GL11.glGenTextures();
try {
GLStateManager.glBindImageTexture(2, tex, 1, false, 3, GL15.GL_READ_ONLY, GL30.GL_RGBA32UI);
@@ -40,13 +37,11 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
} finally {
unbind(2);
GLStateManager.glDeleteTextures(tex);
drainErrors();
}
}
@Test
void saveRestoreRoundTripsTheWholeTuple() {
drainErrors();
final int a = GL11.glGenTextures();
final int b = GL11.glGenTextures();
try {
@@ -55,7 +50,7 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
assertNotNull(live);
final ImageUnitBinding saved = live.copy();
GLStateManager.glBindImageTexture(0, b, 0, true, 0, GL15.GL_WRITE_ONLY, 0);
GLStateManager.glBindImageTexture(0, b, 0, true, 0, GL15.GL_WRITE_ONLY, GL30.GL_R8);
assertEquals(b, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 0));
GLStateManager.glBindImageTexture(0, saved.getTexture(), saved.getLevel(), saved.isLayered(), saved.getLayer(), saved.getAccess(), saved.getFormat());
@@ -68,30 +63,26 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
unbind(0);
GLStateManager.glDeleteTextures(a);
GLStateManager.glDeleteTextures(b);
drainErrors();
}
}
@Test
void resetToZeroIsNotARestore() {
drainErrors();
final int tex = GL11.glGenTextures();
try {
GLStateManager.glBindImageTexture(0, tex, 1, true, 0, GL15.GL_READ_WRITE, GL30.GL_R32UI);
GLStateManager.glBindImageTexture(0, 0, 0, true, 0, GL15.GL_WRITE_ONLY, 0);
GLStateManager.glBindImageTexture(0, 0, 0, true, 0, GL15.GL_WRITE_ONLY, GL30.GL_R8);
assertEquals(0, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 0));
assertEquals(0, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_LEVEL, 0));
} finally {
unbind(0);
GLStateManager.glDeleteTextures(tex);
drainErrors();
}
}
@Test
void deletingTheBoundTextureClearsTheImageUnit() {
drainErrors();
final int tex = GL11.glGenTextures();
try {
GLStateManager.glBindImageTexture(1, tex, 0, true, 0, GL15.GL_READ_WRITE, GL30.GL_R32UI);
@@ -101,13 +92,11 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
assertEquals(0, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 1), "GL unbinds a deleted texture from every image unit");
} finally {
unbind(1);
drainErrors();
}
}
@Test
void unitsAreIndependentAndUnitZeroIsNotSpecial() {
drainErrors();
final int a = GL11.glGenTextures();
final int b = GL11.glGenTextures();
try {
@@ -123,13 +112,11 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
unbind(3);
GLStateManager.glDeleteTextures(a);
GLStateManager.glDeleteTextures(b);
drainErrors();
}
}
@Test
void outOfRangeUnitDoesNotCorruptNeighbours() {
drainErrors();
final int tex = GL11.glGenTextures();
try {
GLStateManager.glBindImageTexture(0, tex, 0, true, 0, GL15.GL_READ_ONLY, GL30.GL_R32UI);
@@ -137,11 +124,14 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
assertFalse(beyond == 0, "unit 0 must resolve for this test to mean anything");
GLStateManager.glBindImageTexture(beyond, tex, 0, true, 0, GL15.GL_READ_ONLY, GL30.GL_R32UI);
// Without image load/store the backend drops the call, so the driver never sees the unit.
final ContextCapabilities caps = GLStateManager.capabilities;
final boolean reachesDriver = caps.OpenGL42 || caps.GL_ARB_shader_image_load_store || caps.GL_EXT_shader_image_load_store;
assertEquals(reachesDriver ? GL11.GL_INVALID_VALUE : GL11.GL_NO_ERROR, GL11.glGetError(), "driver must reject the out-of-range unit");
assertEquals(tex, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 0));
} finally {
unbind(0);
GLStateManager.glDeleteTextures(tex);
drainErrors();
}
}
@@ -151,9 +141,9 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
drainErrors();
final int tex = GL11.glGenTextures();
try {
GL11.glBindTexture(GL11.GL_TEXTURE_2D, tex);
GL42.glTexStorage2D(GL11.GL_TEXTURE_2D, 1, GL30.GL_R32UI, 4, 4);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex);
GLStateManager.glTexStorage2D(GL11.GL_TEXTURE_2D, 1, GL30.GL_R32UI, 4, 4);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GLStateManager.glBindImageTexture(1, tex, 0, false, 0, GL15.GL_READ_ONLY, GL30.GL_R32UI);
drainErrors();
@@ -164,7 +154,6 @@ public class GLSM_ImageUnitBindingCache_UnitTest {
} finally {
unbind(1);
GLStateManager.glDeleteTextures(tex);
drainErrors();
}
}
}
@@ -1,22 +1,34 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
import com.gtnewhorizons.angelica.glsm.ffp.VertexKey;
import com.gtnewhorizons.angelica.glsm.states.PixelStoreState;
import org.junit.jupiter.api.Test;
import org.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.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL21;
import org.lwjgl.opengl.GL30;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.stream.Stream;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.HINT_TARGETS;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.assertEnableRoundTrip;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.drainClientAttribStack;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.hex;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.legacyPnames;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.popAttribTo;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.setEnabled;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -27,44 +39,12 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
@GLCoreTest
public class GLSM_LegacyStackQueries_GLTest {
private static final int[] SINGLE_VALUE_QUERIES = {
GL11.GL_ATTRIB_STACK_DEPTH,
GL11.GL_CLIENT_ATTRIB_STACK_DEPTH,
GL11.GL_TEXTURE_STACK_DEPTH,
GL11.GL_MAX_ATTRIB_STACK_DEPTH,
GL11.GL_MAX_CLIENT_ATTRIB_STACK_DEPTH,
GL11.GL_MAX_MODELVIEW_STACK_DEPTH,
GL11.GL_MAX_PROJECTION_STACK_DEPTH,
GL11.GL_MAX_TEXTURE_STACK_DEPTH,
GL13.GL_MAX_TEXTURE_UNITS,
GL11.GL_MAX_LIGHTS,
GL11.GL_STENCIL_BITS,
GL11.GL_DEPTH_BITS,
};
private static final int[] LEGACY_PNAMES = legacyPnames();
private static boolean positionArrayEnabled() {
final VAOManager.Attrib position = GLStateManager.ctx().vaos.attrib(Usage.POSITION.getAttributeLocation());
return position != null && position.enabled;
}
private static boolean colorArrayEnabled() {
return (GLStateManager.ctx().vaos.getVertexFlags() & VertexFlags.COLOR_BIT) != 0;
}
private static void drainErrors() {
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
private static void drainClientAttribStack() {
while (GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH) > 0) {
GLStateManager.glPopClientAttrib();
}
}
private static void drainAttribStack() {
while (GLStateManager.getAttribDepth() > 0) {
GLStateManager.glPopAttrib();
}
private static void assumeDriverRejectsLegacyPnames() {
drainErrors();
GL11.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH);
assumeTrue(GL11.glGetError() != GL11.GL_NO_ERROR, "driver accepts GL_ATTRIB_STACK_DEPTH, so a clean error state proves nothing");
}
private static int pushToLimit(int depthPname, int maxPname) {
@@ -95,9 +75,7 @@ public class GLSM_LegacyStackQueries_GLTest {
assertEquals(base, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH));
assertEquals(GLStateManager.MAX_ATTRIB_STACK_DEPTH, GLStateManager.glGetInteger(GL11.GL_MAX_ATTRIB_STACK_DEPTH));
} finally {
while (GLStateManager.getAttribDepth() > base) {
GLStateManager.glPopAttrib();
}
popAttribTo(base);
}
}
@@ -150,42 +128,24 @@ public class GLSM_LegacyStackQueries_GLTest {
}
}
@Test
void maxModelviewStackDepthIsExactlyReachable() {
try {
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_MODELVIEW_STACK_DEPTH));
assertEquals(GLStateManager.glGetInteger(GL11.GL_MAX_MODELVIEW_STACK_DEPTH), pushToLimit(GL11.GL_MODELVIEW_STACK_DEPTH, GL11.GL_MAX_MODELVIEW_STACK_DEPTH));
} finally {
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
popToBase(GL11.GL_MODELVIEW_STACK_DEPTH);
}
static Stream<Arguments> matrixStacks() {
return Stream.of(
Arguments.of("modelview", GL11.GL_MODELVIEW, GL11.GL_MODELVIEW_STACK_DEPTH, GL11.GL_MAX_MODELVIEW_STACK_DEPTH),
Arguments.of("projection", GL11.GL_PROJECTION, GL11.GL_PROJECTION_STACK_DEPTH, GL11.GL_MAX_PROJECTION_STACK_DEPTH),
Arguments.of("texture", GL11.GL_TEXTURE, GL11.GL_TEXTURE_STACK_DEPTH, GL11.GL_MAX_TEXTURE_STACK_DEPTH));
}
@Test
void maxProjectionStackDepthIsExactlyReachable() {
try {
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_PROJECTION_STACK_DEPTH));
assertEquals(GLStateManager.glGetInteger(GL11.GL_MAX_PROJECTION_STACK_DEPTH), pushToLimit(GL11.GL_PROJECTION_STACK_DEPTH, GL11.GL_MAX_PROJECTION_STACK_DEPTH));
} finally {
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
popToBase(GL11.GL_PROJECTION_STACK_DEPTH);
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
}
}
@Test
void maxTextureStackDepthIsExactlyReachable() {
@ParameterizedTest(name = "{0}")
@MethodSource("matrixStacks")
void maxMatrixStackDepthIsExactlyReachable(String name, int mode, int depthPname, int maxPname) {
try {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_TEXTURE_STACK_DEPTH));
assertEquals(GLStateManager.glGetInteger(GL11.GL_MAX_TEXTURE_STACK_DEPTH), pushToLimit(GL11.GL_TEXTURE_STACK_DEPTH, GL11.GL_MAX_TEXTURE_STACK_DEPTH));
GLStateManager.glMatrixMode(mode);
assertEquals(1, GLStateManager.glGetInteger(depthPname));
assertEquals(GLStateManager.glGetInteger(maxPname), pushToLimit(depthPname, maxPname));
} finally {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glMatrixMode(GL11.GL_TEXTURE);
popToBase(GL11.GL_TEXTURE_STACK_DEPTH);
GLStateManager.glMatrixMode(mode);
popToBase(depthPname);
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
}
}
@@ -203,14 +163,12 @@ public class GLSM_LegacyStackQueries_GLTest {
final int savedRead = glCtx.readFramebuffer;
try {
GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
drainErrors();
assertEquals(
GL30.glGetFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL11.GL_STENCIL, GL30.GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE),
GLStateManager.glGetInteger(GL11.GL_STENCIL_BITS));
assertEquals(
GL30.glGetFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL11.GL_DEPTH, GL30.GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE),
GLStateManager.glGetInteger(GL11.GL_DEPTH_BITS));
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
} finally {
GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, savedDraw);
GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, savedRead);
@@ -226,7 +184,6 @@ public class GLSM_LegacyStackQueries_GLTest {
final int depthOnly = GLStateManager.glGenRenderbuffers();
final int depthStencil = GLStateManager.glGenRenderbuffers();
try {
drainErrors();
GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depthOnly);
GLStateManager.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL14.GL_DEPTH_COMPONENT24, 16, 16);
GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depthStencil);
@@ -247,7 +204,6 @@ public class GLSM_LegacyStackQueries_GLTest {
GLStateManager.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, depthStencil);
assertEquals(8, GLStateManager.glGetInteger(GL11.GL_STENCIL_BITS));
assertEquals(24, GLStateManager.glGetInteger(GL11.GL_DEPTH_BITS));
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
} finally {
GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, savedDraw);
GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, savedRead);
@@ -257,33 +213,6 @@ public class GLSM_LegacyStackQueries_GLTest {
}
}
@Test
void intBufferOverloadMatchesAndDriverIsNotAsked() {
final int base = GLStateManager.getAttribDepth();
final IntBuffer buf = BufferUtils.createIntBuffer(16);
try {
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
drainErrors();
GL11.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH);
assumeTrue(GL11.glGetError() != GL11.GL_NO_ERROR, "driver accepts GL_ATTRIB_STACK_DEPTH, so a clean error state proves nothing");
for (final int pname : SINGLE_VALUE_QUERIES) {
final int direct = GLStateManager.glGetInteger(pname);
buf.put(0, -12345);
GLStateManager.glGetInteger(pname, buf);
assertEquals(direct, buf.get(0), "IntBuffer overload for 0x" + Integer.toHexString(pname));
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "driver error after querying 0x" + Integer.toHexString(pname));
}
} finally {
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
drainClientAttribStack();
while (GLStateManager.getAttribDepth() > base) {
GLStateManager.glPopAttrib();
}
}
}
@Test
void attribDepthUnchangedWhileCompilingDisplayList() {
final int base = GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH);
@@ -306,14 +235,12 @@ public class GLSM_LegacyStackQueries_GLTest {
@Test
void popAttribOnEmptyStackIsIgnored() {
drainAttribStack();
drainErrors();
popAttribTo(0);
assertEquals(0, GLStateManager.getAttribDepth());
assertDoesNotThrow(GLStateManager::glPopAttrib);
assertEquals(0, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH));
assertDoesNotThrow(GLStateManager::glPopAttrib);
assertEquals(0, GLStateManager.getAttribDepth());
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
}
@Test
@@ -322,8 +249,11 @@ public class GLSM_LegacyStackQueries_GLTest {
final int unpackBuffer = GLStateManager.glGenBuffers();
final int packBuffer = GLStateManager.glGenBuffers();
try {
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4);
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
// Pack and unpack start from different values, so a pop that crosses them is caught
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2);
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 8);
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 3);
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 5);
GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 0);
GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
@@ -332,31 +262,41 @@ public class GLSM_LegacyStackQueries_GLTest {
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 2);
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 6);
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 7);
GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 7);
GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, unpackBuffer);
GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, packBuffer);
assertEquals(1, glCtx.pixelUnpackState.alignment());
assertEquals(2, glCtx.pixelPackState.alignment());
assertEquals(4, glCtx.pixelPackState.alignment());
assertEquals(6, glCtx.pixelUnpackState.skipRows());
assertEquals(7, glCtx.pixelPackState.skipRows());
assertEquals(7, glCtx.pixelPackState.rowLength());
assertEquals(2, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT));
assertEquals(4, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT));
assertEquals(6, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS));
assertEquals(7, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS));
GLStateManager.glPopClientAttrib();
assertSame(unpackBefore, glCtx.pixelUnpackState);
assertSame(packBefore, glCtx.pixelPackState);
assertEquals(4, GLStateManager.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
assertEquals(2, GLStateManager.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
assertEquals(8, GLStateManager.glGetInteger(GL11.GL_PACK_ALIGNMENT));
assertEquals(0, GLStateManager.glGetInteger(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING));
assertEquals(0, GLStateManager.glGetInteger(GL21.GL_PIXEL_PACK_BUFFER_BINDING));
assertEquals(4, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
assertEquals(4, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT));
assertEquals(2, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
assertEquals(8, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT));
assertEquals(3, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS));
assertEquals(5, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS));
assertEquals(0, GL11.glGetInteger(GL11.GL_PACK_ROW_LENGTH));
assertEquals(0, GL11.glGetInteger(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING));
assertEquals(0, GL11.glGetInteger(GL21.GL_PIXEL_PACK_BUFFER_BINDING));
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
} finally {
drainClientAttribStack();
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4);
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0);
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 0);
GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 0);
GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);
GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0);
@@ -428,122 +368,6 @@ public class GLSM_LegacyStackQueries_GLTest {
}
}
@Test
void packAndUnpackRestoreToTheirOwnPriorValues() {
final GLContextState glCtx = GLStateManager.ctx();
try {
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2);
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 8);
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 3);
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 5);
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 6);
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 7);
assertEquals(1, glCtx.pixelUnpackState.alignment());
assertEquals(4, glCtx.pixelPackState.alignment());
assertEquals(6, glCtx.pixelUnpackState.skipRows());
assertEquals(7, glCtx.pixelPackState.skipRows());
assertEquals(6, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS));
assertEquals(7, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS));
GLStateManager.glPopClientAttrib();
assertEquals(2, glCtx.pixelUnpackState.alignment());
assertEquals(8, glCtx.pixelPackState.alignment());
assertEquals(3, glCtx.pixelUnpackState.skipRows());
assertEquals(5, glCtx.pixelPackState.skipRows());
assertEquals(2, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT));
assertEquals(8, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT));
assertEquals(3, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS));
assertEquals(5, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS));
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
} finally {
drainClientAttribStack();
GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4);
GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4);
GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0);
GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 0);
}
}
@Test
void vertexArrayEnableRestoredOnlyWithVertexArrayBit() {
final int savedVao = GLStateManager.getBoundVAO();
final int vao = GLStateManager.glGenVertexArrays();
final IntBuffer enabled = BufferUtils.createIntBuffer(16);
final int position = Usage.POSITION.getAttributeLocation();
try {
GLStateManager.glBindVertexArray(vao);
drainErrors();
assertFalse(positionArrayEnabled());
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
assertTrue(positionArrayEnabled());
GLStateManager.glPopClientAttrib();
assertFalse(positionArrayEnabled());
GL20.glGetVertexAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED, enabled);
assertEquals(GL11.GL_FALSE, enabled.get(0));
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glPopClientAttrib();
assertTrue(positionArrayEnabled());
GL20.glGetVertexAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED, enabled);
assertEquals(GL11.GL_TRUE, enabled.get(0));
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glPopClientAttrib();
assertTrue(positionArrayEnabled());
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
} finally {
drainClientAttribStack();
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glBindVertexArray(savedVao);
GLStateManager.glDeleteVertexArrays(vao);
}
}
@Test
void vertexArrayStateRestoredOnlyWithVertexArrayBit() {
final int savedVao = GLStateManager.getBoundVAO();
final int vao = GLStateManager.glGenVertexArrays();
try {
GLStateManager.glBindVertexArray(vao);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
final int flagsBefore = GLStateManager.ctx().vaos.getVertexFlags();
assertFalse(colorArrayEnabled());
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
assertTrue(colorArrayEnabled());
GLStateManager.glPopClientAttrib();
assertFalse(colorArrayEnabled());
assertEquals(flagsBefore, GLStateManager.ctx().vaos.getVertexFlags());
assertEquals(0, GLStateManager.getClientActiveTextureUnit());
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
GLStateManager.glPopClientAttrib();
assertTrue(colorArrayEnabled());
assertEquals(1, GLStateManager.getClientActiveTextureUnit());
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
} finally {
drainClientAttribStack();
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY);
GLStateManager.glBindVertexArray(savedVao);
GLStateManager.glDeleteVertexArrays(vao);
}
}
@Test
void intLightGettersConvertLikeMesa() {
final FloatBuffer savedDiffuse = BufferUtils.createFloatBuffer(16);
@@ -570,7 +394,6 @@ public class GLSM_LegacyStackQueries_GLTest {
GLStateManager.glLight(GL11.GL_LIGHT1, GL11.GL_SPOT_DIRECTION, values);
GLStateManager.glLightf(GL11.GL_LIGHT1, GL11.GL_SPOT_CUTOFF, 45.75f);
GLStateManager.glLightf(GL11.GL_LIGHT1, GL11.GL_LINEAR_ATTENUATION, 2.5f);
drainErrors();
ints.position(2);
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_DIFFUSE, ints);
@@ -606,7 +429,6 @@ public class GLSM_LegacyStackQueries_GLTest {
assertEquals(-777, ints.get(0));
GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_SHININESS, ints);
assertEquals(-777, ints.get(0));
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
} finally {
GLStateManager.glLoadIdentity();
savedDiffuse.limit(4);
@@ -637,7 +459,6 @@ public class GLSM_LegacyStackQueries_GLTest {
GLStateManager.glMaterialf(GL11.GL_FRONT, GL11.GL_SHININESS, 12.6f);
values.put(0, 2.5f).put(1, -2.5f).put(2, 7.4f).put(3, 0.0f);
GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_COLOR_INDEXES, values);
drainErrors();
ints.position(1);
GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, ints);
@@ -665,7 +486,6 @@ public class GLSM_LegacyStackQueries_GLTest {
assertEquals(-777, ints.get(0));
GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SPOT_CUTOFF, ints);
assertEquals(-777, ints.get(0));
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
} finally {
savedSpecular.limit(4);
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, savedSpecular);
@@ -691,7 +511,6 @@ public class GLSM_LegacyStackQueries_GLTest {
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE1, 240.0f, 16.0f);
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE2, 0.75f, 0.125f);
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE3, 0.375f, 0.625f);
drainErrors();
final float[][] expected = { { 0.25f, 0.5f }, { 240.0f, 16.0f }, { 0.75f, 0.125f }, { 0.375f, 0.625f } };
for (int unit = 0; unit < 4; unit++) {
@@ -702,7 +521,6 @@ public class GLSM_LegacyStackQueries_GLTest {
assertEquals(0.0f, buf.get(2), "r of unit " + unit);
assertEquals(1.0f, buf.get(3), "q of unit " + unit);
}
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
} finally {
for (int unit = 0; unit < 4; unit++) {
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0 + unit, saved[unit][0], saved[unit][1]);
@@ -719,7 +537,6 @@ public class GLSM_LegacyStackQueries_GLTest {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0, 0.25f, 0.5f);
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit);
drainErrors();
for (int i = 0; i < 4; i++) {
buf.put(i, -7.0f);
}
@@ -728,10 +545,481 @@ public class GLSM_LegacyStackQueries_GLTest {
assertEquals(0.0f, buf.get(1));
assertEquals(0.0f, buf.get(2));
assertEquals(1.0f, buf.get(3));
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
} finally {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0, 0.0f, 0.0f);
}
}
@Test
void legacyQueriesNeverReachTheDriver() {
final int base = GLStateManager.getAttribDepth();
final IntBuffer ints = BufferUtils.createIntBuffer(16);
final FloatBuffer floats = BufferUtils.createFloatBuffer(16);
final DoubleBuffer doubles = BufferUtils.createDoubleBuffer(16);
final ByteBuffer bytes = BufferUtils.createByteBuffer(16);
final int[] intArray = new int[16];
final float[] floatArray = new float[16];
final double[] doubleArray = new double[16];
try {
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
assumeDriverRejectsLegacyPnames();
for (final int pname : LEGACY_PNAMES) {
drainErrors();
final int scalar = GLStateManager.glGetInteger(pname);
GLStateManager.glGetFloat(pname);
GLStateManager.glGetBoolean(pname);
GLStateManager.glIsEnabled(pname);
ints.clear();
ints.put(0, -12345);
GLStateManager.glGetInteger(pname, ints);
floats.clear();
GLStateManager.glGetFloat(pname, floats);
doubles.clear();
GLStateManager.glGetDouble(pname, doubles);
bytes.clear();
GLStateManager.glGetBoolean(pname, bytes);
intArray[0] = -12345;
GLStateManager.glGetInteger(pname, intArray);
GLStateManager.glGetFloat(pname, floatArray);
GLStateManager.glGetDouble(pname, doubleArray);
if (LegacyQueries.count(LegacyQueries.desc(pname)) == 1) {
assertEquals(scalar, ints.get(0), "IntBuffer overload for " + hex(pname));
assertEquals(scalar, intArray[0], "int[] overload for " + hex(pname));
}
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "driver error after querying " + hex(pname));
}
} finally {
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
drainClientAttribStack();
popAttribTo(base);
}
}
@Test
void typedEntryPointsAgreeUnderMesaConversions() {
final IntBuffer ints = BufferUtils.createIntBuffer(16);
final FloatBuffer floats = BufferUtils.createFloatBuffer(16);
final DoubleBuffer doubles = BufferUtils.createDoubleBuffer(16);
final FloatBuffer savedColor = BufferUtils.createFloatBuffer(16);
final FloatBuffer savedNormal = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_CURRENT_COLOR, savedColor);
GLStateManager.glGetFloat(GL11.GL_CURRENT_NORMAL, savedNormal);
final int savedFunc = GLStateManager.glGetInteger(GL11.GL_ALPHA_TEST_FUNC);
final float savedRef = GLStateManager.glGetFloat(GL11.GL_ALPHA_TEST_REF);
final float savedStart = GLStateManager.glGetFloat(GL11.GL_FOG_START);
final float savedEnd = GLStateManager.glGetFloat(GL11.GL_FOG_END);
final float savedDensity = GLStateManager.glGetFloat(GL11.GL_FOG_DENSITY);
final boolean savedLighting = GLStateManager.glIsEnabled(GL11.GL_LIGHTING);
final boolean savedFog = GLStateManager.glIsEnabled(GL11.GL_FOG);
final int savedShade = GLStateManager.glGetInteger(GL11.GL_SHADE_MODEL);
final int savedHint = GLStateManager.glGetInteger(GL11.GL_FOG_HINT);
final float savedMinSize = GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN);
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
GLStateManager.glPushMatrix();
try {
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f);
GLStateManager.glFogf(GL11.GL_FOG_START, -1.5f);
GLStateManager.glFogf(GL11.GL_FOG_END, 2.5f);
GLStateManager.glFogf(GL11.GL_FOG_DENSITY, 0.25f);
GLStateManager.glColor4f(0.25f, 0.5f, 0.75f, 1f);
GLStateManager.glNormal3f(0f, -0.5f, 1f);
GLStateManager.glEnable(GL11.GL_LIGHTING);
GLStateManager.glEnable(GL11.GL_FOG);
GLStateManager.glShadeModel(GL11.GL_FLAT);
GLStateManager.glHint(GL11.GL_FOG_HINT, GL11.GL_NICEST);
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, 2.5f);
GLStateManager.glTranslatef(1.5f, -2.5f, 3f);
for (final int pname : LEGACY_PNAMES) {
final int desc = LegacyQueries.desc(pname);
final int type = LegacyQueries.type(desc);
final int count = LegacyQueries.count(desc);
final String name = hex(pname);
if (count == 1) {
if (type == LegacyQueries.BOOL) {
final boolean b = GLStateManager.glGetBoolean(pname);
assertEquals(b ? 1 : 0, GLStateManager.glGetInteger(pname), "int of bool " + name);
assertEquals(b ? 1.0f : 0.0f, GLStateManager.glGetFloat(pname), "float of bool " + name);
} else if (type == LegacyQueries.INT) {
final int i = GLStateManager.glGetInteger(pname);
assertEquals((float) i, GLStateManager.glGetFloat(pname), "float of int " + name);
assertEquals(i != 0, GLStateManager.glGetBoolean(pname), "bool of int " + name);
} else {
final float f = GLStateManager.glGetFloat(pname);
assertEquals(LegacyQueries.toInt(type, f), GLStateManager.glGetInteger(pname), "int of float " + name);
assertEquals(f != 0.0f, GLStateManager.glGetBoolean(pname), "bool of float " + name);
}
} else {
floats.clear();
GLStateManager.glGetFloat(pname, floats);
ints.clear();
GLStateManager.glGetInteger(pname, ints);
doubles.clear();
GLStateManager.glGetDouble(pname, doubles);
for (int i = 0; i < count; i++) {
final float f = floats.get(i);
assertEquals(LegacyQueries.toInt(type, f), ints.get(i), "int element " + i + " of " + name);
assertEquals((double) f, doubles.get(i), "double element " + i + " of " + name);
}
assertEquals(floats.get(0), GLStateManager.glGetFloat(pname), "scalar float of " + name);
}
}
} finally {
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
GLStateManager.glPopMatrix();
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, savedMinSize);
GLStateManager.glHint(GL11.GL_FOG_HINT, savedHint);
GLStateManager.glShadeModel(savedShade);
setEnabled(GL11.GL_FOG, savedFog);
setEnabled(GL11.GL_LIGHTING, savedLighting);
GLStateManager.glNormal3f(savedNormal.get(0), savedNormal.get(1), savedNormal.get(2));
GLStateManager.glColor4f(savedColor.get(0), savedColor.get(1), savedColor.get(2), savedColor.get(3));
GLStateManager.glFogf(GL11.GL_FOG_DENSITY, savedDensity);
GLStateManager.glFogf(GL11.GL_FOG_END, savedEnd);
GLStateManager.glFogf(GL11.GL_FOG_START, savedStart);
GLStateManager.glAlphaFunc(savedFunc, savedRef);
}
}
private static float driverMaxPointSize() {
final FloatBuffer range = BufferUtils.createFloatBuffer(16);
GL11.glGetFloat(GL11.GL_POINT_SIZE_RANGE, range);
final float smooth = range.get(1);
// A rejected query leaves the buffer alone, so the smooth maximum stands in for the missing aliased range
GL11.glGetFloat(GL12.GL_ALIASED_POINT_SIZE_RANGE, range);
drainErrors();
return Math.max(smooth, range.get(1));
}
@Test
void edgeFlagIsCompiledIntoDisplayLists() {
final int list = GLStateManager.glGenLists(1);
try {
GLStateManager.glEdgeFlag(true);
GLStateManager.glNewList(list, GL11.GL_COMPILE);
GLStateManager.glEdgeFlag(false);
GLStateManager.glEndList();
assertTrue(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG), "compiling must not change the live edge flag");
GLStateManager.glCallList(list);
assertFalse(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG));
} finally {
GLStateManager.glEdgeFlag(true);
GLStateManager.glDeleteLists(list, 1);
}
}
@Test
void textureBitRestoresPerUnitTextureEnables() {
final int base = GLStateManager.getAttribDepth();
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
final boolean cube = GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP);
final boolean texture2d = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
try {
GLStateManager.glPushAttrib(GL11.GL_TEXTURE_BIT);
setEnabled(GL13.GL_TEXTURE_CUBE_MAP, !cube);
setEnabled(GL11.GL_TEXTURE_2D, !texture2d);
GLStateManager.glPopAttrib();
assertEquals(cube, GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP));
assertEquals(texture2d, GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D));
} finally {
popAttribTo(base);
setEnabled(GL13.GL_TEXTURE_CUBE_MAP, cube);
setEnabled(GL11.GL_TEXTURE_2D, texture2d);
}
}
@Test
void crossTypeQueriesConvert() {
final FloatBuffer savedColor = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_CURRENT_COLOR, savedColor);
final int savedFunc = GLStateManager.glGetInteger(GL11.GL_ALPHA_TEST_FUNC);
final float savedRef = GLStateManager.glGetFloat(GL11.GL_ALPHA_TEST_REF);
final float savedStart = GLStateManager.glGetFloat(GL11.GL_FOG_START);
final boolean savedLighting = GLStateManager.glIsEnabled(GL11.GL_LIGHTING);
final IntBuffer ints = BufferUtils.createIntBuffer(16);
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
GLStateManager.glPushMatrix();
try {
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f);
assertEquals(1073741823, GLStateManager.glGetInteger(GL11.GL_ALPHA_TEST_REF));
assertEquals((float) GLStateManager.glGetInteger(GL11.GL_FOG_MODE), GLStateManager.glGetFloat(GL11.GL_FOG_MODE));
GLStateManager.glFogf(GL11.GL_FOG_START, -1.5f);
assertEquals(-2, GLStateManager.glGetInteger(GL11.GL_FOG_START));
GLStateManager.glEnable(GL11.GL_LIGHTING);
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_LIGHTING));
GLStateManager.glDisable(GL11.GL_LIGHTING);
assertEquals(0, GLStateManager.glGetInteger(GL11.GL_LIGHTING));
GLStateManager.glColor4f(1f, 0.5f, 0f, 1f);
ints.clear();
GLStateManager.glGetInteger(GL11.GL_CURRENT_COLOR, ints);
assertEquals(2147483647, ints.get(0));
assertEquals(1073741823, ints.get(1));
assertEquals(0, ints.get(2));
assertEquals(2147483647, ints.get(3));
GLStateManager.glLoadIdentity();
GLStateManager.glGetInteger(GL11.GL_MODELVIEW_MATRIX, ints);
assertEquals(2147483647, ints.get(0), "matrix elements scale like normalized floats");
assertEquals(0, ints.get(1));
assertEquals(2147483647, ints.get(15));
assertEquals(2147483647, GLStateManager.glGetInteger(GL11.GL_MODELVIEW_MATRIX));
assertEquals(1.0f, GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX));
} finally {
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
GLStateManager.glPopMatrix();
GLStateManager.glFogf(GL11.GL_FOG_START, savedStart);
GLStateManager.glColor4f(savedColor.get(0), savedColor.get(1), savedColor.get(2), savedColor.get(3));
GLStateManager.glAlphaFunc(savedFunc, savedRef);
setEnabled(GL11.GL_LIGHTING, savedLighting);
}
}
@Test
void conversionHelpersMatchMesa() {
assertEquals(1, LegacyQueries.lround(0.5f));
assertEquals(-1, LegacyQueries.lround(-0.5f));
assertEquals(2, LegacyQueries.lround(2.4f));
assertEquals(-3, LegacyQueries.lround(-2.6f));
assertEquals(Integer.MAX_VALUE, LegacyQueries.floatToInt(1f));
assertEquals(0, LegacyQueries.floatToInt(0f));
assertEquals(-Integer.MAX_VALUE, LegacyQueries.floatToInt(-1f));
}
@Test
void queriesWriteAtBufferPosition() {
final IntBuffer ints = BufferUtils.createIntBuffer(32);
final FloatBuffer floats = BufferUtils.createFloatBuffer(32);
final DoubleBuffer doubles = BufferUtils.createDoubleBuffer(32);
final FloatBuffer reference = BufferUtils.createFloatBuffer(32);
final FloatBuffer savedColor = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_CURRENT_COLOR, savedColor);
final float savedStart = GLStateManager.glGetFloat(GL11.GL_FOG_START);
try {
for (int i = 0; i < 32; i++) {
ints.put(i, -777);
floats.put(i, -777f);
doubles.put(i, -777.0);
}
ints.position(4);
GLStateManager.glGetInteger(GL11.GL_MATRIX_MODE, ints);
assertEquals(GLStateManager.glGetInteger(GL11.GL_MATRIX_MODE), ints.get(4));
assertEquals(-777, ints.get(0));
floats.position(4);
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, floats);
reference.clear();
GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, reference);
for (int i = 0; i < 16; i++) {
assertEquals(reference.get(i), floats.get(4 + i), "modelview element " + i);
}
assertEquals(-777f, floats.get(0));
for (int i = 0; i < 32; i++) {
ints.put(i, -777);
}
GLStateManager.glColor4f(0.25f, 0.5f, 0.75f, 1f);
ints.position(4);
GLStateManager.glGetInteger(GL11.GL_CURRENT_COLOR, ints);
assertEquals(536870911, ints.get(4));
assertEquals(1073741823, ints.get(5));
assertEquals(1610612735, ints.get(6));
assertEquals(2147483647, ints.get(7));
assertEquals(-777, ints.get(0));
GLStateManager.glFogf(GL11.GL_FOG_START, 3.5f);
doubles.position(4);
GLStateManager.glGetDouble(GL11.GL_FOG_START, doubles);
assertEquals(3.5, doubles.get(4));
assertEquals(-777.0, doubles.get(0));
for (int i = 0; i < 32; i++) {
floats.put(i, -777f);
}
floats.position(4);
GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE, floats);
reference.clear();
GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE, reference);
for (int i = 0; i < 4; i++) {
assertEquals(reference.get(i), floats.get(4 + i), "diffuse element " + i);
}
assertEquals(-777f, floats.get(0));
} finally {
GLStateManager.glFogf(GL11.GL_FOG_START, savedStart);
GLStateManager.glColor4f(savedColor.get(0), savedColor.get(1), savedColor.get(2), savedColor.get(3));
}
}
@Test
void hintsRoundTripAndFollowHintBit() {
final int base = GLStateManager.getAttribDepth();
final int[] saved = new int[HINT_TARGETS.length];
for (int i = 0; i < HINT_TARGETS.length; i++) {
saved[i] = GLStateManager.glGetInteger(HINT_TARGETS[i]);
}
try {
for (final int target : HINT_TARGETS) {
final String name = hex(target);
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);
GLStateManager.glHint(target, 12345);
assertEquals(GL11.GL_NICEST, GLStateManager.glGetInteger(target), "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));
}
GLStateManager.glPopAttrib();
for (final int target : HINT_TARGETS) {
assertEquals(GL11.GL_NICEST, GLStateManager.glGetInteger(target), "restored " + hex(target));
}
assertEquals(GL11.GL_RENDER, GLStateManager.glGetInteger(GL11.GL_RENDER_MODE));
} finally {
popAttribTo(base);
for (int i = 0; i < HINT_TARGETS.length; i++) {
GLStateManager.glHint(HINT_TARGETS[i], saved[i]);
}
}
}
@Test
void pointParametersRoundTripAndFollowPointBit() {
final int base = GLStateManager.getAttribDepth();
final FloatBuffer attenuation = BufferUtils.createFloatBuffer(16);
final FloatBuffer savedAttenuation = BufferUtils.createFloatBuffer(16);
final float savedMin = GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN);
final float savedMax = GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX);
final float savedFade = GLStateManager.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE);
final int savedOrigin = GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN);
GLStateManager.glGetFloat(GL14.GL_POINT_DISTANCE_ATTENUATION, savedAttenuation);
GLStateManager.glPushAttrib(GL11.GL_POINT_BIT);
try {
assertEquals(0f, savedMin);
assertEquals(driverMaxPointSize(), savedMax);
assertEquals(1f, savedFade);
assertEquals(1f, savedAttenuation.get(0));
assertEquals(0f, savedAttenuation.get(1));
assertEquals(0f, savedAttenuation.get(2));
assertEquals(GL20.GL_UPPER_LEFT, savedOrigin);
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, 0.5f);
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MAX, 2.5f);
GLStateManager.glPointParameterf(GL14.GL_POINT_FADE_THRESHOLD_SIZE, 4.5f);
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));
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, -1f);
assertEquals(0.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN));
attenuation.put(0, 0.5f).put(1, 0.25f).put(2, 0.125f);
attenuation.position(0).limit(3);
GLStateManager.glPointParameter(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation);
attenuation.clear();
for (int i = 0; i < 16; i++) {
attenuation.put(i, -777f);
}
GLStateManager.glGetFloat(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation);
assertEquals(0.5f, attenuation.get(0));
assertEquals(0.25f, attenuation.get(1));
assertEquals(0.125f, attenuation.get(2));
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));
GLStateManager.glPushAttrib(GL11.GL_POINT_BIT);
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, 1f);
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MAX, 3f);
GLStateManager.glPointParameterf(GL14.GL_POINT_FADE_THRESHOLD_SIZE, 2f);
attenuation.put(0, 1f).put(1, 1f).put(2, 1f);
attenuation.position(0).limit(3);
GLStateManager.glPointParameter(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation);
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));
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, Float.NaN);
assertTrue(Float.isNaN(GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN)));
GLStateManager.glPopAttrib();
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(GL20.GL_LOWER_LEFT, GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN));
attenuation.clear();
GLStateManager.glGetFloat(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation);
assertEquals(0.5f, attenuation.get(0));
assertEquals(0.25f, attenuation.get(1));
assertEquals(0.125f, attenuation.get(2));
} finally {
popAttribTo(base);
assertEquals(savedMax, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX));
}
}
@Test
void cacheOnlyEnablesRoundTrip() {
final int base = GLStateManager.getAttribDepth();
final boolean savedSprite = GLStateManager.glIsEnabled(GL20.GL_POINT_SPRITE);
final boolean savedTwoSide = GLStateManager.glIsEnabled(GL20.GL_VERTEX_PROGRAM_TWO_SIDE);
final boolean savedEdge = GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG);
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
final boolean savedCube0 = GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP);
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
final boolean savedCube1 = GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP);
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
try {
assertEnableRoundTrip(GL20.GL_POINT_SPRITE, false);
assertEnableRoundTrip(GL20.GL_VERTEX_PROGRAM_TWO_SIDE, false);
assertEnableRoundTrip(GL13.GL_TEXTURE_CUBE_MAP, false);
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
GLStateManager.glEnable(GL20.GL_POINT_SPRITE);
GLStateManager.glEnable(GL20.GL_VERTEX_PROGRAM_TWO_SIDE);
GLStateManager.glEnable(GL13.GL_TEXTURE_CUBE_MAP);
assertTrue(GLStateManager.glIsEnabled(GL20.GL_POINT_SPRITE));
assertTrue(GLStateManager.glIsEnabled(GL20.GL_VERTEX_PROGRAM_TWO_SIDE));
assertTrue(GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP));
GLStateManager.glPopAttrib();
assertFalse(GLStateManager.glIsEnabled(GL20.GL_POINT_SPRITE));
assertFalse(GLStateManager.glIsEnabled(GL20.GL_VERTEX_PROGRAM_TWO_SIDE));
assertFalse(GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP));
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
GLStateManager.glDisable(GL13.GL_TEXTURE_CUBE_MAP);
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glEnable(GL13.GL_TEXTURE_CUBE_MAP);
assertTrue(GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP));
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
assertFalse(GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP));
assertFalse(GLStateManager.glGetBoolean(GL13.GL_TEXTURE_CUBE_MAP));
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
assertTrue(GLStateManager.glGetBoolean(GL13.GL_TEXTURE_CUBE_MAP));
GLStateManager.glDisable(GL13.GL_TEXTURE_CUBE_MAP);
assertTrue(savedEdge);
GLStateManager.glEdgeFlag(false);
assertFalse(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG));
GLStateManager.glPushAttrib(GL11.GL_CURRENT_BIT);
GLStateManager.glEdgeFlag(true);
assertTrue(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG));
GLStateManager.glPopAttrib();
assertFalse(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG));
} finally {
popAttribTo(base);
GLStateManager.glEdgeFlag(savedEdge);
setEnabled(GL20.GL_POINT_SPRITE, savedSprite);
setEnabled(GL20.GL_VERTEX_PROGRAM_TWO_SIDE, savedTwoSide);
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
setEnabled(GL13.GL_TEXTURE_CUBE_MAP, savedCube1);
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
setEnabled(GL13.GL_TEXTURE_CUBE_MAP, savedCube0);
}
}
}
@@ -4,10 +4,7 @@ import org.junit.jupiter.api.Test;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.floats;
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyLightState;
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyMaterialState;
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
@@ -15,9 +12,6 @@ import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
@GLCompatTest
public class GLSM_Lighting_UnitTest {
static final FloatBuffer f4b = ByteBuffer.allocateDirect(4 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
static final FloatBuffer f3b = ByteBuffer.allocateDirect(3 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer();
@Test
void testLightEnablement() {
verifyState(GL11.GL_LIGHT0, false, "GL_LIGHT0 Initial State");
@@ -46,12 +40,10 @@ public class GLSM_Lighting_UnitTest {
verifyLightState(GL11.GL_LIGHT1, GL11.GL_SPECULAR, new float[]{0F, 0F, 0F, 1F}, "GL_LIGHT1 Initial Specular");
verifyLightState(GL11.GL_LIGHT1, GL11.GL_POSITION, new float[]{0F, 0F, 1F, 0F}, "GL_LIGHT1 Initial Position");
newf4b(0.25F, 0.5F, 0.25F, 0.5F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, f4b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, floats(0.25F, 0.5F, 0.25F, 0.5F));
verifyLightState(GL11.GL_LIGHT0, GL11.GL_AMBIENT, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 Ambient Changed State");
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
newf4b(0.3F, 0.4F, 0.5F, 0.6F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, f4b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, floats(0.3F, 0.4F, 0.5F, 0.6F));
verifyLightState(GL11.GL_LIGHT0, GL11.GL_AMBIENT, new float[]{0.3F, 0.4F, 0.5F, 0.6F}, "GL_LIGHT0 attrib push state changed check");
GLStateManager.glPopAttrib();
verifyLightState(GL11.GL_LIGHT0, GL11.GL_AMBIENT, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 attrib pop state check");
@@ -64,33 +56,27 @@ public class GLSM_Lighting_UnitTest {
GLStateManager.glPushMatrix();
GLStateManager.glTranslatef(25.0F, 25.0F, 25.0F);
GLStateManager.glRotatef(35.0F, 10.0F, 15.0F, 20.0F);
newf4b(0.25F, 0.5F, 0.25F, 0.0F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.25F, 0.5F, 0.25F, 0.0F));
verifyLightState(GL11.GL_LIGHT0, GL11.GL_POSITION, new float[]{0.1090F, 0.5189F, 0.3062F, 0.0F}, "GL_LIGHT0 Position Changed State");
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
newf4b(0.3F, 0.4F, 0.5F, 0.0F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.3F, 0.4F, 0.5F, 0.0F));
verifyLightState(GL11.GL_LIGHT0, GL11.GL_POSITION, new float[]{0.2824F, 0.4200F, 0.4937F, 0.0F}, "GL_LIGHT0 attrib push state changed check");
GLStateManager.glPopAttrib();
verifyLightState(GL11.GL_LIGHT0, GL11.GL_POSITION, new float[]{0.1090F, 0.5189F, 0.3062F, 0.0F}, "GL_LIGHT0 attrib pop state check");
GLStateManager.glPopMatrix();
newf4b(0.25F, 0.5F, 0.25F, 0.5F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, f4b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, floats(0.25F, 0.5F, 0.25F, 0.5F));
verifyLightState(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 Diffuse Changed State");
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
newf4b(0.3F, 0.4F, 0.5F, 0.6F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, f4b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, floats(0.3F, 0.4F, 0.5F, 0.6F));
verifyLightState(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, new float[]{0.3F, 0.4F, 0.5F, 0.6F}, "GL_LIGHT0 attrib push state changed check");
GLStateManager.glPopAttrib();
verifyLightState(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 attrib pop state check");
newf4b(0.25F, 0.5F, 0.25F, 0.5F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, f4b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, floats(0.25F, 0.5F, 0.25F, 0.5F));
verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPECULAR, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 Specular Changed State");
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
newf4b(0.3F, 0.4F, 0.5F, 0.6F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, f4b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, floats(0.3F, 0.4F, 0.5F, 0.6F));
verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPECULAR, new float[]{0.3F, 0.4F, 0.5F, 0.6F}, "GL_LIGHT0 attrib push state changed check");
GLStateManager.glPopAttrib();
verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPECULAR, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 attrib pop state check");
@@ -99,12 +85,10 @@ public class GLSM_Lighting_UnitTest {
GLStateManager.glPushMatrix();
GLStateManager.glTranslatef(25.0F, 25.0F, 25.0F);
GLStateManager.glRotatef(35.0F, 10.0F, 15.0F, 20.0F);
newf4b(0.25F, 0.5F, 0.25F, 0.0F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f4b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, floats(0.25F, 0.5F, 0.25F, 0.0F));
verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, new float[]{0.1090F, 0.5189F, 0.3062F}, "GL_LIGHT0 Spot Direction Changed State");
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
newf4b(0.3F, 0.4F, 0.5F, 0.0F);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f4b);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, floats(0.3F, 0.4F, 0.5F, 0.0F));
verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, new float[]{0.2824F, 0.4200F, 0.4937F}, "GL_LIGHT0 attrib push state changed check");
GLStateManager.glPopAttrib();
verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, new float[]{0.1090F, 0.5189F, 0.3062F}, "GL_LIGHT0 attrib pop state check");
@@ -157,8 +141,7 @@ public class GLSM_Lighting_UnitTest {
void testLightModel() {
verifyState(GL11.GL_LIGHT_MODEL_AMBIENT, new float[]{0.2F, 0.2F, 0.2F, 1.0F}, "GL_LIGHT_MODEL_AMBIENT initial state");
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
newf4b(0.8F, 0.6F, 0.4F, 0.2F);
GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, f4b);
GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, floats(0.8F, 0.6F, 0.4F, 0.2F));
verifyState(GL11.GL_LIGHT_MODEL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "GL_LIGHT_MODEL_AMBIENT changed state");
GLStateManager.glPopAttrib();
verifyState(GL11.GL_LIGHT_MODEL_AMBIENT, new float[]{0.2F, 0.2F, 0.2F, 1.0F});
@@ -235,16 +218,14 @@ public class GLSM_Lighting_UnitTest {
void testMaterial() {
verifyMaterialState(GL11.GL_FRONT, GL11.GL_AMBIENT, new float[]{0.2F, 0.2F, 0.2F, 1.0F}, "");
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
newf4b(0.8F, 0.6F, 0.4F, 0.2F);
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, f4b);
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, floats(0.8F, 0.6F, 0.4F, 0.2F));
verifyMaterialState(GL11.GL_FRONT, GL11.GL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
GLStateManager.glPopAttrib();
verifyMaterialState(GL11.GL_FRONT, GL11.GL_AMBIENT, new float[]{0.2F, 0.2F, 0.2F, 1.0F}, "");
verifyMaterialState(GL11.GL_BACK, GL11.GL_AMBIENT, new float[]{0.2F, 0.2F, 0.2F, 1.0F}, "");
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
newf4b(0.8F, 0.6F, 0.4F, 0.2F);
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT, f4b);
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT, floats(0.8F, 0.6F, 0.4F, 0.2F));
verifyMaterialState(GL11.GL_FRONT, GL11.GL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
verifyMaterialState(GL11.GL_BACK, GL11.GL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
GLStateManager.glPopAttrib();
@@ -254,8 +235,7 @@ public class GLSM_Lighting_UnitTest {
verifyMaterialState(GL11.GL_FRONT, GL11.GL_DIFFUSE, new float[]{0.8F, 0.8F, 0.8F, 1.0F}, "");
verifyMaterialState(GL11.GL_BACK, GL11.GL_DIFFUSE, new float[]{0.8F, 0.8F, 0.8F, 1.0F}, "");
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
newf4b(0.8F, 0.6F, 0.4F, 0.2F);
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE, f4b);
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE, floats(0.8F, 0.6F, 0.4F, 0.2F));
verifyMaterialState(GL11.GL_FRONT, GL11.GL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
verifyMaterialState(GL11.GL_BACK, GL11.GL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
verifyMaterialState(GL11.GL_FRONT, GL11.GL_DIFFUSE, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
@@ -268,16 +248,14 @@ public class GLSM_Lighting_UnitTest {
verifyMaterialState(GL11.GL_BACK, GL11.GL_SPECULAR, new float[]{0.0F, 0.0F, 0.0F, 1.0F}, "");
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
newf4b(0.8F, 0.6F, 0.4F, 0.2F);
GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_SPECULAR, f4b);
GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_SPECULAR, floats(0.8F, 0.6F, 0.4F, 0.2F));
verifyMaterialState(GL11.GL_BACK, GL11.GL_SPECULAR, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
GLStateManager.glPopAttrib();
verifyMaterialState(GL11.GL_BACK, GL11.GL_SPECULAR, new float[]{0.0F, 0.0F, 0.0F, 1.0F}, "");
verifyMaterialState(GL11.GL_BACK, GL11.GL_EMISSION, new float[]{0.0F, 0.0F, 0.0F, 1.0F}, "");
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
newf4b(0.8F, 0.6F, 0.4F, 0.2F);
GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_EMISSION, f4b);
GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_EMISSION, floats(0.8F, 0.6F, 0.4F, 0.2F));
verifyMaterialState(GL11.GL_BACK, GL11.GL_EMISSION, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "");
GLStateManager.glPopAttrib();
verifyMaterialState(GL11.GL_BACK, GL11.GL_EMISSION, new float[]{0.0F, 0.0F, 0.0F, 1.0F}, "");
@@ -291,8 +269,7 @@ public class GLSM_Lighting_UnitTest {
verifyMaterialState(GL11.GL_FRONT, GL11.GL_COLOR_INDEXES, new float[]{0.0F, 1.0F, 1.0F}, "");
GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT);
newf4b(0.8F, 0.6F, 0.4F, 0.0F);
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_COLOR_INDEXES, f4b);
GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_COLOR_INDEXES, floats(0.8F, 0.6F, 0.4F, 0.0F));
if (GLStateManager.vendorIsNVIDIA()) {
verifyMaterialState(GL11.GL_FRONT, GL11.GL_COLOR_INDEXES, new float[]{0.8F, 0.4F, 0.6F}, "");
} else {
@@ -302,16 +279,4 @@ public class GLSM_Lighting_UnitTest {
verifyMaterialState(GL11.GL_FRONT, GL11.GL_COLOR_INDEXES, new float[]{0.0F, 1.0F, 1.0F}, "");
}
static void newf4b(float x, float y, float z, float w) {
f4b.clear();
f4b.put(x).put(y).put(z).put(w);
f4b.flip();
}
static void newf3b(float x, float y, float z) {
f3b.clear();
f3b.put(x).put(y).put(z);
f3b.flip();
}
}
@@ -5,6 +5,9 @@ import com.gtnewhorizons.angelica.glsm.states.ViewportState;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
@@ -12,6 +15,7 @@ import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.function.IntPredicate;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -72,7 +76,6 @@ public class GLSM_LineStipple_GLTest {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
if (vbo != 0) GLStateManager.glDeleteBuffers(vbo);
if (vao != 0) GLStateManager.glDeleteVertexArrays(vao);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
private boolean[] renderLine(float x0, float x1) {
@@ -117,35 +120,29 @@ public class GLSM_LineStipple_GLTest {
GLStateManager.glLineStipple(factor, (short) pattern);
}
@Test
void solidPatternCoversEveryPixel() {
stipple(1, 0xFFFF);
assertArrayEquals(expected(i -> true), renderLine(0f, W));
static Stream<Arguments> patterns() {
return Stream.of(
pattern("solid pattern covers every pixel", true, 1, 0xFFFF, i -> true),
pattern("empty pattern covers nothing", true, 1, 0x0000, i -> false),
pattern("alternating pattern dashes every other pixel", true, 1, 0xAAAA, i -> (i & 1) == 1),
pattern("factor stretches each bit across that many pixels", true, 2, 0xAAAA, i -> ((i / 2) & 1) == 1),
pattern("disabled stipple covers every pixel", false, 1, 0xAAAA, i -> true));
}
@Test
void emptyPatternCoversNothing() {
stipple(1, 0x0000);
assertArrayEquals(expected(i -> false), renderLine(0f, W));
private static Arguments pattern(String name, boolean enabled, int factor, int pattern, IntPredicate on) {
return Arguments.of(name, enabled, factor, pattern, on);
}
@Test
void alternatingPatternDashesEveryOtherPixel() {
stipple(1, 0xAAAA);
assertArrayEquals(expected(i -> (i & 1) == 1), renderLine(0f, W));
}
@Test
void factorStretchesEachBitAcrossThatManyPixels() {
stipple(2, 0xAAAA);
assertArrayEquals(expected(i -> ((i / 2) & 1) == 1), renderLine(0f, W));
}
@Test
void disabledStippleCoversEveryPixel() {
GLStateManager.glLineStipple(1, (short) 0xAAAA);
GLStateManager.glDisable(GL11.GL_LINE_STIPPLE);
assertArrayEquals(expected(i -> true), renderLine(0f, W));
@ParameterizedTest(name = "{0}")
@MethodSource("patterns")
void patternSelectsTheCoveredPixels(String name, boolean enabled, int factor, int pattern, IntPredicate on) {
GLStateManager.glLineStipple(factor, (short) pattern);
if (enabled) {
GLStateManager.glEnable(GL11.GL_LINE_STIPPLE);
} else {
GLStateManager.glDisable(GL11.GL_LINE_STIPPLE);
}
assertArrayEquals(expected(on), renderLine(0f, W));
}
@Test
@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.glsm.testutil.TestThreads;
import org.joml.Matrix4f;
import org.junit.jupiter.api.Test;
import org.lwjgl.opengl.GL11;
@@ -18,23 +19,14 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
@GLCoreTest
public class GLSM_PerThread_UnitTest {
private static void runThreaded(int n, ThreadBody body) throws Throwable {
private static void runThreaded(int n, ThreadBody body) throws InterruptedException {
final CyclicBarrier barrier = new CyclicBarrier(n);
final Throwable[] errs = new Throwable[n];
final Thread[] ts = new Thread[n];
final TestThreads.Worker[] workers = new TestThreads.Worker[n];
for (int i = 0; i < n; i++) {
final int tid = i;
ts[i] = new Thread(() -> {
try {
body.run(tid, barrier);
} catch (Throwable t) {
errs[tid] = t;
}
}, "glsm-perthread-" + i);
workers[i] = TestThreads.start("glsm-perthread-" + i, () -> body.run(tid, barrier));
}
for (Thread t : ts) t.start();
for (Thread t : ts) t.join();
for (int i = 0; i < n; i++) if (errs[i] != null) throw errs[i];
for (TestThreads.Worker worker : workers) worker.join();
}
interface ThreadBody {
@@ -13,6 +13,7 @@ import org.lwjgl.opengl.GL20;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import static com.gtnewhorizons.angelica.util.GLSMUtil.resetGLState;
import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyIsEnabled;
@@ -49,6 +50,24 @@ class GLSM_PushPop_UnitTest {
public static final float[] FLOAT_ARRAY_3_1 = { 1f, 1f, 1f };
public static final float[] FLOAT_ARRAY_GL_SPOT_DIRECTION_DEFAULT = { 0f, 0f, -1f };
private static void toggleAndVerify(List<GLBit> bits) {
for (GLBit bit : bits) {
verifyState(bit.glEnum(), bit.initial(), bit.name() + " Initial State");
if (bit.initial()) {
GLStateManager.glDisable(bit.glEnum());
} else {
GLStateManager.glEnable(bit.glEnum());
}
verifyState(bit.glEnum(), !bit.initial(), bit.name() + " Toggle State");
}
}
private static void verifyReset(List<GLBit> bits) {
for (GLBit bit : bits) {
verifyState(bit.glEnum(), bit.initial(), bit.name() + " Reset State");
}
}
@Test
void testPushPopColorBufferBit() {
@@ -241,19 +260,11 @@ class GLSM_PushPop_UnitTest {
bits.add(new GLBit(GL11.GL_TEXTURE_GEN_Q, "Texture Gen Q", false));
GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT);
bits.forEach(bit -> {
verifyState(bit.glEnum(), bit.initial(), bit.name() + " Initial State");
if(bit.initial()) {
GLStateManager.glDisable(bit.glEnum());
} else {
GLStateManager.glEnable(bit.glEnum());
}
verifyState(bit.glEnum(), !bit.initial(), bit.name() + " Toggle State");
});
toggleAndVerify(bits);
GLStateManager.glPopAttrib();
bits.forEach(bit -> verifyState(bit.glEnum(), bit.initial(), bit.name() + " Reset State"));
verifyReset(bits);
}
@Test
@@ -329,15 +340,7 @@ class GLSM_PushPop_UnitTest {
verifyState(GL11.GL_SHADE_MODEL, GL11.GL_FLAT, "Shade Model");
bits.forEach(bit -> {
verifyState(bit.glEnum(), bit.initial(), bit.name() + " Initial State");
if(bit.initial()) {
GLStateManager.glDisable(bit.glEnum());
} else {
GLStateManager.glEnable(bit.glEnum());
}
verifyState(bit.glEnum(), !bit.initial(), bit.name() + " Toggle State");
});
toggleAndVerify(bits);
GLStateManager.glPopAttrib();
verifyState(GL11.GL_COLOR_MATERIAL_FACE, GL11.GL_FRONT_AND_BACK, "Color Material Face - Reset");
@@ -353,7 +356,7 @@ class GLSM_PushPop_UnitTest {
verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, FLOAT_ARRAY_GL_SPOT_DIRECTION_DEFAULT, "Light Spot Direction - Reset");
verifyState(GL11.GL_SHADE_MODEL, GL11.GL_SMOOTH, "Shade Model - Reset");
bits.forEach(bit -> verifyState(bit.glEnum(), bit.initial(), bit.name() + " Reset State"));
verifyReset(bits);
}
@Test
@@ -388,15 +391,7 @@ class GLSM_PushPop_UnitTest {
verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE1, "Active Texture");
verifyState(GL11.GL_TEXTURE_BINDING_2D, tex2, "Texture Binding - Unit 1");
bits.forEach(bit -> {
verifyState(bit.glEnum(), bit.initial(), bit.name() + " Initial State");
if(bit.initial()) {
GLStateManager.glDisable(bit.glEnum());
} else {
GLStateManager.glEnable(bit.glEnum());
}
verifyState(bit.glEnum(), !bit.initial(), bit.name() + " Toggle State");
});
toggleAndVerify(bits);
GLStateManager.glPopAttrib();
verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE0, "Active Texture Unit 0 - Reset");
@@ -404,7 +399,7 @@ class GLSM_PushPop_UnitTest {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
verifyState(GL11.GL_TEXTURE_BINDING_2D, 0, "Texture Binding Unit 1 - Reset");
bits.forEach(bit -> verifyState(bit.glEnum(), bit.initial(), bit.name() + " Reset State"));
verifyReset(bits);
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glDeleteTextures(tex1);
@@ -1156,30 +1151,6 @@ class GLSM_PushPop_UnitTest {
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO);
}
@Test
void testPolygonStateReadBack() {
GLStateManager.glCullFace(GL11.GL_FRONT);
GLStateManager.glFrontFace(GL11.GL_CW);
GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
verifyState(GL11.GL_CULL_FACE_MODE, GL11.GL_FRONT, "Cull Face Mode");
verifyState(GL11.GL_FRONT_FACE, GL11.GL_CW, "Front Face");
verifyState(GL11.GL_POLYGON_MODE, new int[] { GL11.GL_LINE, GL11.GL_LINE }, "Polygon Mode");
GLStateManager.glCullFace(GL11.GL_BACK);
GLStateManager.glFrontFace(GL11.GL_CCW);
GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
}
@Test
void testActiveTextureReadBack() {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE1, "Active Texture");
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE0, "Active Texture - Reset");
}
@Test
void testUntouchedPushPopRoundTripLeavesStateIdentical() {
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
@@ -6,6 +6,8 @@ import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.driverTextureBinding;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -15,14 +17,6 @@ public class GLSM_RetainedState_GLTest {
private static final StateSet ALL = StateSet.forMask(GL11.GL_ALL_ATTRIB_BITS);
private static int driverTextureBinding(int unit) {
final int saved = GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE);
GL13.glActiveTexture(GL13.GL_TEXTURE0 + unit);
final int binding = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
GL13.glActiveTexture(saved);
return binding;
}
private static void baseline() {
GLStateManager.enableCull();
GLStateManager.disableBlend();
@@ -234,7 +228,7 @@ public class GLSM_RetainedState_GLTest {
@Test
void aDeletedProgramIsNotRebound() {
final RetainedState retained = new RetainedState();
final int program = GLSM_StateSet_GLTest.linkTrivialProgram();
final int program = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS);
try {
baseline();
final int d = GLStateManager.pushState(ALL);
@@ -243,11 +237,10 @@ public class GLSM_RetainedState_GLTest {
GLStateManager.popStateTo(d);
GLStateManager.glUseProgram(0);
GLStateManager.glDeleteProgram(program);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
drainErrors();
GLStateManager.applyRetainedState(retained);
assertEquals(0, GLStateManager.getActiveProgram());
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
} finally {
GLStateManager.glUseProgram(0);
baseline();
@@ -24,7 +24,6 @@ class GLSM_SamplerBinding_GLTest {
@BeforeEach
void requireSamplerObjects() {
assumeTrue(RenderSystem.supportsSamplerObjects());
drainGlErrors();
}
@AfterEach
@@ -35,11 +34,6 @@ class GLSM_SamplerBinding_GLTest {
GL33.glBindSampler(unit, 0);
}
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
drainGlErrors();
}
private static void drainGlErrors() {
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
private static int driverBinding(int unit) {
@@ -3,6 +3,8 @@ package com.gtnewhorizons.angelica.glsm;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
@@ -12,6 +14,7 @@ import org.lwjgl.opengl.SharedDrawable;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -43,21 +46,13 @@ public class GLSM_ServerSideState_UnitTest {
private boolean originalSplashComplete;
private Thread originalDrawableGLHolder;
private static void setSplashComplete(boolean value) {
SplashWindow.setSplashComplete(value);
}
private static boolean getSplashComplete() {
return SplashWindow.isSplashComplete();
}
@BeforeEach
void setupSharedDrawable() throws Exception {
originalSplashComplete = getSplashComplete();
originalSplashComplete = SplashWindow.isSplashComplete();
originalDrawableGLHolder = GLStateManager.getDrawableGLHolder();
// Reset splash state to simulate splash screen scenario
setSplashComplete(false);
SplashWindow.setSplashComplete(false);
sharedDrawable = new SharedDrawable(Display.getDrawable());
@@ -149,7 +144,7 @@ public class GLSM_ServerSideState_UnitTest {
// Restore original state
GLStateManager.setDrawableGL(null);
GLStateManager.setDrawableGLHolder(originalDrawableGLHolder);
setSplashComplete(originalSplashComplete);
SplashWindow.setSplashComplete(originalSplashComplete);
if (originalSplashComplete) {
GLStateManager.markSplashComplete("test");
}
@@ -214,46 +209,81 @@ public class GLSM_ServerSideState_UnitTest {
GL11.glDeleteTextures(texShared[0]);
}
/** One texture parameter through its typed GLSM setter and getter, plus the raw driver read. Values travel as float. */
private static final class TexParam {
final String name;
final float initial;
final float changed;
final FloatConsumer set;
final FloatSupplier glsm;
final FloatSupplier driver;
TexParam(String name, float initial, float changed, FloatConsumer set, FloatSupplier glsm, FloatSupplier driver) {
this.name = name;
this.initial = initial;
this.changed = changed;
this.set = set;
this.glsm = glsm;
this.driver = driver;
}
@Override
public String toString() {
return name;
}
}
private interface FloatConsumer {
void accept(float value);
}
private interface FloatSupplier {
float get();
}
static Stream<TexParam> texParams() {
return Stream.of(
new TexParam("glTexParameteri MIN_FILTER", GL11.GL_LINEAR, GL11.GL_NEAREST,
v -> GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, (int) v),
() -> GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER),
() -> GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER)),
new TexParam("glTexParameterf LOD_BIAS", 0.0f, 1.5f,
v -> GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, v),
() -> GLStateManager.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS),
() -> GL11.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS)));
}
/**
* Test: SharedDrawable sets texture MIN_FILTER, verify both GLSM and actual GL state are correct on both contexts.
* Test: SharedDrawable sets a texture parameter, verify both GLSM and actual GL state are correct on both contexts.
*/
@Test
void testTexParameterFromSharedDrawable() throws InterruptedException {
@ParameterizedTest
@MethodSource("texParams")
void testTexParameterFromSharedDrawable(TexParam param) throws InterruptedException {
int texId = GL11.glGenTextures();
try {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
param.set.accept(param.initial);
int initialGLSM = GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
assertEquals(GL11.GL_LINEAR, initialGLSM, "Initial GLSM should be GL_LINEAR");
assertEquals(param.initial, param.glsm.get(), 0.001f, "Initial GLSM value");
assertEquals(param.initial, param.driver.get(), 0.001f, "Initial GL value");
int initialGL = GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
assertEquals(GL11.GL_LINEAR, initialGL, "Initial GL should be GL_LINEAR");
// SharedDrawable: bind and change params to GL_NEAREST via GLSM
executeOnSharedDrawable(() -> {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
int boundTex = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
assertEquals(texId, boundTex, "SharedDrawable should have texId bound");
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
param.set.accept(param.changed);
int glsmValue = GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
assertEquals(GL11.GL_NEAREST, glsmValue, "SharedDrawable GLSM should return GL_NEAREST");
int glValue = GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
assertEquals(GL11.GL_NEAREST, glValue, "SharedDrawable GL should be GL_NEAREST");
assertEquals(param.changed, param.glsm.get(), 0.001f, "SharedDrawable GLSM value");
assertEquals(param.changed, param.driver.get(), 0.001f, "SharedDrawable GL value");
});
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
int mainGLSM = GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
assertEquals(GL11.GL_NEAREST, mainGLSM, "Main thread GLSM should return GL_NEAREST");
int mainGL = GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER);
assertEquals(GL11.GL_NEAREST, mainGL, "Main thread GL should be GL_NEAREST");
assertEquals(param.changed, param.glsm.get(), 0.001f, "Main thread GLSM value");
assertEquals(param.changed, param.driver.get(), 0.001f, "Main thread GL value");
} finally {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
@@ -358,50 +388,6 @@ public class GLSM_ServerSideState_UnitTest {
}
}
/**
* Test glTexParameterf (float parameters like LOD_BIAS).
*/
@Test
void testTexParameterfFromSharedDrawable() throws InterruptedException {
int texId = GL11.glGenTextures();
try {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, 0.0f);
float initialGLSM = GLStateManager.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS);
float initialGL = GL11.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS);
assertEquals(0.0f, initialGLSM, 0.001f, "Initial GLSM LOD_BIAS should be 0.0");
assertEquals(0.0f, initialGL, 0.001f, "Initial GL LOD_BIAS should be 0.0");
// SharedDrawable: change LOD_BIAS
executeOnSharedDrawable(() -> {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
int bound = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
assertEquals(texId, bound, "SharedDrawable should have texId bound");
GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, 1.5f);
float glsmValue = GLStateManager.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS);
assertEquals(1.5f, glsmValue, 0.001f, "SharedDrawable GLSM should return 1.5");
float glValue = GL11.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS);
assertEquals(1.5f, glValue, 0.001f, "SharedDrawable GL should be 1.5");
});
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId);
float finalGLSM = GLStateManager.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS);
float finalGL = GL11.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS);
assertEquals(1.5f, finalGLSM, 0.001f, "Main thread GLSM should return 1.5");
assertEquals(1.5f, finalGL, 0.001f, "Main thread GL should be 1.5");
} finally {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GLStateManager.glDeleteTextures(texId);
}
}
/**
* Test glTexImage2D: SharedDrawable uploads texture data, verify dimensions cached correctly.
*/
@@ -478,7 +464,7 @@ public class GLSM_ServerSideState_UnitTest {
GL11.glDeleteTextures(deferredGen);
assertTrue(GL11.glIsTexture(texId), "Delete should stay deferred while the splash is not complete");
setSplashComplete(true);
SplashWindow.setSplashComplete(true);
flushGen = GLStateManager.glGenTextures();
assertFalse(GL11.glIsTexture(texId), "Texture should be gone after the post-splash flush");
assertEquals(0, GLStateManager.getBoundTextureForServerState(), "Flush should scrub the main thread binding");
@@ -5,12 +5,17 @@ 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;
@@ -30,13 +35,23 @@ public class GLSM_SplashCachingCoverage_UnitTest {
void leaveSplashWindow() throws IllegalAccessException {
SplashWindow.leave();
GLStateManager.glDepthFunc(GL11.GL_LESS);
GLStateManager.glCullFace(GL11.GL_BACK);
}
@Test
void depthFuncSetDuringSplashLandsInCache() {
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
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()));
}
assertEquals(GL11.GL_LEQUAL, GLStateManager.getDepthState().getFunc(), "cache write dropped while caching=false");
@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
@@ -137,11 +152,4 @@ public class GLSM_SplashCachingCoverage_UnitTest {
assertFalse(GLStateManager.takeStateSeedPending(), "seed must be consumed exactly once, not once per frame");
assertFalse(GLStateManager.takeStateSeedPending(), "seed must stay consumed");
}
@Test
void cullFaceSetDuringSplashLandsInCache() {
GLStateManager.glCullFace(GL11.GL_FRONT);
assertEquals(GL11.GL_FRONT, GLStateManager.getPolygonState().getCullFaceMode(), "cache write dropped while caching=false");
}
}
@@ -54,18 +54,14 @@ public class GLSM_SplashCompleteLatch_UnitTest {
@Test
void cachingIsThreadScopedBeforeTheLatchAndGlobalAfter() throws Exception {
final AtomicBoolean cachingOffThread = new AtomicBoolean(true);
final Runnable probe = () -> cachingOffThread.set(GLStateManager.isCachingEnabled());
final TestThreads.Body probe = () -> cachingOffThread.set(GLStateManager.isCachingEnabled());
Thread t = new Thread(probe, "non-holder");
t.start();
t.join();
TestThreads.run("non-holder", probe);
assertFalse(cachingOffThread.get(), "caching must be holder-scoped during the dual-context window");
GLStateManager.markSplashComplete("test");
t = new Thread(probe, "non-holder");
t.start();
t.join();
TestThreads.run("non-holder", probe);
assertTrue(cachingOffThread.get(), "after the latch caching is global - this is what the latch buys");
}
File diff suppressed because it is too large. Load diff
@@ -10,13 +10,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
@GLCoreTest
public class GLSM_TextureBindingTargetCache_UnitTest {
private static void drainErrors() {
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
@Test
void flattenedCacheTracksNon2DTargets() {
drainErrors();
final int t3d = GL11.glGenTextures();
final int t2d = GL11.glGenTextures();
try {
@@ -38,13 +33,11 @@ public class GLSM_TextureBindingTargetCache_UnitTest {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glDeleteTextures(t3d);
GLStateManager.glDeleteTextures(t2d);
drainErrors();
}
}
@Test
void proxyTargetBindIsNeitherForwardedNorCached() {
drainErrors();
final int t2d = GL11.glGenTextures();
try {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE5);
@@ -60,13 +53,11 @@ public class GLSM_TextureBindingTargetCache_UnitTest {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glDeleteTextures(t2d);
drainErrors();
}
}
@Test
void popAttribRestoresRecordedTarget() {
drainErrors();
final int t3d = GL11.glGenTextures();
final int t2d = GL11.glGenTextures();
try {
@@ -86,13 +77,11 @@ public class GLSM_TextureBindingTargetCache_UnitTest {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glDeleteTextures(t3d);
GLStateManager.glDeleteTextures(t2d);
drainErrors();
}
}
@Test
void unbindIsNotElidedAcrossTargets() {
drainErrors();
final int t2d = GL11.glGenTextures();
try {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE4);
@@ -100,11 +89,9 @@ public class GLSM_TextureBindingTargetCache_UnitTest {
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, 0);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
assertEquals(0, GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D), "2D unbind after a 3D unbind must reach the driver");
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
} finally {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glDeleteTextures(t2d);
drainErrors();
}
}
}
@@ -79,6 +79,8 @@ public class GLSM_TextureUploadGeneration_GLTest {
final int afterSub = info.getUploadGeneration();
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 1, 0, 0, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, BufferUtils.createByteBuffer(4));
assertEquals(afterSub, info.getUploadGeneration(), "only level 0 is tracked");
// Level 1 was never allocated, so the driver rejects that upload.
assertEquals(GL11.GL_INVALID_OPERATION, GL11.glGetError());
} finally {
GLStateManager.glDeleteTextures(texId);
}
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizon.gtnhlib.client.opengl.UniversalVAO;
import com.gtnewhorizons.angelica.glsm.ffp.VAOManager;
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.opengl.GL11;
@@ -15,6 +16,13 @@ import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState;
@GLCompatTest
public class GLSM_VAO_UnitTest {
@AfterEach
void resetClientArrayState() {
glBindVertexArray(0);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
GLSMUtil.setClientState(GL11.GL_NORMAL_ARRAY, false);
}
@Test
void testDefaultStates() {
verifyState(GL30.GL_VERTEX_ARRAY_BINDING, GLStateManager.ctx().defaultVAO, "GL_VERTEX_ARRAY_BINDING Initial State");
@@ -36,14 +44,14 @@ public class GLSM_VAO_UnitTest {
final int vaoVertexType = GL11.GL_INT;
final int vaoVertexStride = vaoVertexSize * 4;
GL13.glClientActiveTexture(GL13.GL_TEXTURE1);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
verifyState(GL13.GL_CLIENT_ACTIVE_TEXTURE, GL13.GL_TEXTURE1, "GL_CLIENT_ACTIVE_TEXTURE - Setup");
// Set the vertex attrib to a defined state
int vbo = GL15.glGenBuffers();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GL11.glVertexPointer(defaultVertexSize, defaultVertexType, defaultVertexStride, 0);
GLStateManager.glVertexPointer(defaultVertexSize, defaultVertexType, defaultVertexStride, 0L);
GLSMUtil.setClientState(GL11.GL_NORMAL_ARRAY, true);
verifyState(GL11.GL_NORMAL_ARRAY, true, "GL_NORMAL_ARRAY - Enabled");
@@ -74,7 +82,7 @@ public class GLSM_VAO_UnitTest {
GLSMUtil.setClientState(GL11.GL_VERTEX_ARRAY, vaoVertexEnabled);
verifyState(GL11.GL_VERTEX_ARRAY, vaoVertexEnabled, "GL_VERTEX_ARRAY - Enabled");
GL11.glVertexPointer(vaoVertexSize, vaoVertexType, vaoVertexStride, 0);
GLStateManager.glVertexPointer(vaoVertexSize, vaoVertexType, vaoVertexStride, 0L);
verifyState(GL11.GL_VERTEX_ARRAY_SIZE, vaoVertexSize, "GL_VERTEX_ARRAY_SIZE - Set");
verifyState(GL11.GL_VERTEX_ARRAY_TYPE, vaoVertexType, "GL_VERTEX_ARRAY_TYPE - Set");
@@ -117,7 +125,7 @@ public class GLSM_VAO_UnitTest {
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
GL15.glDeleteBuffers(vbo);
GLStateManager.glDeleteBuffers(vbo);
UniversalVAO.deleteVertexArrays(vao);
}
@@ -138,7 +146,7 @@ public class GLSM_VAO_UnitTest {
GLStateManager.glDisableVertexAttribArray(0);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
GL15.glDeleteBuffers(vbo);
GLStateManager.glDeleteBuffers(vbo);
UniversalVAO.deleteVertexArrays(vao);
}
@@ -159,7 +167,7 @@ public class GLSM_VAO_UnitTest {
GLStateManager.glDisableVertexAttribArray(0);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
GL15.glDeleteBuffers(vbo);
GLStateManager.glDeleteBuffers(vbo);
UniversalVAO.deleteVertexArrays(vao);
}
@@ -179,7 +187,7 @@ public class GLSM_VAO_UnitTest {
GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
GL15.glDeleteBuffers(vbo);
GLStateManager.glDeleteBuffers(vbo);
UniversalVAO.deleteVertexArrays(vao);
}
@@ -31,6 +31,9 @@ public class GLSM_ValueStackCow_GLTest {
if (fixtureTexA == -1) {
fixtureTexA = GLStateManager.glGenTextures();
fixtureTexB = GLStateManager.glGenTextures();
final int bound = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, fixtureTexB);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, bound);
}
}
@@ -4,12 +4,12 @@ import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList;
import com.gtnewhorizons.angelica.glsm.recording.GLCommand;
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
import com.gtnewhorizons.angelica.glsm.testutil.TestThreads;
import org.junit.jupiter.api.Test;
import org.lwjgl.opengl.GL11;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@@ -29,10 +29,10 @@ class ImmediateModeThreadIsolationTest {
final AtomicReference<DirectTessellator> bg1 = new AtomicReference<>();
final AtomicReference<DirectTessellator> bg2 = new AtomicReference<>();
final Thread t1 = new Thread(() -> bg1.set(ImmediateModeRecorder.getInternalTessellator()), "iso-test-1");
final Thread t2 = new Thread(() -> bg2.set(ImmediateModeRecorder.getInternalTessellator()), "iso-test-2");
t1.start(); t2.start();
t1.join(); t2.join();
final TestThreads.Worker t1 = TestThreads.start("iso-test-1", () -> bg1.set(ImmediateModeRecorder.getInternalTessellator()));
final TestThreads.Worker t2 = TestThreads.start("iso-test-2", () -> bg2.set(ImmediateModeRecorder.getInternalTessellator()));
t1.join();
t2.join();
assertNotNull(bg1.get());
assertNotNull(bg2.get());
@@ -43,28 +43,22 @@ class ImmediateModeThreadIsolationTest {
@Test
void concurrentImmediateModeDoesNotCorruptRecording() throws Exception {
final List<Throwable> failures = new CopyOnWriteArrayList<>();
final CountDownLatch start = new CountDownLatch(1);
final AtomicBoolean stop = new AtomicBoolean(false);
final Runnable spam = () -> {
try {
start.await();
while (!stop.get()) {
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();
}
} catch (Throwable t) {
failures.add(t);
final TestThreads.Body spam = () -> {
start.await();
while (!stop.get()) {
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();
}
};
final Thread t1 = new Thread(spam, "iso-spam-1");
final Thread t2 = new Thread(spam, "iso-spam-2");
t1.start(); t2.start();
final TestThreads.Worker t1 = TestThreads.start("iso-spam-1", spam);
final TestThreads.Worker t2 = TestThreads.start("iso-spam-2", spam);
start.countDown();
final List<Integer> lists = new ArrayList<>();
@@ -83,10 +77,10 @@ class ImmediateModeThreadIsolationTest {
}
} finally {
stop.set(true);
t1.join(5000); t2.join(5000);
}
t1.join();
t2.join();
assertTrue(failures.isEmpty(), "background immediate mode threw: " + failures);
for (int list : lists) {
final CompiledDisplayList compiled = DisplayListManager.getDisplayList(list);
assertNotNull(compiled, "list " + list + " should be compiled");
@@ -97,9 +91,7 @@ class ImmediateModeThreadIsolationTest {
@Test
void sweepFreesDeadThreadTessellators() throws Exception {
final Thread t = new Thread(() -> ImmediateModeRecorder.getInternalTessellator(), "iso-sweep");
t.start();
t.join();
TestThreads.run("iso-sweep", ImmediateModeRecorder::getInternalTessellator);
final int before = ImmediateModeRecorder.getThreadTessellatorCount();
assertTrue(before >= 1);
ImmediateModeRecorder.cleanupOrphanTessellators();
@@ -2,17 +2,19 @@ package com.gtnewhorizons.angelica.glsm;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.function.IntConsumer;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
@GLCoreTest
class InstancedQuadDrawGLTest {
@@ -26,7 +28,7 @@ class InstancedQuadDrawGLTest {
@BeforeEach
void setup() {
program = buildProgram();
program = GlTestSupport.program(VS, FS);
GLStateManager.glUseProgram(program);
final FloatBuffer verts = BufferUtils.createFloatBuffer(8);
@@ -42,63 +44,39 @@ class InstancedQuadDrawGLTest {
ebo = GLStateManager.glGenBuffers();
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
GLStateManager.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, idx, GL15.GL_STATIC_DRAW);
drainErrors();
}
@AfterEach
void cleanup() {
GLStateManager.glUseProgram(0);
if (program != 0) GL20.glDeleteProgram(program);
if (program != 0) GLStateManager.glDeleteProgram(program);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
if (vbo != 0) GLStateManager.glDeleteBuffers(vbo);
if (ebo != 0) GLStateManager.glDeleteBuffers(ebo);
drainErrors();
}
@Test
void drawArraysInstanced_quads_noInvalidEnum() {
GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, 4, 3);
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "GL_QUADS instanced array draw must be emulated, not rejected");
static Stream<Arguments> draws() {
return Stream.of(
draw("GL_QUADS instanced array draw must be emulated, not rejected", ebo -> GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, 4, 3)),
draw("non-quad instanced array draw passes through cleanly", ebo -> {
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 3);
}),
draw("GL_QUADS instanced element draw must be emulated, not rejected", ebo -> {
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
GLStateManager.glDrawElementsInstanced(GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_INT, 0L, 3);
}));
}
@Test
void drawArraysInstanced_triangles_fastPathClean() {
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 3);
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "non-quad instanced array draw passes through cleanly");
private static Arguments draw(String name, IntConsumer body) {
return Arguments.of(name, body);
}
@Test
void drawElementsInstanced_quads_noInvalidEnum() {
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
GLStateManager.glDrawElementsInstanced(GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_INT, 0L, 3);
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "GL_QUADS instanced element draw must be emulated, not rejected");
}
private static int buildProgram() {
final int vs = compile(GL20.GL_VERTEX_SHADER, VS);
final int fs = compile(GL20.GL_FRAGMENT_SHADER, FS);
final int p = GL20.glCreateProgram();
GL20.glAttachShader(p, vs);
GL20.glAttachShader(p, fs);
GL20.glLinkProgram(p);
assertNotEquals(0, GL20.glGetProgrami(p, GL20.GL_LINK_STATUS), "test shader must link: " + GL20.glGetProgramInfoLog(p, 1024));
GL20.glDeleteShader(vs);
GL20.glDeleteShader(fs);
return p;
}
private static int compile(int type, String src) {
final int s = GL20.glCreateShader(type);
GL20.glShaderSource(s, src);
GL20.glCompileShader(s);
assertNotEquals(0, GL20.glGetShaderi(s, GL20.GL_COMPILE_STATUS), "test shader must compile: " + GL20.glGetShaderInfoLog(s, 1024));
return s;
}
private static void drainErrors() {
while (GL11.glGetError() != GL11.GL_NO_ERROR) { /* clear */ }
@ParameterizedTest(name = "{0}")
@MethodSource("draws")
void instancedDrawRaisesNoGlError(String name, IntConsumer draw) {
draw.accept(ebo);
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), name);
}
}
@@ -81,11 +81,6 @@ class QuadConverterTest {
}
}
@Test
void triangulateQuads_zeroQuads_noop() {
QuadConverter.triangulateQuads(0L, GL11.GL_UNSIGNED_INT, 0L, GL11.GL_UNSIGNED_INT, 0);
}
@Test
void triangulateQuads_unsupportedTypes_throw() {
final ByteBuffer src = MemoryUtilities.memAlloc(16);
@@ -240,8 +235,6 @@ class QuadConverterTest {
}
}
// === helpers ===
private static void assertUintIndices(ByteBuffer buf, int... expected) {
for (int i = 0; i < expected.length; i++) {
assertEquals(expected[i], buf.getInt(i * 4), "uint index[" + i + "]");
@@ -1,72 +1,42 @@
package com.gtnewhorizons.angelica.glsm;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.lwjgl.opengl.GL11;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.stream.Stream;
@GLCoreTest
class ReadbackGLTest {
@AfterEach
void noErrors() {
void resetPack() {
ReadbackFixture.resetPack();
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
}
@Test
void rgba8() {
ReadbackFixture.rgba8();
static Stream<Arguments> readbacks() {
return Stream.of(
format("rgba8", ReadbackFixture::rgba8),
format("packState", ReadbackFixture::packState),
format("pixelPackBuffer", ReadbackFixture::pixelPackBuffer),
format("rgb8", ReadbackFixture::rgb8),
format("rgba16f", ReadbackFixture::rgba16f),
format("rgba32f", ReadbackFixture::rgba32f),
format("r8", ReadbackFixture::r8),
format("rg16f", ReadbackFixture::rg16f),
format("rgb10a2", ReadbackFixture::rgb10a2),
format("r11g11b10f", ReadbackFixture::r11g11b10f),
format("depthStencil", ReadbackFixture::depthStencil));
}
@Test
void packState() {
ReadbackFixture.packState();
private static Arguments format(String name, Runnable body) {
return Arguments.of(name, body);
}
@Test
void pixelPackBuffer() {
ReadbackFixture.pixelPackBuffer();
}
@Test
void rgb8() {
ReadbackFixture.rgb8();
}
@Test
void rgba16f() {
ReadbackFixture.rgba16f();
}
@Test
void rgba32f() {
ReadbackFixture.rgba32f();
}
@Test
void r8() {
ReadbackFixture.r8();
}
@Test
void rg16f() {
ReadbackFixture.rg16f();
}
@Test
void rgb10a2() {
ReadbackFixture.rgb10a2();
}
@Test
void r11g11b10f() {
ReadbackFixture.r11g11b10f();
}
@Test
void depthStencil() {
ReadbackFixture.depthStencil();
@ParameterizedTest(name = "{0}")
@MethodSource("readbacks")
void readback(String name, Runnable body) {
body.run();
}
}
@@ -9,6 +9,7 @@ import org.lwjgl.opengl.GL13;
import java.nio.ByteBuffer;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors;
import static org.junit.jupiter.api.Assertions.assertEquals;
@GLCoreTest
@@ -71,8 +72,7 @@ public class DSAUnsupportedBindingTest {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, t);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, y);
GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, x);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {
}
drainErrors();
dsa.texParameteri(t, GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
@@ -7,15 +7,19 @@ import org.joml.Vector3f;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.stream.Stream;
import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.COLOR_BIT;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.floats;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -112,12 +116,6 @@ class FFPVertexLightingGLTest {
+ " light1=" + GLStateManager.getLightStates()[1].isEnabled();
}
private static FloatBuffer floats(float... values) {
final FloatBuffer buf = BufferUtils.createFloatBuffer(values.length);
buf.put(values).flip();
return buf;
}
private void buildTriangle() {
vao = GLStateManager.glGenVertexArrays();
GLStateManager.glBindVertexArray(vao);
@@ -156,9 +154,8 @@ class FFPVertexLightingGLTest {
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "FFP draw must not raise a GL error");
final ByteBuffer pixel = BufferUtils.createByteBuffer(4);
GL11.glReadPixels(400, 200, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixel);
return new float[] { (pixel.get(0) & 0xFF) / 255.0f, (pixel.get(1) & 0xFF) / 255.0f, (pixel.get(2) & 0xFF) / 255.0f };
final int[] pixel = FfpFixture.readPixel(400, 200);
return new float[] { pixel[0] / 255.0f, pixel[1] / 255.0f, pixel[2] / 255.0f };
}
private void assertSolverMatchesShader(String context) {
@@ -170,23 +167,20 @@ class FFPVertexLightingGLTest {
}
}
@Test
void faceOnNormalMatchesGeneratedShader() {
GLStateManager.glNormal3f(0.0f, 0.0f, 1.0f);
assertSolverMatchesShader("face-on normal");
static Stream<Arguments> normals() {
return Stream.of(
Arguments.of("face-on normal", 0.0f, 0.0f, 1.0f, Float.POSITIVE_INFINITY),
// A 0.0104-long normal must stay at the 0.4 light model ambient floor rather than pick up 0.6 per light
Arguments.of("sign normal", 0.0f, 0.0f, -0.010416667f, 0.45f),
Arguments.of("tilted normal", 0.3f, 0.8f, 0.5f, Float.POSITIVE_INFINITY));
}
@Test
void signNormalMatchesGeneratedShader() {
GLStateManager.glNormal3f(0.0f, 0.0f, -0.010416667f);
assertSolverMatchesShader("sign normal");
assertTrue(factor.x < 0.45f, "a 0.0104-long normal must stay at the 0.4 light model ambient floor rather than pick up 0.6 per light, got " + factor.x);
}
@Test
void tiltedNormalMatchesGeneratedShader() {
GLStateManager.glNormal3f(0.3f, 0.8f, 0.5f);
assertSolverMatchesShader("tilted normal");
@ParameterizedTest(name = "{0}")
@MethodSource("normals")
void normalMatchesGeneratedShader(String name, float x, float y, float z, float factorCeiling) {
GLStateManager.glNormal3f(x, y, z);
assertSolverMatchesShader(name);
assertTrue(factor.x < factorCeiling, name + ": factor must stay below " + factorCeiling + ", got " + factor.x);
}
@Test
@@ -206,27 +200,31 @@ class FFPVertexLightingGLTest {
assertTrue(factor.x > 1.2f, "normalize restores the unit normal, which must pick up more diffuse than rescale's 0.5-long one, got " + factor.x);
}
@Test
void materialEmissionIsNotRepresentable() {
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_EMISSION, floats(0.1f, 0.1f, 0.1f, 1.0f));
assertFalse(FFPVertexLighting.modulatesVertexColor(factor), "emission is additive, so the lit colour is not vertexColor * factor");
static Stream<Arguments> unrepresentableStates() {
return Stream.of(
unrepresentable("emission is additive, so the lit colour is not vertexColor * factor",
() -> GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_EMISSION, floats(0.1f, 0.1f, 0.1f, 1.0f))),
unrepresentable("specular is additive, so the lit colour is not vertexColor * factor", () -> {
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_SPECULAR, floats(1.0f, 1.0f, 1.0f, 1.0f));
GLStateManager.glMaterialf(GL11.GL_FRONT_AND_BACK, GL11.GL_SHININESS, 16.0f);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, floats(1.0f, 1.0f, 1.0f, 1.0f));
}),
unrepresentable("only AMBIENT_AND_DIFFUSE makes the lit colour scale with the vertex colour",
() -> GLStateManager.glColorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_EMISSION)));
}
private static Arguments unrepresentable(String reason, Runnable state) {
return Arguments.of(reason, state);
}
@ParameterizedTest(name = "{0}")
@MethodSource("unrepresentableStates")
void additiveLightingIsNotRepresentable(String reason, Runnable state) {
state.run();
assertFalse(FFPVertexLighting.modulatesVertexColor(factor), reason);
assertEquals(IDENTITY_FACTOR, factor, "a rejected solve must leave an identity factor");
}
@Test
void specularContributionIsNotRepresentable() {
GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_SPECULAR, floats(1.0f, 1.0f, 1.0f, 1.0f));
GLStateManager.glMaterialf(GL11.GL_FRONT_AND_BACK, GL11.GL_SHININESS, 16.0f);
GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, floats(1.0f, 1.0f, 1.0f, 1.0f));
assertFalse(FFPVertexLighting.modulatesVertexColor(factor), "specular is additive, so the lit colour is not vertexColor * factor");
}
@Test
void nonAmbientAndDiffuseColorMaterialIsNotRepresentable() {
GLStateManager.glColorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_EMISSION);
assertFalse(FFPVertexLighting.modulatesVertexColor(factor), "only AMBIENT_AND_DIFFUSE makes the lit colour scale with the vertex colour");
}
@Test
void lightsBeyondTheFfpCountAreIgnored() {
assertTrue(FFPVertexLighting.modulatesVertexColor(factor), FFPVertexLightingGLTest::gateState);
@@ -11,10 +11,10 @@ import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.floats;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
@@ -25,12 +25,6 @@ class FfpUniformBlockDedupTest {
return GL30.glGetInteger(GL31.GL_UNIFORM_BUFFER_START, FFPUniformBlock.BINDING_POINT);
}
private static FloatBuffer floats(float... values) {
final FloatBuffer buf = BufferUtils.createFloatBuffer(values.length);
buf.put(values).flip();
return buf;
}
private static DoubleBuffer doubles(double... values) {
final DoubleBuffer buf = BufferUtils.createDoubleBuffer(values.length);
buf.put(values).flip();
@@ -21,6 +21,7 @@ import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.COLOR_BIT;
import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.NORMAL_BIT;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -100,7 +101,7 @@ class InstancedFFPDrawGLTest {
GLSMHooks.blendHandler = new LoggingBlendHandler();
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 0);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
drainErrors();
assertEquals(List.of("resolve", "flush"), log);
}
@@ -36,7 +36,6 @@ class LogicOpGLTest {
GLStateManager.glDisable(GL11.GL_BLEND);
LogicOpFixture.deleteResources();
FfpFixture.resetFfpState();
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
@Test
@@ -48,7 +47,6 @@ class LogicOpGLTest {
assertRgb(op, src);
}
}
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
}
@Test
@@ -5,11 +5,15 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.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.GL15;
import java.nio.FloatBuffer;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -59,17 +63,18 @@ class WideLineClipGLTest {
GLStateManager.glLoadIdentity();
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
FfpFixture.resetFfpState();
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
@Test
void lineEndingBehindTheCameraKeepsItsWidth() {
assertConstantWidth(IN_FRONT, BEHIND);
static Stream<Arguments> linesCrossingTheNearPlane() {
return Stream.of(
Arguments.of("line ending behind the camera", IN_FRONT, BEHIND),
Arguments.of("line starting behind the camera", BEHIND, IN_FRONT));
}
@Test
void lineStartingBehindTheCameraKeepsItsWidth() {
assertConstantWidth(BEHIND, IN_FRONT);
@ParameterizedTest(name = "{0}")
@MethodSource("linesCrossingTheNearPlane")
void lineCrossingTheNearPlaneKeepsItsWidth(String name, float[] start, float[] end) {
assertConstantWidth(start, end);
}
@Test
@@ -144,7 +149,6 @@ class WideLineClipGLTest {
final int y = row, count = lit;
assertTrue(count > 0 && count <= LINE_WIDTH + 1, () -> "row " + y + " is " + count + " pixels wide");
}
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
}
private int[] drawLine(float[] start, float[] end) {
@@ -2,6 +2,9 @@ package com.gtnewhorizons.angelica.glsm.loading;
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
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.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
@@ -12,6 +15,7 @@ import org.objectweb.asm.tree.MethodNode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;
@@ -71,14 +75,16 @@ class ModRedirectorTest {
assertEquals(coreExclusions.length + 2, exclusions.size(), exclusions.toString());
}
@Test
void exclusionsCombineCoreLwjgl3AndExtra() {
assertStandardExclusions(redirector().getTransformerExclusions());
static Stream<Arguments> exclusionLists() {
return Stream.of(
Arguments.of("ModRedirector", redirector().getTransformerExclusions()),
Arguments.of("EarlyRedirectorCore", new EarlyRedirectorCore(EXTRA_EXCLUSION).getTransformerExclusions()));
}
@Test
void earlyRedirectorExclusionsCombineCoreLwjgl3AndExtra() {
assertStandardExclusions(new EarlyRedirectorCore(EXTRA_EXCLUSION).getTransformerExclusions());
@ParameterizedTest(name = "{0}")
@MethodSource("exclusionLists")
void exclusionsCombineCoreLwjgl3AndExtra(String name, String[] exclusions) {
assertStandardExclusions(exclusions);
}
@Test
@@ -86,22 +92,17 @@ class ModRedirectorTest {
assertNull(redirector().transform("com.example.TestClass", null, this));
}
@Test
void excludedPrefixReturnsOriginalArray() {
final byte[] original = bytesWithGlCall();
assertSame(original, redirector().transform(EXTRA_EXCLUSION + ".TestClass", original, this));
static Stream<Arguments> untouchedClasses() {
return Stream.of(
Arguments.of("extra exclusion prefix", EXTRA_EXCLUSION + ".TestClass", bytesWithGlCall()),
Arguments.of("core exclusion prefix", "org.lwjgl.opengl.TestClass", bytesWithGlCall()),
Arguments.of("no GL call", "com.example.TestClass", bytesWithoutGlCall()));
}
@Test
void coreExcludedPrefixReturnsOriginalArray() {
final byte[] original = bytesWithGlCall();
assertSame(original, redirector().transform("org.lwjgl.opengl.TestClass", original, this));
}
@Test
void unchangedClassReturnsOriginalArray() {
final byte[] original = bytesWithoutGlCall();
assertSame(original, redirector().transform("com.example.TestClass", original, this));
@ParameterizedTest(name = "{0}")
@MethodSource("untouchedClasses")
void untouchedClassReturnsOriginalArray(String name, String className, byte[] original) {
assertSame(original, redirector().transform(className, original, this));
}
@Test
@@ -1,6 +1,8 @@
package com.gtnewhorizons.angelica.glsm.profiling;
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;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -8,32 +10,26 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class TracyOptionsTest {
@Test
void resolveBooleanFallsBackToConfigWhenNoOverride() {
assertTrue(TracyOptions.resolveBoolean(null, true));
assertFalse(TracyOptions.resolveBoolean(null, false));
@ParameterizedTest(name = "{0}")
@CsvSource(value = {
"no override falls back to config true, null, true, true",
"no override falls back to config false, null, false, false",
"override true wins, true, false, true",
"override false wins, false, true, false"
}, nullValues = "null")
void resolveBoolean(String name, Boolean override, boolean config, boolean expected) {
assertEquals(expected, TracyOptions.resolveBoolean(override, config));
}
@Test
void resolveBooleanOverrideWinsEitherWay() {
assertTrue(TracyOptions.resolveBoolean(Boolean.TRUE, false));
assertFalse(TracyOptions.resolveBoolean(Boolean.FALSE, true));
}
@Test
void resolveMaxSrcLocsFallsBackToConfigWhenNoOverride() {
assertEquals(4096, TracyOptions.resolveMaxSrcLocs(null, 4096));
}
@Test
void resolveMaxSrcLocsOverrideWins() {
assertEquals(8192, TracyOptions.resolveMaxSrcLocs(8192, 4096));
}
@Test
void resolveMaxSrcLocsClampsBothConfigAndOverrideToTheMinimum() {
assertEquals(16, TracyOptions.resolveMaxSrcLocs(null, 4));
assertEquals(16, TracyOptions.resolveMaxSrcLocs(1, 4096));
@ParameterizedTest(name = "{0}")
@CsvSource(value = {
"no override falls back to config, null, 4096, 4096",
"override wins, 8192, 4096, 8192",
"config is clamped to the minimum, null, 4, 16",
"override is clamped to the minimum, 1, 4096, 16"
}, nullValues = "null")
void resolveMaxSrcLocs(String name, Integer override, int config, int expected) {
assertEquals(expected, TracyOptions.resolveMaxSrcLocs(override, config));
}
@Test
@@ -62,25 +62,6 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
MemoryUtilities.memFree(read);
}
@Test
void compileAndExecute_firesLiveAndBakes() {
final int vbo = defaultQuadVbo();
final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2);
final int vao = setupPosColorVao(vbo);
final int list = newList();
GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE);
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();
final BatchedIndexedDrawCmd cmd = firstBatchedCmd(list);
assertNotNull(cmd);
assertEquals(3, cmd.getIndexCount());
}
@Test
void quads_triangulateAtRecord() {
final int vbo = defaultQuadVbo();
@@ -457,11 +438,6 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture {
assertNotEquals(cmds.get(0).getSharedVAO(), cmds.get(1).getSharedVAO(), "VAOs from separate batches must be distinct");
}
@Test
void nullListProbeCompiles() {
assertNull(DisplayListManager.getDisplayList(0));
}
@Test
void multiLayoutDraws_emitInInsertionOrder() {
// Two distinct layouts. The builder uses a LinkedHashMap, so the first
@@ -1,24 +1,30 @@
package com.gtnewhorizons.angelica.glsm.recording;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GlTestSupport;
import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatch;
import com.gtnewhorizons.angelica.glsm.recording.support.DisplayListTestFixture;
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.opengl.GL15;
import org.lwjgl.opengl.GL20;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors;
import static org.junit.jupiter.api.Assertions.assertEquals;
class IndexedDrawDeleteSafetyTest extends DisplayListTestFixture {
@Test
void vaoEboMapSweep_clearsStaleEntryOnForeignVao() {
@ParameterizedTest(name = "replayed before delete={0}")
@ValueSource(booleans = {false, true})
void listDelete_clearsSharedEboFromForeignVao(boolean replayFirst) {
final int vbo = defaultQuadVbo();
final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2, 3);
final int vao = setupPosColorVao(vbo);
final int list = recordSingleIndexedDraw(vao, ebo, GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_SHORT);
if (replayFirst) compiledOf(list).render();
final IndexedDrawBatch batch = firstBatch(list);
final int sharedEbo = batch.getSharedEBO();
@@ -31,32 +37,13 @@ class IndexedDrawDeleteSafetyTest extends DisplayListTestFixture {
untrackList(list);
GLStateManager.glBindVertexArray(foreignVao);
assertEquals(0, GLStateManager.getBoundEBO(), "vaoEboMap still references the freed sharedEBO — non-gen crash imminent");
GLStateManager.glBindVertexArray(0);
}
assertEquals(0, GLStateManager.getBoundEBO(), "vaoEboMap still references the freed sharedEBO - non-gen crash imminent");
@Test
void bindBufferCycle_afterListDelete_isGlNoError() {
final int vbo = defaultQuadVbo();
final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2, 3);
final int vao = setupPosColorVao(vbo);
final int list = recordSingleIndexedDraw(vao, ebo, GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_SHORT);
final int sharedEbo = firstBatch(list).getSharedEBO();
final int foreignVao = newVao();
GLStateManager.glBindVertexArray(foreignVao);
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, sharedEbo);
GLStateManager.glBindVertexArray(0);
GLStateManager.glDeleteLists(list, 1);
untrackList(list);
GLStateManager.glBindVertexArray(foreignVao);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
drainErrors();
final int prevEbo = GLStateManager.getBoundEBO();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
final int err = GL11.glGetError();
assertEquals(GL11.GL_NO_ERROR, err, () -> "bindBuffer(prevEbo=" + prevEbo + ") hit GL 0x" + Integer.toHexString(err) + " — cache carried a freed id through a VAO rebind");
assertEquals(GL11.GL_NO_ERROR, err, () -> "bindBuffer(prevEbo=" + prevEbo + ") hit GL 0x" + Integer.toHexString(err) + " - cache carried a freed id through a VAO rebind");
GLStateManager.glBindVertexArray(0);
}
@@ -85,8 +72,8 @@ class IndexedDrawDeleteSafetyTest extends DisplayListTestFixture {
@Test
void glDeleteProgram_zero_isNoOpAndLeavesActiveProgram() {
final int prog = linkMinimalProgram();
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
final int prog = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS);
drainErrors();
GLStateManager.glUseProgram(prog);
assertEquals(prog, GLStateManager.getActiveProgram());
@@ -97,13 +84,13 @@ class IndexedDrawDeleteSafetyTest extends DisplayListTestFixture {
GLStateManager.glUseProgram(0);
GLStateManager.glDeleteProgram(prog);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
drainErrors();
}
@Test
void activeProgram_retainedAfterDelete_perGlSpec() {
final int prog = linkMinimalProgram();
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
final int prog = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS);
drainErrors();
GLStateManager.glUseProgram(prog);
assertEquals(prog, GLStateManager.getActiveProgram());
@@ -114,38 +101,6 @@ class IndexedDrawDeleteSafetyTest extends DisplayListTestFixture {
GLStateManager.glUseProgram(0);
assertEquals(0, GLStateManager.getActiveProgram(), "activeProgram clears once the deleted program is unbound");
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
drainErrors();
}
private static int linkMinimalProgram() {
final int vert = GLStateManager.glCreateShader(GL20.GL_VERTEX_SHADER);
GLStateManager.glShaderSource(vert, MIN_VERT_SRC);
GLStateManager.glCompileShader(vert);
if (GL20.glGetShaderi(vert, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
throw new AssertionError("vertex shader failed to compile: " + GL20.glGetShaderInfoLog(vert, 4096));
}
final int frag = GLStateManager.glCreateShader(GL20.GL_FRAGMENT_SHADER);
GLStateManager.glShaderSource(frag, MIN_FRAG_SRC);
GLStateManager.glCompileShader(frag);
if (GL20.glGetShaderi(frag, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
throw new AssertionError("fragment shader failed to compile: " + GL20.glGetShaderInfoLog(frag, 4096));
}
final int prog = GLStateManager.glCreateProgram();
GLStateManager.glAttachShader(prog, vert);
GLStateManager.glAttachShader(prog, frag);
GLStateManager.glLinkProgram(prog);
if (GL20.glGetProgrami(prog, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
throw new AssertionError("program failed to link: " + GL20.glGetProgramInfoLog(prog, 4096));
}
GLStateManager.glDetachShader(prog, vert);
GLStateManager.glDetachShader(prog, frag);
GLStateManager.glDeleteShader(vert);
GLStateManager.glDeleteShader(frag);
return prog;
}
private static final String MIN_VERT_SRC = "#version 330 core\nvoid main(){gl_Position=vec4(0.0);}";
private static final String MIN_FRAG_SRC = "#version 330 core\nout vec4 oC;void main(){oC=vec4(1.0);}";
}
@@ -15,6 +15,7 @@ import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer;
import java.util.List;
import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -36,7 +37,7 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
GL11.glViewport(0, 0, 16, 16);
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
drainErrors();
final int query = GL15.glGenQueries();
GL15.glBeginQuery(GL15.GL_SAMPLES_PASSED, query);
@@ -92,7 +93,7 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
GLStateManager.glBindVertexArray(distractorVao);
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
drainErrors();
compiledOf(list).render();
@@ -103,35 +104,6 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
GLStateManager.glBindVertexArray(0);
}
@Test
void replayThenDelete_thenBindBufferCycle_isGlNoError() {
final int vbo = defaultQuadVbo();
final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2, 3);
final int vao = setupPosColorVao(vbo);
final int list = recordSingleIndexedDraw(vao, ebo, GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_SHORT);
compiledOf(list).render();
final int sharedEbo = firstBatch(list).getSharedEBO();
final int foreignVao = newVao();
GLStateManager.glBindVertexArray(foreignVao);
GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, sharedEbo);
GLStateManager.glBindVertexArray(0);
GLStateManager.glDeleteLists(list, 1);
untrackList(list);
GLStateManager.glBindVertexArray(foreignVao);
assertEquals(0, GLStateManager.getBoundEBO(), "batched EBO id survived list delete in vaoEboMap — non-gen crash imminent");
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
final int prevEbo = GLStateManager.getBoundEBO();
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo);
final int err = GL11.glGetError();
assertEquals(GL11.GL_NO_ERROR, err, () -> "post-delete bindBuffer(" + prevEbo + ") hit GL 0x" + Integer.toHexString(err));
GLStateManager.glBindVertexArray(0);
}
@Test
void execute_doesNotRestoreVAO_backToBackDraws() {
final int vbo = defaultQuadVbo();
@@ -153,7 +125,7 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
final int distractor = newVao();
GLStateManager.glBindVertexArray(distractor);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
drainErrors();
compiledOf(list).render();
@@ -229,7 +201,7 @@ class IndexedDrawReplayTest extends DisplayListTestFixture {
GLStateManager.glDrawElements(GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_SHORT, 0L);
GLStateManager.glEndList();
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
drainErrors();
compiledOf(list).render();
compiledOf(list).render();
@@ -3,10 +3,8 @@ package com.gtnewhorizons.angelica.glsm.recording;
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
import com.gtnewhorizons.angelica.glsm.GLCompatTest;
import com.gtnewhorizons.angelica.glsm.DisplayListManager;
import com.gtnewhorizons.angelica.glsm.GLCompatTest;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.lwjgl.opengl.GL11;
@@ -33,37 +31,6 @@ class RecordingDataStructuresTest {
}
}
@Test
void testCompiledDisplayListCreation() {
// Create a display list with PushMatrix, Scale, PopMatrix
testList = GL11.glGenLists(1);
GLStateManager.glNewList(testList, GL11.GL_COMPILE);
GLStateManager.glPushMatrix();
GLStateManager.glScalef(2.0f, 2.0f, 2.0f);
GLStateManager.glPopMatrix();
GLStateManager.glEndList();
// Verify the compiled display list
CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList);
assertNotNull(compiled, "Display list should be compiled");
assertNotNull(compiled.getCommandBuffer(), "Command buffer should not be null");
// Get command counts to verify structure
Int2IntMap counts = compiled.getCommandCounts();
// Should have PushMatrix and PopMatrix
assertEquals(1, counts.getOrDefault(GLCommand.PUSH_MATRIX, 0), "Should have 1 PushMatrix");
assertEquals(1, counts.getOrDefault(GLCommand.POP_MATRIX, 0), "Should have 1 PopMatrix");
// Scale should be collapsed into a MultMatrix (optimizer collapses transforms)
// Or if in debug mode, might have individual commands - just check we have some transforms
int transformCount = counts.getOrDefault(GLCommand.MULT_MATRIX, 0)
+ counts.getOrDefault(GLCommand.SCALE, 0);
assertTrue(transformCount >= 0, "Should have transform commands");
}
@Test
void testImmediateModeRecorder() {
DirectTessellator recorder = ImmediateModeRecorder.getInternalTessellator();
@@ -1,8 +1,13 @@
package com.gtnewhorizons.angelica.glsm.recording.commands;
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 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.assertNotEquals;
@@ -15,59 +20,50 @@ class AttribLayoutKeyTest {
return out;
}
@Test
void standardPosColorUvNormal_isAlignedAndPacked() {
final AttribLayoutKey k = new AttribLayoutKey(
new int[]{0, 1, 2, 4},
new int[]{3, 4, 2, 3},
new int[]{GL11.GL_FLOAT, GL11.GL_UNSIGNED_BYTE, GL11.GL_FLOAT, GL11.GL_BYTE},
new boolean[]{false, true, false, true});
assertArrayEquals(new int[]{0, 12, 16, 24}, offsetsOf(k));
assertEquals(28, k.stride());
static Stream<Arguments> layouts() {
return Stream.of(
Arguments.of("standard pos/color/uv/normal is aligned and packed",
new AttribLayoutKey(
new int[]{0, 1, 2, 4},
new int[]{3, 4, 2, 3},
new int[]{GL11.GL_FLOAT, GL11.GL_UNSIGNED_BYTE, GL11.GL_FLOAT, GL11.GL_BYTE},
new boolean[]{false, true, false, true}),
new int[]{0, 12, 16, 24}, 28),
Arguments.of("pos/uv/normal3 rounds the tail stride to 24",
new AttribLayoutKey(
new int[]{0, 2, 4},
new int[]{3, 2, 3},
new int[]{GL11.GL_FLOAT, GL11.GL_FLOAT, GL11.GL_BYTE},
new boolean[]{false, false, true}),
new int[]{0, 12, 20}, 24),
Arguments.of("byte attrib in the middle rounds the next offset",
new AttribLayoutKey(
new int[]{0, 4, 2},
new int[]{3, 3, 2},
new int[]{GL11.GL_FLOAT, GL11.GL_BYTE, GL11.GL_FLOAT},
new boolean[]{false, true, false}),
new int[]{0, 12, 16}, 24),
Arguments.of("all-byte layout packs tight",
new AttribLayoutKey(
new int[]{0, 1, 2},
new int[]{3, 4, 2},
new int[]{GL11.GL_BYTE, GL11.GL_UNSIGNED_BYTE, GL11.GL_BYTE},
new boolean[]{true, true, false}),
new int[]{0, 3, 7}, 9),
Arguments.of("shorts after a byte round offset and stride to even",
new AttribLayoutKey(
new int[]{0, 1},
new int[]{1, 2},
new int[]{GL11.GL_BYTE, GL11.GL_SHORT},
new boolean[]{true, false}),
new int[]{0, 2}, 6));
}
@Test
void posUvNormal3_roundsTailStrideTo24() {
final AttribLayoutKey k = new AttribLayoutKey(
new int[]{0, 2, 4},
new int[]{3, 2, 3},
new int[]{GL11.GL_FLOAT, GL11.GL_FLOAT, GL11.GL_BYTE},
new boolean[]{false, false, true});
assertArrayEquals(new int[]{0, 12, 20}, offsetsOf(k));
assertEquals(24, k.stride());
}
@Test
void interiorHazardOrder_roundsMidAttribOffset() {
final AttribLayoutKey k = new AttribLayoutKey(
new int[]{0, 4, 2},
new int[]{3, 3, 2},
new int[]{GL11.GL_FLOAT, GL11.GL_BYTE, GL11.GL_FLOAT},
new boolean[]{false, true, false});
assertArrayEquals(new int[]{0, 12, 16}, offsetsOf(k));
assertEquals(24, k.stride());
}
@Test
void allByteLayout_packsTight() {
final AttribLayoutKey k = new AttribLayoutKey(
new int[]{0, 1, 2},
new int[]{3, 4, 2},
new int[]{GL11.GL_BYTE, GL11.GL_UNSIGNED_BYTE, GL11.GL_BYTE},
new boolean[]{true, true, false});
assertArrayEquals(new int[]{0, 3, 7}, offsetsOf(k));
assertEquals(9, k.stride());
}
@Test
void shortsFollowingByte_roundToEvenAndStrideToEven() {
final AttribLayoutKey k = new AttribLayoutKey(
new int[]{0, 1},
new int[]{1, 2},
new int[]{GL11.GL_BYTE, GL11.GL_SHORT},
new boolean[]{true, false});
assertArrayEquals(new int[]{0, 2}, offsetsOf(k));
assertEquals(6, k.stride());
@ParameterizedTest(name = "{0}")
@MethodSource("layouts")
void offsetsAndStride(String name, AttribLayoutKey key, int[] offsets, int stride) {
assertArrayEquals(offsets, offsetsOf(key));
assertEquals(stride, key.stride());
}
@Test
@@ -5,6 +5,8 @@ import com.gtnewhorizons.angelica.config.SystemProperties.UnmappedGLMode;
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
@@ -16,7 +18,6 @@ import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
@@ -32,19 +33,8 @@ class GLSMRedirectorTest {
private static final String GLSM = GLSMRedirector.getTargetClassName();
@SuppressWarnings("unchecked")
private static Set<String> seenSet(String field) {
try {
final Field f = GLSMRedirector.class.getDeclaredField(field);
f.setAccessible(true);
return (Set<String>) f.get(null);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
private static Set<String> unmappedSeen() { return seenSet("UNMAPPED_SEEN"); }
private static Set<String> debugSeen() { return seenSet("DEBUG_SEEN"); }
private static Set<String> unmappedSeen() { return Reflect.getStatic(GLSMRedirector.class, "UNMAPPED_SEEN"); }
private static Set<String> debugSeen() { return Reflect.getStatic(GLSMRedirector.class, "DEBUG_SEEN"); }
@BeforeEach
void reset() {
@@ -112,35 +102,25 @@ class GLSMRedirectorTest {
return (Handle) dyn.bsmArgs[1];
}
@Test
void invokeDynamicGL11HandleIsRedirected() {
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/GL11", "glPopMatrix", "()V", false);
final ClassNode cn = makeClassWithInvokeDynamic(impl);
@ParameterizedTest(name = "{0}")
@CsvSource({
"GL11, org/lwjgl/opengl/GL11, glPopMatrix, ()V, glPopMatrix",
"extension class with a renamed target, org/lwjgl/opengl/EXTBlendFuncSeparate, glBlendFuncSeparateEXT, (IIII)V, tryBlendFuncSeparate",
"GLxxC variant, org/lwjgl/opengl/GL11C, glDrawArrays, (III)V, glDrawArrays"
})
void invokeDynamicHandleIsRedirected(String name, String owner, String method, String desc, String expectedName) {
final ClassNode cn = makeClassWithInvokeDynamic(new Handle(Opcodes.H_INVOKESTATIC, owner, method, desc, false));
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
assertTrue(changed);
final Handle result = getResultHandle(cn);
assertEquals(GLSM, result.getOwner());
assertEquals("glPopMatrix", result.getName());
assertEquals("()V", result.getDesc());
assertEquals(expectedName, result.getName());
assertEquals(desc, result.getDesc());
assertEquals(Opcodes.H_INVOKESTATIC, result.getTag());
}
@Test
void invokeDynamicNamedClassHandleIsRedirected() {
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/EXTBlendFuncSeparate", "glBlendFuncSeparateEXT", "(IIII)V", false);
final ClassNode cn = makeClassWithInvokeDynamic(impl);
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
assertTrue(changed);
final Handle result = getResultHandle(cn);
assertEquals(GLSM, result.getOwner());
assertEquals("tryBlendFuncSeparate", result.getName());
assertEquals("(IIII)V", result.getDesc());
}
@Test
void invokeDynamicNonGLHandleIsNotTouched() {
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
@@ -188,6 +168,7 @@ class GLSMRedirectorTest {
@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());
}
@@ -240,54 +221,16 @@ class GLSMRedirectorTest {
assertTrue(unmappedSeen().isEmpty());
}
@Test
void newlyMappedFramebufferAliasIsRedirected() {
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/EXTFramebufferObject", "glBindFramebufferEXT", "(II)V");
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
assertTrue(changed);
assertEquals(GLSM, firstCall(cn).owner);
assertEquals("glBindFramebuffer", firstCall(cn).name);
assertTrue(unmappedSeen().isEmpty());
}
@Test
void arbGenerateMipmapAliasIsRedirected() {
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/ARBFramebufferObject", "glGenerateMipmap", "(I)V");
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
assertTrue(changed);
assertEquals(GLSM, firstCall(cn).owner);
assertEquals("glGenerateMipmap", firstCall(cn).name);
assertEquals("(I)V", firstCall(cn).desc);
assertTrue(unmappedSeen().isEmpty());
}
@Test
void extGenerateMipmapAliasIsRedirected() {
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/EXTFramebufferObject", "glGenerateMipmapEXT", "(I)V");
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
assertTrue(changed);
assertEquals(GLSM, firstCall(cn).owner);
assertEquals("glGenerateMipmap", firstCall(cn).name);
assertEquals("(I)V", firstCall(cn).desc);
assertTrue(unmappedSeen().isEmpty());
}
@Test
void newlyMappedReadPixelsIsRedirected() {
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/GL11", "glReadPixels", "(IIIIIILjava/nio/IntBuffer;)V");
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
assertTrue(changed);
assertEquals(GLSM, firstCall(cn).owner);
assertEquals("glReadPixels", firstCall(cn).name);
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
@@ -299,47 +242,29 @@ class GLSMRedirectorTest {
});
}
@Test
void newlyMappedOcclusionQueryAliasIsRedirected() {
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/ARBOcclusionQuery", "glGenQueriesARB", "(Ljava/nio/IntBuffer;)V");
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
assertTrue(changed);
assertEquals(GLSM, firstCall(cn).owner);
assertEquals("glGenQueries", firstCall(cn).name);
assertTrue(unmappedSeen().isEmpty());
}
@Test
void utilCheckGLErrorIsRedirected() throws Exception {
assertRedirectedToDeclaredTarget(new String[][]{{"org/lwjgl/opengl/Util", "checkGLError", "()V"}});
}
@Test
void indexedGettersHaveRealTargets() throws Exception {
final Class<?> glsm = Class.forName(GLSM.replace('/', '.'), false, getClass().getClassLoader());
assertDoesNotThrow(() -> glsm.getDeclaredMethod("glGetInteger", int.class, int.class), "LWJGL2 GL30.glGetInteger(pname, index) redirects here");
assertDoesNotThrow(() -> glsm.getDeclaredMethod("glGetIntegeri", int.class, int.class), "LWJGL3 GL31C.glGetIntegeri(pname, index) redirects here");
assertDoesNotThrow(() -> glsm.getDeclaredMethod("glGetIntegeri_v", int.class, int.class, java.nio.IntBuffer.class));
assertDoesNotThrow(() -> glsm.getDeclaredMethod("glGetBooleani_v", int.class, int.class, java.nio.ByteBuffer.class));
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"},
});
}
@Test
void indexedGettersAreRedirected() {
assertRedirected("org/lwjgl/opengl/GL30", "glGetInteger", "(II)I", "glGetInteger");
assertRedirected("org/lwjgl/opengl/GL31C", "glGetIntegeri", "(II)I", "glGetIntegeri");
assertRedirected("org/lwjgl/opengl/GL30", "glGetIntegeri_v", "(IILjava/nio/IntBuffer;)V", "glGetIntegeri_v");
assertRedirected("org/lwjgl/opengl/GL30", "glGetBooleani_v", "(IILjava/nio/ByteBuffer;)V", "glGetBooleani_v");
}
private void assertRedirected(String owner, String name, String desc, String expectedName) {
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);
assertTrue(changed, owner + "." + name + " should redirect");
assertEquals(GLSM, firstCall(cn).owner);
assertEquals(expectedName, firstCall(cn).name);
assertTrue(unmappedSeen().isEmpty(), owner + "." + name + " should not be recorded");
return firstCall(cn);
}
private static boolean glsmDeclares(String name, String desc) throws ClassNotFoundException {
@@ -465,20 +390,6 @@ class GLSMRedirectorTest {
assertRedirected("org/lwjgl/opengl/APPLEVertexArrayObject", "glGenVertexArraysAPPLE", "()I", "glGenVertexArrays");
}
@Test
void invokeDynamicGLCVariantIsRedirected() {
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/GL11C", "glDrawArrays", "(III)V", false);
final ClassNode cn = makeClassWithInvokeDynamic(impl);
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
assertTrue(changed);
final Handle result = getResultHandle(cn);
assertEquals(GLSM, result.getOwner());
assertEquals("glDrawArrays", result.getName());
assertEquals("(III)V", result.getDesc());
}
private static final String AWARE_SHIM = "com/gtnewhorizons/angelica/sdlgpu/compat/AwareGLDebugShim";
private static final String CALLBACK_I = "Lorg/lwjgl/opengl/GLDebugMessageCallbackI;";
@@ -502,15 +413,6 @@ class GLSMRedirectorTest {
assertEquals("glDebugMessageCallback", firstCall(cn).name);
}
@Test
void nonAwareDebugMessageCallbackTargetsGLStateManager() {
final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/KHRDebug", "glDebugMessageCallback", "(Lorg/lwjgl/opengl/KHRDebugCallback;)V");
assertTrue(new GLSMRedirector().transformClassNode("com.example.TestClass", cn, false));
assertEquals(GLSM, firstCall(cn).owner);
}
@Test
void awareGLUtilSetupDebugMessageCallbackIsRedirected() {
for (String desc : new String[]{"()Lorg/lwjgl/system/Callback;", "(Ljava/io/PrintStream;)Lorg/lwjgl/system/Callback;"}) {
@@ -5,6 +5,7 @@ import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
@@ -16,14 +17,15 @@ import java.util.stream.IntStream;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
public class GLSMUtil {
// Cached GL limits (queried once at class load time)
private static final int MAX_CLIP_PLANES = GL11.glGetInteger(GL11.GL_MAX_CLIP_PLANES);
private static final int MAX_LIGHTS = GL11.glGetInteger(GL11.GL_MAX_LIGHTS);
private static final int MAX_TEXTURE_UNITS = GL11.glGetInteger(GL13.GL_MAX_TEXTURE_UNITS);
// Compat-only pnames, so they load with resetGLState() and not with the class: a core driver answers GL_INVALID_ENUM
private static final class CompatLimits {
static final int MAX_CLIP_PLANES = GL11.glGetInteger(GL11.GL_MAX_CLIP_PLANES);
static final int MAX_LIGHTS = GL11.glGetInteger(GL11.GL_MAX_LIGHTS);
static final int MAX_TEXTURE_UNITS = GL11.glGetInteger(GL13.GL_MAX_TEXTURE_UNITS);
}
// MAP1 evaluator constants for DRY loops
private static final int[] MAP1_STATES = {
@@ -76,13 +78,25 @@ public class GLSMUtil {
GL11.GL_FOG_MODE, GL11.GL_FOG_INDEX,
// Also removed from core profile
GL11.GL_POINT_SMOOTH, GL11.GL_POLYGON_STIPPLE,
GL11.GL_INDEX_LOGIC_OP, GL11.GL_AUTO_NORMAL));
GL11.GL_INDEX_LOGIC_OP, GL11.GL_AUTO_NORMAL,
GL13.GL_CLIENT_ACTIVE_TEXTURE,
GL11.GL_VERTEX_ARRAY, GL11.GL_NORMAL_ARRAY, GL11.GL_COLOR_ARRAY, GL11.GL_TEXTURE_COORD_ARRAY,
GL11.GL_INDEX_ARRAY, GL11.GL_EDGE_FLAG_ARRAY, GL14.GL_SECONDARY_COLOR_ARRAY, GL14.GL_FOG_COORDINATE_ARRAY,
GL11.GL_VERTEX_ARRAY_SIZE, GL11.GL_VERTEX_ARRAY_TYPE, GL11.GL_VERTEX_ARRAY_STRIDE));
for (int cap : MAP1_STATES) caps.add(cap);
for (int cap : MAP2_STATES) caps.add(cap);
return caps;
}
// A core driver rejects these caps with GL_INVALID_ENUM. Only that error is dropped: one already pending is the test's.
private static boolean driverBoolean(int glCap, boolean isEnabled) {
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "GL error pending before the driver query");
final boolean value = isEnabled ? GL11.glIsEnabled(glCap) : GL11.glGetBoolean(glCap);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
return value;
}
private static boolean isFFPOnly(int glCap) {
return FFP_ONLY_CAPS.contains(glCap);
}
@@ -94,7 +108,7 @@ public class GLSMUtil {
public static void verifyIsEnabled(int glCap, boolean expected, String message) {
if (isFFPOnly(glCap)) {
assertAll(message,
() -> assertFalse(GL11.glIsEnabled(glCap), "GL State Mismatch - cache-only cap reached the driver"),
() -> assertFalse(driverBoolean(glCap, true), "GL State Mismatch - cache-only cap reached the driver"),
() -> assertEquals(expected, GLStateManager.glIsEnabled(glCap), "GLSM State Mismatch")
);
} else {
@@ -112,7 +126,7 @@ public class GLSMUtil {
public static void verifyState(int glCap, boolean expected, String message) {
if (isFFPOnly(glCap)) {
assertAll(message,
() -> assertFalse(GL11.glGetBoolean(glCap), "GL State Mismatch - cache-only cap reached the driver"),
() -> assertFalse(driverBoolean(glCap, false), "GL State Mismatch - cache-only cap reached the driver"),
() -> assertEquals(expected, GLStateManager.glGetBoolean(glCap), "GLSM State Mismatch")
);
} else {
@@ -208,22 +222,6 @@ public class GLSMUtil {
}
}
public static void verifyNotDefaultState(int glCap, float[] expected, String message) {
if (isFFPOnly(glCap)) {
GLStateManager.glGetFloat(glCap, (FloatBuffer) glsmFloatBuffer.clear());
IntStream.range(0, expected.length).forEach(i ->
assertNotEquals(expected[i], glsmFloatBuffer.get(i), 0.0001f, message + " - GLSM State Mismatch: " + i)
);
} else {
GL11.glGetFloat(glCap, (FloatBuffer) glFloatBuffer.clear());
GLStateManager.glGetFloat(glCap, (FloatBuffer) glsmFloatBuffer.clear());
IntStream.range(0, expected.length).forEach(i -> assertAll(message,
() -> assertNotEquals(expected[i], glFloatBuffer.get(i), 0.0001f, "GL State Mismatch: " + i),
() -> assertNotEquals(expected[i], glsmFloatBuffer.get(i), 0.0001f, "GLSM State Mismatch: " + i)
));
}
}
public static void verifyState(int glCap, boolean[] expected) {
verifyState(glCap, expected, "Boolean State Mismatch");
}
@@ -275,7 +273,7 @@ public class GLSMUtil {
GLStateManager.disableBlend();
// Disable all clip planes (using cached MAX_CLIP_PLANES)
for(int i = 0; i < MAX_CLIP_PLANES; i++) {
for(int i = 0; i < CompatLimits.MAX_CLIP_PLANES; i++) {
GLStateManager.glDisable(GL11.GL_CLIP_PLANE0 + i);
}
@@ -290,7 +288,7 @@ public class GLSMUtil {
GLStateManager.disableLighting();
// Disable all lights (using cached MAX_LIGHTS)
for(int i = 0; i < MAX_LIGHTS; i++) {
for(int i = 0; i < CompatLimits.MAX_LIGHTS; i++) {
GLStateManager.disableLight(i);
}
@@ -330,7 +328,7 @@ public class GLSMUtil {
GLStateManager.glDisable(GL11.GL_STENCIL_TEST);
// Reset texture state for all units (using cached MAX_TEXTURE_UNITS)
for(int i = 0; i < MAX_TEXTURE_UNITS; i++) {
for(int i = 0; i < CompatLimits.MAX_TEXTURE_UNITS; i++) {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + i);
GLStateManager.glDisable(GL11.GL_TEXTURE_1D);
GLStateManager.disableTexture(); // GL_TEXTURE_2D
@@ -383,9 +381,9 @@ public class GLSMUtil {
public static void setClientState(int glCap, boolean state) {
if (state) {
GL11.glEnableClientState(glCap);
GLStateManager.glEnableClientState(glCap);
} else {
GL11.glDisableClientState(glCap);
GLStateManager.glDisableClientState(glCap);
}
}
}
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.config.SystemProperties;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
@@ -14,12 +15,13 @@ import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.PixelFormat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
/**
* 3.3 core forward-compatible context. FFP emulation stays enabled, so per-test isolation only deactivates it.
*/
public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback, ExtensionContext.Store.CloseableResource {
public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback, ExtensionContext.Store.CloseableResource {
private static boolean started = false;
private static Throwable startFailure;
@@ -75,6 +77,13 @@ public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback,
}
}
@Override
public void afterEach(ExtensionContext context) {
final int error = GL11.glGetError();
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
assertEquals(GL11.GL_NO_ERROR, error, () -> "GL Error: 0x" + Integer.toHexString(error));
}
@Override
public void close() {
if (Display.isCreated()) Display.destroy();
@@ -0,0 +1,82 @@
package com.gtnewhorizons.angelica.glsm;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import java.nio.FloatBuffer;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.fail;
public final class GlTestSupport {
public static final String TRIVIAL_VS = "#version 330 core\nvoid main() { gl_Position = vec4(0.0); }\n";
public static final String TRIVIAL_FS = "#version 330 core\nout vec4 color;\nvoid main() { color = vec4(1.0); }\n";
private GlTestSupport() {}
public static void drainErrors() {
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
}
public static int compile(int type, String source) {
final int shader = GLStateManager.glCreateShader(type);
GLStateManager.glShaderSource(shader, source);
GLStateManager.glCompileShader(shader);
if (GLStateManager.glGetShaderi(shader, GL20.GL_COMPILE_STATUS) == 0) {
fail("test shader failed to compile:\n" + GLStateManager.glGetShaderInfoLog(shader, 4096) + "\n\n" + source);
}
return shader;
}
/** Links and deletes the two shaders. */
public static int link(int vs, int fs) {
final int p = GLStateManager.glCreateProgram();
GLStateManager.glAttachShader(p, vs);
GLStateManager.glAttachShader(p, fs);
GLStateManager.glLinkProgram(p);
assertNotEquals(0, GLStateManager.glGetProgrami(p, GL20.GL_LINK_STATUS), () -> "test program must link: " + GLStateManager.glGetProgramInfoLog(p, 4096));
GLStateManager.glDeleteShader(vs);
GLStateManager.glDeleteShader(fs);
return p;
}
public static int program(String vertexSource, String fragmentSource) {
return link(compile(GL20.GL_VERTEX_SHADER, vertexSource), compile(GL20.GL_FRAGMENT_SHADER, fragmentSource));
}
/** Raw driver GL_TEXTURE_BINDING_2D of a unit, bypassing the GLSM cache. */
public static int driverTextureBinding(int unit) {
final int saved = GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE);
GL13.glActiveTexture(GL13.GL_TEXTURE0 + unit);
final int binding = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
GL13.glActiveTexture(saved);
return binding;
}
public static FloatBuffer floats(float... values) {
final FloatBuffer buf = BufferUtils.createFloatBuffer(values.length);
buf.put(values).flip();
return buf;
}
/** GLSM's answer for a 16-float matrix pname. */
public static FloatBuffer matrix(int pname) {
final FloatBuffer buf = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(pname, buf);
return buf;
}
public static FloatBuffer modelview() {
return matrix(GL11.GL_MODELVIEW_MATRIX);
}
public static void assertMatrixEquals(FloatBuffer expected, FloatBuffer actual, String message) {
for (int i = 0; i < 16; i++) {
assertEquals(expected.get(i), actual.get(i), 0.001f, message + " (element " + i + ")");
}
}
}
@@ -0,0 +1,84 @@
package com.gtnewhorizons.angelica.glsm;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL20;
import java.util.Arrays;
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 final class LegacyQueryFixture {
public static final int[] CLIENT_ARRAY_CAPS = {
GL11.GL_VERTEX_ARRAY, GL11.GL_NORMAL_ARRAY, GL11.GL_COLOR_ARRAY, GL11.GL_TEXTURE_COORD_ARRAY,
GL11.GL_INDEX_ARRAY, GL11.GL_EDGE_FLAG_ARRAY, GL14.GL_SECONDARY_COLOR_ARRAY, GL14.GL_FOG_COORDINATE_ARRAY
};
public static final int[] HINT_TARGETS = {
GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_POINT_SMOOTH_HINT, GL11.GL_LINE_SMOOTH_HINT, GL11.GL_POLYGON_SMOOTH_HINT,
GL11.GL_FOG_HINT, GL13.GL_TEXTURE_COMPRESSION_HINT, GL14.GL_GENERATE_MIPMAP_HINT, GL20.GL_FRAGMENT_SHADER_DERIVATIVE_HINT
};
private LegacyQueryFixture() {}
/** Every pname LegacyQueries answers from the cache. */
public static int[] legacyPnames() {
final int[] out = new int[0x10000];
int n = 0;
for (int pname = 0; pname <= 0xFFFF; pname++) {
if (LegacyQueries.desc(pname) != 0) {
out[n++] = pname;
}
}
return Arrays.copyOf(out, n);
}
public static String hex(int pname) {
return "0x" + Integer.toHexString(pname);
}
public static void setEnabled(int cap, boolean on) {
if (on) {
GLStateManager.glEnable(cap);
} else {
GLStateManager.glDisable(cap);
}
}
public static void popAttribTo(int base) {
while (GLStateManager.getAttribDepth() > base) {
GLStateManager.glPopAttrib();
}
}
public static void drainClientAttribStack() {
while (GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH) > 0) {
GLStateManager.glPopClientAttrib();
}
}
/** Enables then disables {@code cap}, checking glIsEnabled, glGetBoolean and glGetInteger at each step. */
public static void assertEnableRoundTrip(int cap, boolean clientState) {
final String name = hex(cap);
if (clientState) {
GLStateManager.glEnableClientState(cap);
} else {
GLStateManager.glEnable(cap);
}
assertTrue(GLStateManager.glIsEnabled(cap), "enabled isEnabled " + name);
assertTrue(GLStateManager.glGetBoolean(cap), "enabled getBoolean " + name);
assertEquals(1, GLStateManager.glGetInteger(cap), "enabled getInteger " + name);
if (clientState) {
GLStateManager.glDisableClientState(cap);
} else {
GLStateManager.glDisable(cap);
}
assertFalse(GLStateManager.glIsEnabled(cap), "disabled isEnabled " + name);
assertFalse(GLStateManager.glGetBoolean(cap), "disabled getBoolean " + name);
assertEquals(0, GLStateManager.glGetInteger(cap), "disabled getInteger " + name);
}
}
@@ -4,20 +4,18 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GlTestSupport;
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 java.nio.ByteBuffer;
import java.util.function.IntFunction;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
public final class FfpFixture {
@@ -128,26 +126,7 @@ public final class FfpFixture {
}
private static int buildProgram() {
final int vs = compile(GL20.GL_VERTEX_SHADER, VS);
final int fs = compile(GL20.GL_FRAGMENT_SHADER, FS);
final int p = GLStateManager.glCreateProgram();
GLStateManager.glAttachShader(p, vs);
GLStateManager.glAttachShader(p, fs);
GLStateManager.glLinkProgram(p);
assertNotEquals(0, GLStateManager.glGetProgrami(p, GL20.GL_LINK_STATUS), () -> "test program must link: " + GLStateManager.glGetProgramInfoLog(p, 4096));
GLStateManager.glDeleteShader(vs);
GLStateManager.glDeleteShader(fs);
return p;
}
private static int compile(int type, String source) {
final int shader = GLStateManager.glCreateShader(type);
GLStateManager.glShaderSource(shader, source);
GLStateManager.glCompileShader(shader);
if (GLStateManager.glGetShaderi(shader, GL20.GL_COMPILE_STATUS) == 0) {
fail("test shader failed to compile:\n" + GLStateManager.glGetShaderInfoLog(shader, 4096) + "\n\n" + source);
}
return shader;
return GlTestSupport.program(VS, FS);
}
}
@@ -451,6 +451,7 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
@Override
public void hint(int target, int mode) {
if (RenderSystem.isGLES() && target != GL20C.GL_FRAGMENT_SHADER_DERIVATIVE_HINT) return;
GL11C.glHint(target, mode);
}
@@ -1822,11 +1823,13 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
@Override
public void pointParameterf(int pname, float param) {
if (RenderSystem.isGLES()) return;
GL14C.glPointParameterf(pname, param);
}
@Override
public void pointParameteri(int pname, int param) {
if (RenderSystem.isGLES()) return;
GL14C.glPointParameteri(pname, param);
}
@@ -4126,7 +4126,7 @@ public class SDLGPURenderBackend extends RenderBackend {
}
@Override public void getFloat(int pname, FloatBuffer params) {
if (params == null || params.remaining() == 0) return;
if (pname == GL12.GL_ALIASED_LINE_WIDTH_RANGE || pname == GL11.GL_LINE_WIDTH_RANGE) {
if (pname == GL12.GL_ALIASED_LINE_WIDTH_RANGE || pname == GL11.GL_LINE_WIDTH_RANGE || pname == GL12.GL_ALIASED_POINT_SIZE_RANGE || pname == GL11.GL_POINT_SIZE_RANGE) {
if (params.remaining() >= 2) {
params.put(params.position(), 1.0f);
params.put(params.position() + 1, 1.0f);
@@ -1,6 +1,5 @@
package com.gtnewhorizons.angelica.sdlgpu;
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
import com.gtnewhorizons.angelica.sdlgpu.resource.FboState;
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
@@ -31,7 +30,7 @@ class DrawBuffersLimitTest {
@BeforeAll
static void createBackend() {
backend = new SDLGPURenderBackend();
resourceManager = Reflect.get(backend, "resourceManager");
resourceManager = SdlTestRig.resourceManagerOf(backend);
}
@BeforeEach
@@ -1,38 +1,29 @@
package com.gtnewhorizons.angelica.sdlgpu;
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.GL15;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ;
import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE;
import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE;
class ImageAccessUsageMappingTest {
@Test
void readOnlyRequestsOnlyStorageRead() {
assertEquals(SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ, SDLGPURenderBackend.imageUsageForAccess(GL15.GL_READ_ONLY));
/** Never READ|WRITE together: that leaves SDL's DefaultTextureUsageMode ambiguous on a sampled texture. */
static Stream<Arguments> accessModes() {
return Stream.of(
Arguments.of("GL_READ_ONLY", GL15.GL_READ_ONLY, SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ),
Arguments.of("GL_WRITE_ONLY", GL15.GL_WRITE_ONLY, SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE),
Arguments.of("GL_READ_WRITE", GL15.GL_READ_WRITE, SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE));
}
@Test
void writeOnlyRequestsOnlyStorageWrite() {
assertEquals(SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE, SDLGPURenderBackend.imageUsageForAccess(GL15.GL_WRITE_ONLY));
}
@Test
void readWriteRequestsOnlyStorageWrite() {
assertEquals(SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE, SDLGPURenderBackend.imageUsageForAccess(GL15.GL_READ_WRITE));
}
@Test
void noAccessModeRequestsBothStorageBits() {
final int both = SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE;
for (int access : new int[] { GL15.GL_READ_ONLY, GL15.GL_WRITE_ONLY, GL15.GL_READ_WRITE }) {
final int usage = SDLGPURenderBackend.imageUsageForAccess(access);
assertEquals(0, usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE);
assertNotEquals(both, usage & both, "READ|WRITE together leaves SDL's DefaultTextureUsageMode ambiguous on a sampled texture");
}
@ParameterizedTest(name = "{0}")
@MethodSource("accessModes")
void accessModeRequestsExactlyOneStorageBit(String name, int access, int expectedUsage) {
assertEquals(expectedUsage, SDLGPURenderBackend.imageUsageForAccess(access));
}
}
@@ -0,0 +1,51 @@
package com.gtnewhorizons.angelica.sdlgpu;
import com.gtnewhorizons.angelica.sdlgpu.resource.PixelOps;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
/** Owns one real SDL GPU device per test class. Runs before the class's own {@code @BeforeAll} and after its {@code @AfterAll}. */
public final class RealDeviceExtension implements BeforeAllCallback, AfterAllCallback {
private static SdlTestRig rig;
public record Target(long texture, int sdlFormat) {}
public static SdlTestRig rig() {
return rig;
}
@Override
public void beforeAll(ExtensionContext context) throws Exception {
rig = SdlTestRig.acquireRealDevice();
}
@Override
public void afterAll(ExtensionContext context) {
if (rig != null && rig.frameManager.isFrameActive()) rig.frameManager.endFrame();
rig = null;
SdlTestRig.releaseRealDevice();
}
public static Target colorTarget(int glId, int width, int height) {
final long texture = rig.resourceManager.createTexture(glId, GL11.GL_TEXTURE_2D, GL11.GL_RGBA8, width, height, 1, 1);
assertTrue(texture != 0, "color target creation failed");
return new Target(texture, rig.resourceManager.getTextureMeta(glId).sdlFormat());
}
public static Target depthStencilTarget(int glId, int width, int height) {
rig.resourceManager.cachePreferredDepthFormats();
rig.resourceManager.createTexture(glId, GL11.GL_TEXTURE_2D, GL30.GL_DEPTH24_STENCIL8, width, height, 1, 1);
final long texture = rig.resourceManager.ensureTextureUsage(glId, SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET);
assertTrue(texture != 0, "depth+stencil target creation failed");
final int format = rig.resourceManager.getTextureMeta(glId).sdlFormat();
assertTrue(PixelOps.isDepthStencilFormat(format), "test needs a packed depth+stencil format");
return new Target(texture, format);
}
}
@@ -6,7 +6,6 @@ import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class SDLGPUBufferQueryTest {
@@ -28,11 +27,6 @@ class SDLGPUBufferQueryTest {
assertEquals(GL11.GL_NONE, SDLGPURenderBackend.bufferEnum(7, -1));
}
@Test
void defaultMatchesGLStateManagerDeclaredDefault() {
assertEquals(0x405, SDLGPURenderBackend.bufferEnum(0, 0));
}
@Test
void boundTextureReadsTheActiveUnitsSlot() {
final int[] bound = {11, 22, 33, 44};
@@ -56,12 +50,6 @@ class SDLGPUBufferQueryTest {
assertEquals(256, SDLGPURenderBackend.deviceLimit(GL30.GL_MAX_ARRAY_TEXTURE_LAYERS));
}
@Test
void uniformLimitFitsSdlsUboSection() {
assertTrue(SDLGPURenderBackend.deviceLimit(GL20.GL_MAX_VERTEX_UNIFORM_COMPONENTS) * 4 <= 4096);
assertTrue(SDLGPURenderBackend.deviceLimit(GL30.GL_MAX_ARRAY_TEXTURE_LAYERS) <= 256);
}
@Test
void nonLimitPnamesAreNotClaimed() {
assertEquals(-1, SDLGPURenderBackend.deviceLimit(GL11.GL_DRAW_BUFFER));
@@ -5,12 +5,11 @@ import com.gtnewhorizons.angelica.sdlgpu.device.Device;
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
import com.gtnewhorizons.angelica.sdlgpu.resource.TransferThread;
import org.lwjgl.sdl.SDLError;
import org.lwjgl.sdl.SDLHints;
import org.lwjgl.sdl.SDLInit;
import java.lang.reflect.Field;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.lwjgl.sdl.SDLGPU.SDL_CreateGPUDevice;
import static org.lwjgl.sdl.SDLGPU.SDL_DestroyGPUDevice;
@@ -31,16 +30,12 @@ public final class SdlTestRig {
public final ResourceManager resourceManager;
public final long sdlHandle;
private SdlTestRig(long sdlHandle) throws ReflectiveOperationException {
private SdlTestRig(long sdlHandle) {
this.sdlHandle = sdlHandle;
this.device = new Device();
if (sdlHandle != 0L) {
final Field deviceField = Device.class.getDeclaredField("device");
deviceField.setAccessible(true);
deviceField.setLong(this.device, sdlHandle);
final Field formatsField = Device.class.getDeclaredField("supportedShaderFormats");
formatsField.setAccessible(true);
formatsField.setInt(this.device, SDL_GetGPUShaderFormats(sdlHandle));
Reflect.set(this.device, "device", sdlHandle);
Reflect.set(this.device, "supportedShaderFormats", SDL_GetGPUShaderFormats(sdlHandle));
Reflect.set(this.device, "driverName", SDL_GetGPUDeviceDriver(sdlHandle));
Reflect.invoke(this.device, "probeFences", new Class<?>[] { int.class }, SDL_GetVersion());
}
@@ -49,11 +44,7 @@ public final class SdlTestRig {
}
public static SdlTestRig create() {
try {
return new SdlTestRig(0L);
} catch (ReflectiveOperationException e) {
throw new AssertionError(e);
}
return new SdlTestRig(0L);
}
public static ResourceManager resourceManager() {
@@ -68,6 +59,31 @@ public final class SdlTestRig {
return Reflect.invokeStatic(SDLGPURenderBackend.class, "s", new Class<?>[0]);
}
public static ResourceManager resourceManagerOf(SDLGPURenderBackend backend) {
return Reflect.get(backend, "resourceManager");
}
/** Not installed on the resource manager; pair with {@link #shutdown(TransferThread)}. */
public TransferThread newTransferThread() {
return new TransferThread(device, resourceManager);
}
public TransferThread installTransferThread() {
final TransferThread tt = newTransferThread();
resourceManager.setTransferThread(tt);
return tt;
}
public static void shutdown(TransferThread tt) {
tt.shutdown();
try {
tt.getThread().join(5_000L);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new AssertionError(e);
}
}
private static long realSdlDevice;
private static final int SHADER_FORMATS = SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_MSL | SDL_GPU_SHADERFORMAT_DXBC | SDL_GPU_SHADERFORMAT_DXIL;
@@ -77,7 +93,7 @@ public final class SdlTestRig {
assumeTrue(SDLInit.SDL_Init(SDL_INIT_VIDEO), () -> "SDL_Init failed: " + SDLError.SDL_GetError());
}
public static SdlTestRig acquireRealDevice() throws ReflectiveOperationException {
public static SdlTestRig acquireRealDevice() {
initSdlVideo();
realSdlDevice = SDL_CreateGPUDevice(SHADER_FORMATS, true, (CharSequence) null);
assumeTrue(realSdlDevice != 0, "No SDL GPU device: " + SDLError.SDL_GetError());
@@ -9,7 +9,6 @@ import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL31;
import org.lwjgl.system.MemoryUtil;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -20,11 +19,9 @@ class UboShadowSubDataTest {
private static ResourceManager resourceManager;
@BeforeAll
static void createBackend() throws Exception {
static void createBackend() {
backend = new SDLGPURenderBackend();
final Field f = SDLGPURenderBackend.class.getDeclaredField("resourceManager");
f.setAccessible(true);
resourceManager = (ResourceManager) f.get(backend);
resourceManager = SdlTestRig.resourceManagerOf(backend);
}
@ParameterizedTest
@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.sdlgpu;
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
import com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineApplier;
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
@@ -9,7 +10,6 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.lwjgl.system.MemoryUtil;
import java.lang.reflect.Field;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
@@ -48,11 +48,8 @@ class UniformNegativeLocationTest {
st.releaseUniformStaging(prog);
}
private static void clearApplier(SDLGPURenderBackend backend) throws Exception {
final Field f = SDLGPURenderBackend.class.getDeclaredField("pipelineApplier");
f.setAccessible(true);
f.set(backend, null);
assertNull(f.get(backend), "test rig failed to null the applier");
private static void clearApplier(SDLGPURenderBackend backend) {
Reflect.set(backend, "pipelineApplier", null);
}
@Test
@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.sdlgpu.compat;
import com.gtnewhorizons.angelica.glsm.testutil.TestThreads;
import com.gtnewhorizons.angelica.sdlgpu.SDLGPURenderBackend;
import org.junit.jupiter.api.Test;
import org.lwjgl.opengl.GL;
@@ -9,11 +10,9 @@ import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -31,18 +30,6 @@ class Lwjgl3GLCapabilitiesShimTest {
assertTrue(caps.forwardCompatible, "SDL reports Core Profile Equivalent, so forwardCompatible must be true");
}
@Test
void everyAdvertisedVersionFlagIsTrue() throws ReflectiveOperationException {
final Set<String> advertised = SDLGPURenderBackend.advertisedCapabilities();
final GLCapabilities caps = Lwjgl3GLCapabilitiesShim.create(advertised);
final Set<String> missing = new TreeSet<>();
for (String name : advertised) {
if (name.startsWith("OpenGL") && !flag(caps, name)) missing.add(name);
}
assertTrue(missing.isEmpty(), () -> "advertised but reported unsupported: " + missing);
}
@Test
void advertises46() throws ReflectiveOperationException {
final GLCapabilities caps = Lwjgl3GLCapabilitiesShim.create(SDLGPURenderBackend.advertisedCapabilities());
@@ -92,17 +79,8 @@ class Lwjgl3GLCapabilitiesShimTest {
try {
assertNotNull(GL.getCapabilities());
final AtomicReference<Throwable> onOtherThread = new AtomicReference<>();
final Thread other = new Thread(() -> {
try {
GL.getCapabilities();
} catch (Throwable t) {
onOtherThread.set(t);
}
}, "no-context-thread");
other.start();
other.join();
assertInstanceOf(IllegalStateException.class, onOtherThread.get(), "a thread that does not own the context must not look like the render thread");
TestThreads.run("no-context-thread", () -> assertThrows(IllegalStateException.class, GL::getCapabilities,
"a thread that does not own the context must not look like the render thread"));
} finally {
Lwjgl3GLCapabilitiesShim.clearOnCurrentThread();
}
@@ -1,6 +1,5 @@
package com.gtnewhorizons.angelica.sdlgpu.compute;
import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler;
import com.gtnewhorizons.angelica.glsm.testutil.TestPaths;
import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager;
import org.junit.jupiter.api.Test;
@@ -11,9 +10,9 @@ import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.stream.IntStream;
import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
class ChunkCullSpirvCompileTest {
@@ -25,9 +24,7 @@ class ChunkCullSpirvCompileTest {
@Test
void chunkCullReflectsExpectedBindings() {
final SpirvCompiler.Result r = SpirvCompiler.compile(source(), Shaderc.shaderc_compute_shader, SHADER, SpirvCompiler.Options.vulkanForced460Core());
if (r.spirv() == null) fail(SHADER + " failed to compile: " + r.error());
final ByteBuffer spirv = r.spirv();
final ByteBuffer spirv = compileOrFail(source(), Shaderc.shaderc_compute_shader);
try {
final ShaderManager.ComputeBindingMap map = ShaderManager.remapSpirvForComputeSDLGPU(spirv);
assertEquals(0, map.samplerGlSlots().length, "the frustum cull samples no textures");
@@ -1,35 +0,0 @@
package com.gtnewhorizons.angelica.sdlgpu.compute;
import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.lwjgl.sdl.SDLGPU.SDL_GetGPUDeviceDriver;
class ImageAtomicsGpuTest {
private static SdlTestRig rig;
private static ImageAtomicsProbe probe;
@BeforeAll
static void setUp() throws Exception {
rig = SdlTestRig.acquireRealDevice();
assumeTrue("metal".equalsIgnoreCase(SDL_GetGPUDeviceDriver(rig.sdlHandle)), "native Metal only");
assumeTrue(ImageAtomicsProbe.defaultMslVersion() >= ImageAtomicsProbe.MTL_LANGUAGE_VERSION_3_1, "this process compiles Metal source below MSL 3.1");
probe = new ImageAtomicsProbe(rig.sdlHandle);
}
@AfterAll
static void tearDown() {
if (probe != null) probe.release();
SdlTestRig.releaseRealDevice();
}
@Test
void atomicsCountEveryInvocationOnAnImageTheSizeOfEuphoriasEndCrystalMap() {
assertTrue(probe.run(40, 10));
}
}
@@ -1,9 +1,9 @@
package com.gtnewhorizons.angelica.sdlgpu.device;
import com.gtnewhorizons.angelica.sdlgpu.RealDeviceExtension;
import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.lwjgl.system.MemoryStack;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -17,22 +17,12 @@ import static org.lwjgl.sdl.SDLGPU.SDL_WaitForGPUFences;
import static org.lwjgl.sdl.SDLVersion.SDL_GetVersion;
import static org.lwjgl.sdl.SDLVersion.SDL_VERSIONNUM;
@ExtendWith(RealDeviceExtension.class)
class FenceProbeRealDeviceTest {
private static SdlTestRig rig;
@BeforeAll
static void setUp() throws Exception {
rig = SdlTestRig.acquireRealDevice();
}
@AfterAll
static void tearDown() {
SdlTestRig.releaseRealDevice();
}
@Test
void probeMatchesVersionTableAndCorrectedQueryReadsSignaled() {
final SdlTestRig rig = RealDeviceExtension.rig();
final String driver = SDL_GetGPUDeviceDriver(rig.sdlHandle);
assertEquals("metal".equalsIgnoreCase(driver) && SDL_GetVersion() == SDL_VERSIONNUM(3, 4, 14), rig.device.isFenceQueryInverted(), "driver=" + driver + " sdl=" + SDL_GetVersion());
@@ -1,34 +1,31 @@
package com.gtnewhorizons.angelica.sdlgpu.device;
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 com.gtnewhorizons.angelica.sdlgpu.device.VideoDriverRecovery.shouldRetryOnX11;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
class VideoDriverRecoveryTest {
@Test
void waylandWithoutVulkanSurfaceRetries() {
assertTrue(shouldRetryOnX11("wayland", false, ":0"));
static Stream<Arguments> sessions() {
return Stream.of(
Arguments.of("wayland without a Vulkan loader, XWayland present", "wayland", false, ":0", true),
Arguments.of("wayland with a Vulkan loader", "wayland", true, ":0", false),
Arguments.of("wayland, DISPLAY unset", "wayland", false, null, false),
Arguments.of("wayland, DISPLAY empty", "wayland", false, "", false),
Arguments.of("x11", "x11", false, ":0", false),
Arguments.of("cocoa", "cocoa", false, ":0", false),
Arguments.of("windows", "windows", false, ":0", false),
Arguments.of("no video driver", null, false, ":0", false));
}
@Test
void waylandWithVulkanStays() {
assertFalse(shouldRetryOnX11("wayland", true, ":0"));
}
@Test
void noXWaylandMeansNoRetry() {
assertFalse(shouldRetryOnX11("wayland", false, null));
assertFalse(shouldRetryOnX11("wayland", false, ""));
}
@Test
void otherVideoDriversAreLeftAlone() {
assertFalse(shouldRetryOnX11("x11", false, ":0"));
assertFalse(shouldRetryOnX11("cocoa", false, ":0"));
assertFalse(shouldRetryOnX11("windows", false, ":0"));
assertFalse(shouldRetryOnX11(null, false, ":0"));
@ParameterizedTest(name = "{0}")
@MethodSource("sessions")
void retriesOnX11OnlyForWaylandWithoutVulkan(String name, String videoDriver, boolean vulkanLoaderAvailable, String display, boolean retry) {
assertEquals(retry, shouldRetryOnX11(videoDriver, vulkanLoaderAvailable, display));
}
}
@@ -1,28 +1,29 @@
package com.gtnewhorizons.angelica.sdlgpu.frame;
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 static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
class AcquireAffinityTest {
private static final Thread WINDOW = new Thread("window");
private static final Thread OTHER = new Thread("other");
@Test
void acquiringOnTheWindowThreadIsTheOnlyAcceptedCase() {
assertFalse(FrameManager.acquireAffinityViolated(WINDOW, WINDOW));
/** A null window is never a violation: the check runs before the window is claimed. */
static Stream<Arguments> acquires() {
return Stream.of(
Arguments.of("on the window thread", WINDOW, WINDOW, false),
Arguments.of("on any other thread", OTHER, WINDOW, true),
Arguments.of("before the window thread is known", OTHER, null, false));
}
@Test
void acquiringOnAnyOtherThreadViolatesTheSdlContract() {
assertTrue(FrameManager.acquireAffinityViolated(OTHER, WINDOW));
}
@Test
void anUnknownWindowThreadCannotBeJudged() {
assertFalse(FrameManager.acquireAffinityViolated(OTHER, null),
"the check runs before the window is claimed; judging a null window would throw at startup");
@ParameterizedTest(name = "{0}")
@MethodSource("acquires")
void acquireAffinityViolated(String name, Thread self, Thread window, boolean violated) {
assertEquals(violated, FrameManager.acquireAffinityViolated(self, window));
}
}
@@ -1,6 +1,7 @@
package com.gtnewhorizons.angelica.sdlgpu.frame;
import com.gtnewhorizons.angelica.glsm.backend.MainThreadPump;
import com.gtnewhorizons.angelica.glsm.testutil.TestThreads;
import com.gtnewhorizons.angelica.sdlgpu.device.Device;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
@@ -115,9 +116,7 @@ class PresenterTest {
p.requestPresent(1L, 10, 10, SDL_FLIP_NONE);
final Thread windowThread = new Thread(executor::runNext, "window-thread");
windowThread.start();
windowThread.join();
TestThreads.run("window-thread", executor::runNext);
assertEquals(before, fm.registeredFrameCount(), "the window thread must stay out of the frame registry");
}
@@ -2,11 +2,8 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm;
import com.gtnewhorizons.angelica.config.SystemProperties;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -21,6 +18,8 @@ import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import sun.misc.Unsafe;
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.BLACK;
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.RED;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -30,22 +29,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class GlsmSdlAttachmentClearTest {
private static final int SIZE = GlsmSdlHeadlessRig.SIZE;
private static final int RED = 0xFFFF0000;
private static final int BLACK = 0xFF000000;
@BeforeAll
static void boot() {
GlsmSdlHeadlessRig.boot();
}
private static FrameManager frameManager() {
return Reflect.get(BackendManager.RENDER_BACKEND, "frameManager");
}
private static FrameManager.FrameState frame() {
return frameManager().frame();
return GlsmSdlHeadlessRig.frameManager().frame();
}
// Reflect.setStaticFinal rejects primitive fields.
private static void setDisableInPassClear(boolean value) {
try {
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
@@ -58,6 +47,27 @@ class GlsmSdlAttachmentClearTest {
}
}
/** Clears colour to black and depth to 0.9, then draws a quad so a render pass is open. */
private static void primeTarget(float r, float g, float b) {
GlsmSdlHeadlessRig.bindTarget();
GLStateManager.glDepthMask(true);
GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLStateManager.glClearDepth(0.9);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GlsmSdlHeadlessRig.solidQuad(r, g, b);
}
private static void primeTargetWithStencil(int stencil) {
GlsmSdlHeadlessRig.bindTarget();
GLStateManager.glDepthMask(true);
GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLStateManager.glClearDepth(1.0);
GLStateManager.glClearStencil(stencil);
GLStateManager.glStencilMask(0xFF);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f);
}
private static void drawFullQuadAtZ(float z, float r, float g, float b) {
GLStateManager.glColor4f(r, g, b, 1.0f);
GLStateManager.glNormal3f(0.0f, 0.0f, 1.0f);
@@ -93,12 +103,7 @@ class GlsmSdlAttachmentClearTest {
@Test
void depthMaskOffSuppressesTheClearAndBreaksNoPass() {
GlsmSdlHeadlessRig.bindTarget();
GLStateManager.glDepthMask(true);
GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLStateManager.glClearDepth(0.9);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f);
primeTarget(0.1f, 0.1f, 0.1f);
final FrameManager.FrameState f = frame();
final long passBefore = f.renderPass;
@@ -119,12 +124,7 @@ class GlsmSdlAttachmentClearTest {
@Test
void inPassPathIsTakenForACompatibleActivePass() {
GlsmSdlHeadlessRig.bindTarget();
GLStateManager.glDepthMask(true);
GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLStateManager.glClearDepth(0.9);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f);
primeTarget(0.1f, 0.1f, 0.1f);
final FrameManager.FrameState f = frame();
final long passBefore = f.renderPass;
@@ -142,12 +142,7 @@ class GlsmSdlAttachmentClearTest {
@Test
void disableInPassClearFlagForcesTheLegacyPath() {
GlsmSdlHeadlessRig.bindTarget();
GLStateManager.glDepthMask(true);
GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLStateManager.glClearDepth(0.9);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f);
primeTarget(0.1f, 0.1f, 0.1f);
final FrameManager.FrameState f = frame();
assertTrue(f.renderPass != 0, "expected an active render pass before the clear");
@@ -177,12 +172,7 @@ class GlsmSdlAttachmentClearTest {
try {
GlsmSdlHeadlessRig.beginFrame();
GlsmSdlHeadlessRig.resetState();
GlsmSdlHeadlessRig.bindTarget();
GLStateManager.glDepthMask(true);
GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLStateManager.glClearDepth(0.9);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f);
primeTarget(0.1f, 0.1f, 0.1f);
GLStateManager.glDepthMask(true);
GLStateManager.glClearDepth(depthValue);
@@ -196,12 +186,7 @@ class GlsmSdlAttachmentClearTest {
@Test
void scissoredClearOnlyAffectsTheScissorRect() {
GlsmSdlHeadlessRig.bindTarget();
GLStateManager.glDepthMask(true);
GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLStateManager.glClearDepth(0.9);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f);
primeTarget(0.1f, 0.1f, 0.1f);
GLStateManager.glEnable(GL11.GL_SCISSOR_TEST);
GLStateManager.glScissor(0, 0, SIZE / 2, SIZE);
@@ -229,14 +214,7 @@ class GlsmSdlAttachmentClearTest {
@Test
void stencilWriteMaskIsHonored() {
GlsmSdlHeadlessRig.bindTarget();
GLStateManager.glDepthMask(true);
GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLStateManager.glClearDepth(1.0);
GLStateManager.glClearStencil(0xA5);
GLStateManager.glStencilMask(0xFF);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f);
primeTargetWithStencil(0xA5);
final FrameManager.FrameState f = frame();
final long passBefore = f.renderPass;
@@ -258,14 +236,7 @@ class GlsmSdlAttachmentClearTest {
@Test
@Disabled("stencil reference >= 0x80 compares as 0xFF on this backend; see stencil reference finding")
void stencilReferenceAtOrAbove0x80ComparesExactly() {
GlsmSdlHeadlessRig.bindTarget();
GLStateManager.glDepthMask(true);
GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLStateManager.glClearDepth(1.0);
GLStateManager.glClearStencil(0xAF);
GLStateManager.glStencilMask(0xFF);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f);
primeTargetWithStencil(0xAF);
final StencilProbeResult result = probeStencilReconcile();
assertEquals(0xAF, result.bitPlaneValue(), "bit-plane read of a plain 0xAF stencil clear");
@@ -274,14 +245,7 @@ class GlsmSdlAttachmentClearTest {
@Test
void baselineStencilClearReadsBackExactly() {
GlsmSdlHeadlessRig.bindTarget();
GLStateManager.glDepthMask(true);
GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLStateManager.glClearDepth(1.0);
GLStateManager.glClearStencil(0xA5);
GLStateManager.glStencilMask(0xFF);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f);
primeTargetWithStencil(0xA5);
assertEquals(0xA5, readStencilByteSameFrame(), "a full-write-mask clear (the legacy load-op path) must read back exactly what was cleared");
}
@@ -375,12 +339,7 @@ class GlsmSdlAttachmentClearTest {
@Test
void scissoredColorAndDepthClearStaysInTheRect() {
GlsmSdlHeadlessRig.bindTarget();
GLStateManager.glDepthMask(true);
GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLStateManager.glClearDepth(0.9);
GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GlsmSdlHeadlessRig.solidQuad(0.0f, 0.0f, 1.0f);
primeTarget(0.0f, 0.0f, 1.0f);
final FrameManager.FrameState f = frame();
final long passBefore = f.renderPass;
@@ -594,7 +553,7 @@ class GlsmSdlAttachmentClearTest {
GLStateManager.glDepthMask(true);
GLStateManager.glClear(GL11.GL_DEPTH_BUFFER_BIT);
final ResourceManager resourceManager = Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager");
final ResourceManager resourceManager = GlsmSdlHeadlessRig.resourceManager();
final long depthTexture = resourceManager.getFbo(fbo).depthTexture;
GlsmSdlHeadlessRig.endFrame();
@@ -1,14 +1,12 @@
package com.gtnewhorizons.angelica.sdlgpu.glsm;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL14;
@@ -19,11 +17,6 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals;
@ExtendWith(GlsmSdlFrameExtension.class)
class GlsmSdlBlendConstantsTest {
@BeforeAll
static void boot() {
GlsmSdlHeadlessRig.boot();
}
@AfterEach
void restoreBlend() {
GLStateManager.glDisable(GL11.GL_BLEND);
@@ -31,17 +24,10 @@ class GlsmSdlBlendConstantsTest {
GLStateManager.glBlendColor(0.0f, 0.0f, 0.0f, 0.0f);
}
@Test
void pendingConstantsSurviveADrawWithoutConstantBlending() {
verifyConstants(false);
}
@Test
void constantsChangedWhileBlendingIsDisabledReachTheNextBlendedDraw() {
verifyConstants(true);
}
private static void verifyConstants(boolean previouslyEnabled) {
/** With blending never enabled before, the pending constants must survive a draw that does not use them. */
@ParameterizedTest(name = "blending enabled earlier in the frame: {0}")
@ValueSource(booleans = {false, true})
void constantsSetWhileBlendingIsDisabledReachTheNextBlendedDraw(boolean previouslyEnabled) {
GlsmSdlHeadlessRig.bindTarget();
GLStateManager.glBlendFunc(GL14.GL_CONSTANT_COLOR, GL11.GL_ZERO);
if (previouslyEnabled) {
@@ -52,8 +38,7 @@ class GlsmSdlBlendConstantsTest {
GLStateManager.glDisable(GL11.GL_BLEND);
GLStateManager.glBlendColor(0.0f, 1.0f, 0.0f, 1.0f);
GlsmSdlHeadlessRig.solidQuad(1.0f, 1.0f, 1.0f);
final FrameManager manager = Reflect.get(BackendManager.RENDER_BACKEND, "frameManager");
final FrameManager.FrameState frame = manager.frame();
final FrameManager.FrameState frame = GlsmSdlHeadlessRig.frameManager().frame();
final long pass = frame.renderPass;
final long generation = frame.renderPassGeneration;
assertNotEquals(0, pass);
@@ -64,6 +49,6 @@ class GlsmSdlBlendConstantsTest {
assertEquals(generation, frame.renderPassGeneration);
final int[] pixels = GlsmSdlHeadlessRig.readTarget();
final int size = GlsmSdlHeadlessRig.SIZE;
assertEquals(0xFF00FF00, GlsmSdlHeadlessRig.pixelAt(pixels, size, size / 2, size / 2));
assertEquals(GlsmSdlHeadlessRig.GREEN, GlsmSdlHeadlessRig.pixelAt(pixels, size, size / 2, size / 2));
}
}
@@ -1,10 +1,12 @@
package com.gtnewhorizons.angelica.sdlgpu.glsm;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.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.GL15;
import org.lwjgl.opengl.GL30;
@@ -14,6 +16,7 @@ import org.lwjgl.opengl.GL44;
import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -31,11 +34,6 @@ class GlsmSdlBufferMappingLifetimeTest {
private static final int PERSISTENT_MAP = GL44.GL_MAP_PERSISTENT_BIT | GL30.GL_MAP_WRITE_BIT | GL44.GL_MAP_COHERENT_BIT;
private static final int PLAIN_MAP = GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_INVALIDATE_BUFFER_BIT;
@BeforeAll
static void boot() {
GlsmSdlHeadlessRig.boot();
}
private static void fill(ByteBuffer buf, int seed) {
for (int i = 0; i < buf.capacity(); i++) buf.put(i, (byte) (seed * 37 + i * 7 + 1));
}
@@ -300,7 +298,15 @@ class GlsmSdlBufferMappingLifetimeTest {
}
}
private static void assertBufferDataUnmapsPlainMapping(int target) {
static Stream<Arguments> plainMapTargets() {
return Stream.of(
Arguments.of("GL_ARRAY_BUFFER", GL15.GL_ARRAY_BUFFER),
Arguments.of("GL_UNIFORM_BUFFER", GL31.GL_UNIFORM_BUFFER));
}
@ParameterizedTest(name = "{0}")
@MethodSource("plainMapTargets")
void bufferDataOnMappedBufferUnmapsIt(String name, int target) {
final int id = GLStateManager.glGenBuffers();
try {
GLStateManager.glBindBuffer(target, id);
@@ -314,16 +320,6 @@ class GlsmSdlBufferMappingLifetimeTest {
}
}
@Test
void bufferDataOnMappedArrayBufferUnmapsIt() {
assertBufferDataUnmapsPlainMapping(GL15.GL_ARRAY_BUFFER);
}
@Test
void bufferDataOnMappedUniformBufferUnmapsIt() {
assertBufferDataUnmapsPlainMapping(GL31.GL_UNIFORM_BUFFER);
}
@Test
void outOfRangeFlushOfPersistentMappingIsIgnored() {
final int target = GL15.GL_ARRAY_BUFFER;
@@ -1,16 +1,14 @@
package com.gtnewhorizons.angelica.sdlgpu.glsm;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
import com.gtnewhorizons.angelica.sdlgpu.SDLGPUGate;
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
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.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
@@ -20,39 +18,30 @@ import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.function.IntBinaryOperator;
import java.util.stream.Stream;
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.BLUE;
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.GREEN;
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.RED;
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.SIZE;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Tag("glsm-sdl")
@ExtendWith(GlsmSdlFrameExtension.class)
class GlsmSdlCopyBlitTest {
private static final int RED = 0xFFFF0000;
private static final int GREEN = 0xFF00FF00;
private static final int BLUE = 0xFF0000FF;
private static final int SCISSOR_ROWS = 16;
private static final byte SENTINEL_BYTE = 0x7B;
private static final int SENTINEL = 0x7B7B7B7B;
private static FrameManager frameManager;
private static ResourceManager resourceManager;
@BeforeAll
static void boot() {
GlsmSdlHeadlessRig.boot();
frameManager = Reflect.get(BackendManager.RENDER_BACKEND, "frameManager");
resourceManager = Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager");
frameManager.destroyFinalTarget();
frameManager.finalTarget().create(SDLGPUGate.device(), resourceManager, SIZE, SIZE, resourceManager.mapTextureFormat(GL11.GL_RGBA8));
assertEquals(SIZE, frameManager.getFbo0Height());
static void installFinalTarget() {
GlsmSdlHeadlessRig.installFinalTarget(SIZE, SIZE);
}
@AfterAll
static void dropFinalTarget() {
GlsmSdlHeadlessRig.endFrame();
if (frameManager != null) frameManager.destroyFinalTarget();
GlsmSdlHeadlessRig.dropFinalTarget();
}
private static void assertRow(int[] pixels, int y, int expected, String label) {
@@ -88,27 +77,11 @@ class GlsmSdlCopyBlitTest {
GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MIN_LOD, 0.0f);
GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LOD, 1.0f);
GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, 0.0f);
uploadLevel(0, SIZE, level0Argb);
uploadLevel(1, SIZE / 2, level1Argb);
GlsmSdlHeadlessRig.uploadSolidLevel(0, SIZE, level0Argb);
GlsmSdlHeadlessRig.uploadSolidLevel(1, SIZE / 2, level1Argb);
return id;
}
private static void uploadLevel(int level, int size, int argb) {
final ByteBuffer texels = MemoryUtil.memAlloc(size * size * 4);
try {
for (int i = 0; i < size * size; i++) {
texels.put(i * 4, (byte) ((argb >> 16) & 0xFF));
texels.put(i * 4 + 1, (byte) ((argb >> 8) & 0xFF));
texels.put(i * 4 + 2, (byte) (argb & 0xFF));
texels.put(i * 4 + 3, (byte) ((argb >>> 24) & 0xFF));
}
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, level, GL11.GL_RGBA8, size, size, 0,
GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels);
} finally {
MemoryUtil.memFree(texels);
}
}
private static int copyGreenIntoLevelOne() {
GlsmSdlHeadlessRig.bindTarget();
GlsmSdlHeadlessRig.clearTo(0.0f, 0.0f, 0.0f, 1.0f);
@@ -181,7 +154,7 @@ class GlsmSdlCopyBlitTest {
private static void blitLevelOneInto(int dstSize, int dstLevel) {
final int source = GlsmSdlHeadlessRig.createSolidMipTexture(SIZE, 1, RED);
uploadLevel(1, SIZE / 2, GREEN);
GlsmSdlHeadlessRig.uploadSolidLevel(1, SIZE / 2, GREEN);
final int destination = GlsmSdlHeadlessRig.createSolidMipTexture(dstSize, dstLevel, BLUE);
final int readFbo = GlsmSdlHeadlessRig.fboWithColor(source, 1);
final int drawFbo = GlsmSdlHeadlessRig.fboWithColor(destination, dstLevel);
@@ -202,19 +175,17 @@ class GlsmSdlCopyBlitTest {
GLStateManager.glDeleteTextures(destination);
}
@Test
void blitFramebufferReadsTheSourceAttachmentLevel() {
blitLevelOneInto(SIZE / 2, 0);
static Stream<Arguments> levelBlits() {
return Stream.of(
Arguments.of("1:1 into level 0: reads the source attachment level", SIZE / 2, 0),
Arguments.of("scaled into level 0: reads the source attachment level", SIZE, 0),
Arguments.of("into level 1: writes the destination attachment level", SIZE, 1));
}
@Test
void scaledBlitFramebufferReadsTheSourceAttachmentLevel() {
blitLevelOneInto(SIZE, 0);
}
@Test
void blitFramebufferWritesTheDestinationAttachmentLevel() {
blitLevelOneInto(SIZE, 1);
@ParameterizedTest(name = "{0}")
@MethodSource("levelBlits")
void blitFramebufferHonoursAttachmentLevels(String name, int dstSize, int dstLevel) {
blitLevelOneInto(dstSize, dstLevel);
}
@Test
@@ -299,12 +270,7 @@ class GlsmSdlCopyBlitTest {
try {
for (int y = 0; y < SIZE; y++) {
for (int x = 0; x < SIZE; x++) {
final int argb = pattern(x, y);
final int i = (y * SIZE + x) * 4;
texels.put(i, (byte) ((argb >> 16) & 0xFF));
texels.put(i + 1, (byte) ((argb >> 8) & 0xFF));
texels.put(i + 2, (byte) (argb & 0xFF));
texels.put(i + 3, (byte) ((argb >>> 24) & 0xFF));
GlsmSdlHeadlessRig.putArgb(texels, y * SIZE + x, pattern(x, y));
}
}
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SIZE, SIZE, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels);
@@ -331,32 +297,25 @@ class GlsmSdlCopyBlitTest {
return pixels;
}
@Test
void blitFramebufferWithReversedSourceXMirrorsHorizontally() {
final int[] pixels = blitPattern(SIZE, 0, 0, SIZE, 0, 0, SIZE, SIZE);
for (int y = 0; y < SIZE; y++) {
for (int x = 0; x < SIZE; x++) {
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, x, y, pattern(SIZE - 1 - x, y), "horizontal mirror");
}
}
private static Arguments mirror(String name, int[] srcRect, int[] dstRect, IntBinaryOperator expected) {
return Arguments.of(name, srcRect, dstRect, expected);
}
@Test
void blitFramebufferWithReversedDestinationYMirrorsVertically() {
final int[] pixels = blitPattern(0, 0, SIZE, SIZE, 0, SIZE, SIZE, 0);
for (int y = 0; y < SIZE; y++) {
for (int x = 0; x < SIZE; x++) {
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, x, y, pattern(x, SIZE - 1 - y), "vertical mirror");
}
}
/** Rects are x0, y0, x1, y1; {@code expected} maps a destination pixel to the pattern colour it must hold. */
static Stream<Arguments> mirroredBlits() {
return Stream.of(
mirror("reversed source X mirrors horizontally", new int[]{SIZE, 0, 0, SIZE}, new int[]{0, 0, SIZE, SIZE}, (x, y) -> pattern(SIZE - 1 - x, y)),
mirror("reversed destination Y mirrors vertically", new int[]{0, 0, SIZE, SIZE}, new int[]{0, SIZE, SIZE, 0}, (x, y) -> pattern(x, SIZE - 1 - y)),
mirror("both ranges reversed does not mirror", new int[]{SIZE, SIZE, 0, 0}, new int[]{SIZE, SIZE, 0, 0}, (x, y) -> pattern(x, y)));
}
@Test
void blitFramebufferWithBothRangesReversedDoesNotMirror() {
final int[] pixels = blitPattern(SIZE, SIZE, 0, 0, SIZE, SIZE, 0, 0);
@ParameterizedTest(name = "{0}")
@MethodSource("mirroredBlits")
void blitFramebufferMirrorsByRangeDirection(String name, int[] src, int[] dst, IntBinaryOperator expected) {
final int[] pixels = blitPattern(src[0], src[1], src[2], src[3], dst[0], dst[1], dst[2], dst[3]);
for (int y = 0; y < SIZE; y++) {
for (int x = 0; x < SIZE; x++) {
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, x, y, pattern(x, y), "both ranges reversed");
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, x, y, expected.applyAsInt(x, y), name);
}
}
}
@@ -1,34 +1,23 @@
package com.gtnewhorizons.angelica.sdlgpu.glsm;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.lwjgl.opengl.GL11;
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.RED;
@Tag("glsm-sdl")
@ExtendWith(GlsmSdlFrameExtension.class)
class GlsmSdlDegenerateDrawTest {
private static final int RED = 0xFFFF0000;
@BeforeAll
static void boot() {
GlsmSdlHeadlessRig.boot();
}
@Test
void zeroCountDrawArraysLeavesTheFollowingDrawIntact() {
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 0);
@ParameterizedTest(name = "count {0}")
@ValueSource(ints = {0, -3})
void degenerateCountDrawArraysLeavesTheFollowingDrawIntact(int count) {
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, count);
GlsmSdlHeadlessRig.solidQuad(1.0f, 0.0f, 0.0f);
GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTarget(), RED, "after a zero-count drawArrays");
}
@Test
void negativeCountDrawArraysLeavesTheFollowingDrawIntact() {
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, -3);
GlsmSdlHeadlessRig.solidQuad(1.0f, 0.0f, 0.0f);
GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTarget(), RED, "after a negative-count drawArrays");
GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTarget(), RED, "after a drawArrays with count " + count);
}
}
@@ -1,10 +1,6 @@
package com.gtnewhorizons.angelica.sdlgpu.glsm;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
import com.gtnewhorizons.angelica.sdlgpu.SDLGPUGate;
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
@@ -19,6 +15,9 @@ import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer;
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.BLUE;
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.GREEN;
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.RED;
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.SIZE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -32,27 +31,14 @@ class GlsmSdlFbo0ViewportTest {
private static final int FBO0_H = 96;
private static final int VIEW = 32;
private static final int RED = 0xFFFF0000;
private static final int GREEN = 0xFF00FF00;
private static final int BLUE = 0xFF0000FF;
private static FrameManager frameManager;
private static ResourceManager resourceManager;
@BeforeAll
static void boot() {
GlsmSdlHeadlessRig.boot();
frameManager = Reflect.get(BackendManager.RENDER_BACKEND, "frameManager");
resourceManager = Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager");
frameManager.destroyFinalTarget();
frameManager.finalTarget().create(SDLGPUGate.device(), resourceManager, FBO0_W, FBO0_H, resourceManager.mapTextureFormat(GL11.GL_RGBA8));
assertEquals(FBO0_H, frameManager.getFbo0Height());
static void installFinalTarget() {
GlsmSdlHeadlessRig.installFinalTarget(FBO0_W, FBO0_H);
}
@AfterAll
static void dropFinalTarget() {
GlsmSdlHeadlessRig.endFrame();
if (frameManager != null) frameManager.destroyFinalTarget();
GlsmSdlHeadlessRig.dropFinalTarget();
}
private static void bindFbo0WithSmallViewport() {
@@ -161,11 +147,11 @@ class GlsmSdlFbo0ViewportTest {
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 0);
assertNull(resourceManager.getTextureMeta(copy), "destination was allocated before the copy");
assertNull(GlsmSdlHeadlessRig.resourceManager().getTextureMeta(copy), "destination was allocated before the copy");
GLStateManager.glCopyTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, 0, 0, VIEW, VIEW, 0);
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(copy);
final ResourceManager.TextureMeta meta = GlsmSdlHeadlessRig.resourceManager().getTextureMeta(copy);
assertNotNull(meta, "copyTexImage2D did not allocate the destination");
assertEquals(VIEW, meta.width());
assertEquals(VIEW, meta.height());
@@ -10,17 +10,17 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.SIZE;
import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.TEXEL;
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;
@@ -30,18 +30,14 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
@ExtendWith(GlsmSdlFrameExtension.class)
class GlsmSdlFfpTest {
private static final int TEXEL = 0xFFFF4020;
private static final int LIGHTMAP_SIZE = 16;
private static int baseTexture;
private static int lightmapTexture;
@BeforeAll
static void boot() {
GlsmSdlHeadlessRig.boot();
GlsmSdlHeadlessRig.beginFrame();
baseTexture = GlsmSdlHeadlessRig.createSolidTexture(TEXEL);
lightmapTexture = createLightmapTexture();
lightmapTexture = GlsmSdlHeadlessRig.createLightmapTexture(true);
GlsmSdlHeadlessRig.endFrame();
}
@@ -53,37 +49,8 @@ class GlsmSdlFfpTest {
FfpFixture.IntegerInstances.delete();
}
private static int createLightmapTexture() {
final int id = GLStateManager.glGenTextures();
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, id);
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, LIGHTMAP_SIZE, LIGHTMAP_SIZE, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
final ByteBuffer texels = MemoryUtil.memAlloc(LIGHTMAP_SIZE * LIGHTMAP_SIZE * 4);
try {
for (int y = 0; y < LIGHTMAP_SIZE; y++) {
for (int x = 0; x < LIGHTMAP_SIZE; x++) {
final boolean brightest = (x == 0 && y == 0) || (x == LIGHTMAP_SIZE - 1 && y == LIGHTMAP_SIZE - 1);
texels.put((byte) (brightest ? 255 : 0));
texels.put((byte) 255);
texels.put((byte) (brightest ? 255 : 0));
texels.put((byte) 255);
}
}
texels.flip();
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, LIGHTMAP_SIZE, LIGHTMAP_SIZE, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels);
} finally {
MemoryUtil.memFree(texels);
}
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
return id;
}
private static void bindBaseTexture() {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, baseTexture);
GLStateManager.glEnable(GL11.GL_TEXTURE_2D);
GlsmSdlHeadlessRig.bindBaseTexture(baseTexture);
}
private static void enableLightmapUnit(boolean vanillaTextureMatrix) {
@@ -100,13 +67,7 @@ class GlsmSdlFfpTest {
}
private static void quadTwoUnits(boolean vanillaBrightness) {
final short brightness = (short) (vanillaBrightness ? 240 : 0);
GLStateManager.glBegin(GL11.GL_QUADS);
GlsmSdlHeadlessRig.emitVertex(-1.0f, -1.0f, 0.0f, 0.0f, true, brightness);
GlsmSdlHeadlessRig.emitVertex(1.0f, -1.0f, 1.0f, 0.0f, true, brightness);
GlsmSdlHeadlessRig.emitVertex(1.0f, 1.0f, 1.0f, 1.0f, true, brightness);
GlsmSdlHeadlessRig.emitVertex(-1.0f, 1.0f, 0.0f, 1.0f, true, brightness);
GLStateManager.glEnd();
GlsmSdlHeadlessRig.quadTwoUnits((short) (vanillaBrightness ? 240 : 0));
}
private static void assertTexelEverywhere(String label) {
@@ -124,22 +85,15 @@ class GlsmSdlFfpTest {
assertTexelEverywhere("single unit");
}
@Test
void twoUnitTerrainQuadWithVanillaLightmapMatrixShowsTheTexel() {
/** Vanilla pairs its lightmap texture matrix with brightness 240; the identity matrix case uses brightness 0. */
@ParameterizedTest(name = "vanilla lightmap matrix and brightness: {0}")
@ValueSource(booleans = {true, false})
void twoUnitTerrainQuadShowsTheTexel(boolean vanilla) {
bindBaseTexture();
enableLightmapUnit(true);
enableLightmapUnit(vanilla);
GlsmSdlHeadlessRig.terrainRenderState();
quadTwoUnits(true);
assertTexelEverywhere("two units, vanilla lightmap matrix");
}
@Test
void twoUnitTerrainQuadWithIdentityLightmapMatrixShowsTheTexel() {
bindBaseTexture();
enableLightmapUnit(false);
GlsmSdlHeadlessRig.terrainRenderState();
quadTwoUnits(false);
assertTexelEverywhere("two units, identity lightmap matrix, zero brightness");
quadTwoUnits(vanilla);
assertTexelEverywhere(vanilla ? "two units, vanilla lightmap matrix" : "two units, identity lightmap matrix, zero brightness");
}
@Test
@@ -226,9 +180,9 @@ class GlsmSdlFfpTest {
final int[] pixels = GlsmSdlHeadlessRig.readTarget();
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 13, SIZE / 2, 0xFFFF0000, "entity -1 must render red");
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 32, SIZE / 2, 0xFF00FF00, "entity 7 must render green");
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 51, SIZE / 2, 0xFF0000FF, "entity 50020 must render blue");
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 13, SIZE / 2, GlsmSdlHeadlessRig.RED, "entity -1 must render red");
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 32, SIZE / 2, GlsmSdlHeadlessRig.GREEN, "entity 7 must render green");
GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 51, SIZE / 2, GlsmSdlHeadlessRig.BLUE, "entity 50020 must render blue");
}
private static void enableGuiStandardItemLighting() {
@@ -1,12 +1,18 @@
package com.gtnewhorizons.angelica.sdlgpu.glsm;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import static org.junit.jupiter.api.Assertions.assertTrue;
public final class GlsmSdlFrameExtension implements BeforeEachCallback, AfterEachCallback {
public final class GlsmSdlFrameExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback {
@Override
public void beforeAll(ExtensionContext context) {
GlsmSdlHeadlessRig.boot();
}
@Override
public void beforeEach(ExtensionContext context) {
@@ -9,6 +9,7 @@ import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
import com.gtnewhorizons.angelica.sdlgpu.SDLGPUGate;
import com.gtnewhorizons.angelica.sdlgpu.SDLGPURenderBackend;
import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig;
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
import com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineCache;
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
@@ -29,6 +30,14 @@ public final class GlsmSdlHeadlessRig {
public static final int SIZE = 64;
public static final int RED = 0xFFFF0000;
public static final int GREEN = 0xFF00FF00;
public static final int BLUE = 0xFF0000FF;
public static final int BLACK = 0xFF000000;
public static final int TEXEL = 0xFFFF4020;
public static final int LIGHTMAP_SIZE = 16;
private static boolean booted;
private static int fbo;
private static int colorTexture;
@@ -91,9 +100,30 @@ public final class GlsmSdlHeadlessRig {
return bound.get(target);
}
public static FrameManager frameManager() {
return Reflect.get(BackendManager.RENDER_BACKEND, "frameManager");
}
public static ResourceManager resourceManager() {
return Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager");
}
/** Replaces FBO 0's backing target; pair with {@link #dropFinalTarget()}. */
public static void installFinalTarget(int width, int height) {
final FrameManager fm = frameManager();
final ResourceManager rm = resourceManager();
fm.destroyFinalTarget();
fm.finalTarget().create(SDLGPUGate.device(), rm, width, height, rm.mapTextureFormat(GL11.GL_RGBA8));
assertEquals(height, fm.getFbo0Height());
}
public static void dropFinalTarget() {
endFrame();
if (booted) frameManager().destroyFinalTarget();
}
static int colorTargetSdlFormat() {
final ResourceManager rm = Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager");
final ResourceManager.TextureMeta meta = rm.getTextureMeta(colorTexture);
final ResourceManager.TextureMeta meta = resourceManager().getTextureMeta(colorTexture);
return meta != null ? meta.sdlFormat() : 0;
}
@@ -229,24 +259,69 @@ public final class GlsmSdlHeadlessRig {
return fbo;
}
public static int createSolidTexture(int argb) {
final int id = newNearestTexture();
final ByteBuffer texels = MemoryUtil.memAlloc(2 * 2 * 4);
public static void putArgb(ByteBuffer texels, int index, int argb) {
texels.put(index * 4, (byte) ((argb >> 16) & 0xFF));
texels.put(index * 4 + 1, (byte) ((argb >> 8) & 0xFF));
texels.put(index * 4 + 2, (byte) (argb & 0xFF));
texels.put(index * 4 + 3, (byte) ((argb >>> 24) & 0xFF));
}
/** Uploads one solid RGBA8 level of the GL_TEXTURE_2D bound on the active unit. */
public static void uploadSolidLevel(int level, int size, int argb) {
final ByteBuffer texels = MemoryUtil.memAlloc(size * size * 4);
try {
for (int i = 0; i < 4; i++) {
texels.put((byte) ((argb >> 16) & 0xFF));
texels.put((byte) ((argb >> 8) & 0xFF));
texels.put((byte) (argb & 0xFF));
texels.put((byte) ((argb >>> 24) & 0xFF));
}
texels.flip();
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, 2, 2, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels);
for (int i = 0; i < size * size; i++) putArgb(texels, i, argb);
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, level, GL11.GL_RGBA8, size, size, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels);
} finally {
MemoryUtil.memFree(texels);
}
}
public static int createSolidTexture(int argb) {
final int id = newNearestTexture();
uploadSolidLevel(0, 2, argb);
return id;
}
public static int createLightmapTexture(boolean brightCornersOnly) {
final int id = GLStateManager.glGenTextures();
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, id);
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, LIGHTMAP_SIZE, LIGHTMAP_SIZE, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
final ByteBuffer texels = MemoryUtil.memAlloc(LIGHTMAP_SIZE * LIGHTMAP_SIZE * 4);
try {
final int last = LIGHTMAP_SIZE - 1;
for (int y = 0; y < LIGHTMAP_SIZE; y++) {
for (int x = 0; x < LIGHTMAP_SIZE; x++) {
final boolean bright = !brightCornersOnly || (x == 0 && y == 0) || (x == last && y == last);
putArgb(texels, y * LIGHTMAP_SIZE + x, bright ? 0xFFFFFFFF : GREEN);
}
}
GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, LIGHTMAP_SIZE, LIGHTMAP_SIZE, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels);
} finally {
MemoryUtil.memFree(texels);
}
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
return id;
}
public static void bindBaseTexture(int texture) {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
GLStateManager.glEnable(GL11.GL_TEXTURE_2D);
}
public static void quadTwoUnits(short brightness) {
GLStateManager.glBegin(GL11.GL_QUADS);
emitVertex(-1.0f, -1.0f, 0.0f, 0.0f, true, brightness);
emitVertex(1.0f, -1.0f, 1.0f, 0.0f, true, brightness);
emitVertex(1.0f, 1.0f, 1.0f, 1.0f, true, brightness);
emitVertex(-1.0f, 1.0f, 0.0f, 1.0f, true, brightness);
GLStateManager.glEnd();
}
public static void enableLightmapUnit(int lightmapTexture, boolean vanillaTextureMatrix) {
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, lightmapTexture);
@@ -339,19 +414,7 @@ public final class GlsmSdlHeadlessRig {
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, maxLevel);
for (int level = 0; level <= maxLevel; level++) {
final int levelSize = Math.max(1, size >> level);
final ByteBuffer texels = MemoryUtil.memAlloc(levelSize * levelSize * 4);
try {
for (int i = 0; i < levelSize * levelSize; i++) {
texels.put(i * 4, (byte) ((argb >> 16) & 0xFF));
texels.put(i * 4 + 1, (byte) ((argb >> 8) & 0xFF));
texels.put(i * 4 + 2, (byte) (argb & 0xFF));
texels.put(i * 4 + 3, (byte) ((argb >>> 24) & 0xFF));
}
GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, level, GL11.GL_RGBA8, levelSize, levelSize, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels);
} finally {
MemoryUtil.memFree(texels);
}
uploadSolidLevel(level, Math.max(1, size >> level), argb);
}
return id;
}
@@ -2,12 +2,10 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
import com.gtnewhorizons.angelica.sdlgpu.SDLGPURenderBackend;
import com.gtnewhorizons.angelica.sdlgpu.SdlAsserts;
import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig;
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
import com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineCache;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -22,14 +20,8 @@ import static org.lwjgl.sdl.SDLGPU.SDL_GPU_BLENDFACTOR_SRC_ALPHA;
@ExtendWith(GlsmSdlFrameExtension.class)
class GlsmSdlIndexedBlendRestoreTest {
@BeforeAll
static void boot() {
GlsmSdlHeadlessRig.boot();
}
private static PipelineCache pipeline() {
final ContextState st = Reflect.invokeStatic(SDLGPURenderBackend.class, "s", new Class<?>[0]);
return st.pipeline;
return SdlTestRig.contextState().pipeline;
}
private static void vanillaBlendFunc() {
@@ -1,22 +1,20 @@
package com.gtnewhorizons.angelica.sdlgpu.glsm;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.GlTestSupport;
import com.gtnewhorizons.angelica.glsm.testutil.Reflect;
import com.gtnewhorizons.angelica.sdlgpu.SDLGPUGate;
import com.gtnewhorizons.angelica.sdlgpu.device.Device;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.system.MemoryUtil;
import java.nio.FloatBuffer;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Metal steps a per-instance constant binding by 16 bytes per instance.
@@ -48,29 +46,12 @@ class GlsmSdlInstancedConstantTest {
}
""";
@BeforeAll
static void boot() {
GlsmSdlHeadlessRig.boot();
}
private static int compile(int type, String source) {
final int shader = GLStateManager.glCreateShader(type);
GLStateManager.glShaderSource(shader, source);
GLStateManager.glCompileShader(shader);
assertTrue(GLStateManager.glGetShaderi(shader, GL20.GL_COMPILE_STATUS) != 0, () -> GLStateManager.glGetShaderInfoLog(shader));
return shader;
}
@Test
void everyInstanceReadsTheConstantWhenInstancesStepTheBinding() {
final Device device = SDLGPUGate.device();
final String driver = device.getDriverName();
Reflect.set(device, "driverName", "metal");
final int program = GLStateManager.glCreateProgram();
GLStateManager.glAttachShader(program, compile(GL20.GL_VERTEX_SHADER, VERT));
GLStateManager.glAttachShader(program, compile(GL20.GL_FRAGMENT_SHADER, FRAG));
GLStateManager.glLinkProgram(program);
assertTrue(GLStateManager.glGetProgrami(program, GL20.GL_LINK_STATUS) != 0, "program did not link");
final int program = GlTestSupport.program(VERT, FRAG);
final int vao = GLStateManager.glGenVertexArrays();
final int vbo = GLStateManager.glGenBuffers();
@@ -26,7 +26,6 @@ class GlsmSdlLWJGLServiceRoutingTest {
@BeforeAll
static void boot() {
GlsmSdlHeadlessRig.boot();
svc = SDLGPULWJGLService.create();
}
@@ -0,0 +1,187 @@
package com.gtnewhorizons.angelica.sdlgpu.glsm;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.LegacyQueryFixture;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.CLIENT_ARRAY_CAPS;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.HINT_TARGETS;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.assertEnableRoundTrip;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.drainClientAttribStack;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.hex;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.popAttribTo;
import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.setEnabled;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Tag("glsm-sdl")
@ExtendWith(GlsmSdlFrameExtension.class)
class GlsmSdlLegacyQueryTest {
private static final int[] CACHE_ONLY_CAPS = { GL20.GL_POINT_SPRITE, GL20.GL_VERTEX_PROGRAM_TWO_SIDE, GL13.GL_TEXTURE_CUBE_MAP };
// The SDL backend answers 0 or false for a pname it does not know, so a query that fell through to it
// fails the non-zero expectations below.
@Test
void legacyQueriesAreAnsweredWithoutTheBackend() {
final int base = GLStateManager.getAttribDepth();
final IntBuffer ints = BufferUtils.createIntBuffer(16);
final FloatBuffer floats = BufferUtils.createFloatBuffer(16);
final DoubleBuffer doubles = BufferUtils.createDoubleBuffer(16);
final ByteBuffer bytes = BufferUtils.createByteBuffer(16);
final FloatBuffer positions = BufferUtils.createFloatBuffer(12);
final FloatBuffer other = BufferUtils.createFloatBuffer(12);
final FloatBuffer savedColor = BufferUtils.createFloatBuffer(16);
GLStateManager.glGetFloat(GL11.GL_CURRENT_COLOR, savedColor);
final boolean savedLighting = GLStateManager.glIsEnabled(GL11.GL_LIGHTING);
final boolean savedEdge = GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG);
final int[] savedHints = new int[HINT_TARGETS.length];
for (int i = 0; i < HINT_TARGETS.length; i++) {
savedHints[i] = GLStateManager.glGetInteger(HINT_TARGETS[i]);
}
try {
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1);
assertEquals(GL13.GL_TEXTURE1, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE));
GLStateManager.glClientActiveTexture(0);
assertEquals(1, GLStateManager.getClientActiveTextureUnit(), "an invalid unit is rejected");
assertEquals(GLStateManager.MAX_TEXTURE_COORDS, GLStateManager.glGetInteger(GL20.GL_MAX_TEXTURE_COORDS));
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
for (final int cap : CLIENT_ARRAY_CAPS) {
final boolean was = GLStateManager.glIsEnabled(cap);
assertEnableRoundTrip(cap, true);
if (was) GLStateManager.glEnableClientState(cap);
}
for (final int cap : CACHE_ONLY_CAPS) {
final boolean was = GLStateManager.glIsEnabled(cap);
assertEnableRoundTrip(cap, false);
setEnabled(cap, was);
}
GLStateManager.glEdgeFlag(false);
assertFalse(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG));
GLStateManager.glEdgeFlag(true);
assertTrue(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG));
for (final int target : HINT_TARGETS) {
final String name = hex(target);
GLStateManager.glHint(target, GL11.GL_NICEST);
assertEquals(GL11.GL_NICEST, GLStateManager.glGetInteger(target), "set " + name);
assertEquals((float) GL11.GL_NICEST, GLStateManager.glGetFloat(target), "float " + name);
GLStateManager.glPushAttrib(GL11.GL_HINT_BIT);
GLStateManager.glHint(target, GL11.GL_FASTEST);
assertEquals(GL11.GL_FASTEST, GLStateManager.glGetInteger(target), "inside push " + name);
GLStateManager.glPopAttrib();
assertEquals(GL11.GL_NICEST, GLStateManager.glGetInteger(target), "after pop " + name);
}
GLStateManager.glVertexPointer(3, 12, positions);
GLStateManager.glFogCoordPointer(4, positions);
assertEquals(3, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_SIZE));
assertEquals(GL11.GL_FLOAT, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_TYPE));
assertEquals(12, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_STRIDE));
assertEquals(0, GLStateManager.glGetInteger(GL15.GL_VERTEX_ARRAY_BUFFER_BINDING));
assertEquals(4, GLStateManager.glGetInteger(GL14.GL_FOG_COORDINATE_ARRAY_STRIDE));
assertEquals(3, GLStateManager.glGetInteger(GL14.GL_SECONDARY_COLOR_ARRAY_SIZE));
GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT);
GLStateManager.glVertexPointer(2, 8, other);
GLStateManager.glFogCoordPointer(8, other);
GLStateManager.glEnableClientState(GL14.GL_FOG_COORDINATE_ARRAY);
assertEquals(2, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_SIZE));
GLStateManager.glPopClientAttrib();
assertEquals(3, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_SIZE), "client attrib pop restores the pointer");
assertEquals(12, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_STRIDE));
assertEquals(4, GLStateManager.glGetInteger(GL14.GL_FOG_COORDINATE_ARRAY_STRIDE));
assertFalse(GLStateManager.glIsEnabled(GL14.GL_FOG_COORDINATE_ARRAY));
GLStateManager.glColor4f(1.0f, 0.5f, 0.0f, 1.0f);
GLStateManager.glGetInteger(GL11.GL_CURRENT_COLOR, ints);
assertEquals(2147483647, ints.get(0));
assertEquals(1073741823, ints.get(1));
assertEquals(0, ints.get(2));
GLStateManager.glEnable(GL11.GL_LIGHTING);
assertEquals(1, GLStateManager.glGetInteger(GL11.GL_LIGHTING));
assertEquals(1.0f, GLStateManager.glGetFloat(GL11.GL_LIGHTING));
assertEquals((float) GLStateManager.glGetInteger(GL11.GL_FOG_MODE), GLStateManager.glGetFloat(GL11.GL_FOG_MODE));
assertEquals(GL11.GL_RENDER, GLStateManager.glGetInteger(GL11.GL_RENDER_MODE));
for (final int pname : LegacyQueryFixture.legacyPnames()) {
final String name = hex(pname);
final int scalar = GLStateManager.glGetInteger(pname);
final float scalarFloat = GLStateManager.glGetFloat(pname);
final boolean scalarBool = GLStateManager.glGetBoolean(pname);
ints.clear();
GLStateManager.glGetInteger(pname, ints);
floats.clear();
GLStateManager.glGetFloat(pname, floats);
doubles.clear();
GLStateManager.glGetDouble(pname, doubles);
bytes.clear();
GLStateManager.glGetBoolean(pname, bytes);
assertEquals(scalar, ints.get(0), "IntBuffer overload for " + name);
assertEquals(scalarFloat, floats.get(0), "FloatBuffer overload for " + name);
assertEquals((double) scalarFloat, doubles.get(0), "DoubleBuffer overload for " + name);
assertEquals(scalarBool, bytes.get(0) != 0, "ByteBuffer overload for " + name);
}
} finally {
drainClientAttribStack();
popAttribTo(base);
GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glDisableClientState(GL14.GL_FOG_COORDINATE_ARRAY);
for (int i = 0; i < HINT_TARGETS.length; i++) {
GLStateManager.glHint(HINT_TARGETS[i], savedHints[i]);
}
GLStateManager.glEdgeFlag(savedEdge);
GLStateManager.glColor4f(savedColor.get(0), savedColor.get(1), savedColor.get(2), savedColor.get(3));
setEnabled(GL11.GL_LIGHTING, savedLighting);
}
}
@Test
void pointParametersRoundTrip() {
final FloatBuffer attenuation = BufferUtils.createFloatBuffer(16);
final float savedMin = GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN);
final float savedMax = GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX);
final float savedThreshold = GLStateManager.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE);
final int savedOrigin = GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN);
final int base = GLStateManager.getAttribDepth();
try {
assertEquals(1.0f, savedMax);
GLStateManager.glPushAttrib(GL11.GL_POINT_BIT);
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, 2.0f);
GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MAX, 8.0f);
GLStateManager.glPointParameterf(GL14.GL_POINT_FADE_THRESHOLD_SIZE, 3.0f);
GLStateManager.glPointParameteri(GL20.GL_POINT_SPRITE_COORD_ORIGIN, GL20.GL_LOWER_LEFT);
attenuation.clear();
attenuation.put(0, 0.5f).put(1, 0.25f).put(2, 0.125f);
GLStateManager.glPointParameter(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation);
assertEquals(2.0f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN));
assertEquals(8.0f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX));
assertEquals(3.0f, GLStateManager.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE));
assertEquals(GL20.GL_LOWER_LEFT, GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN));
attenuation.clear();
GLStateManager.glGetFloat(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation);
assertEquals(0.25f, attenuation.get(1));
GLStateManager.glPopAttrib();
assertEquals(savedMin, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN));
assertEquals(savedMax, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX));
assertEquals(savedThreshold, GLStateManager.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE));
assertEquals(savedOrigin, GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN));
} finally {
popAttribTo(base);
}
}
}
Loaded 100 of 264 files, more files were not shown because too many files have changed in this diff. Show more