diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLContextState.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLContextState.java index 66cb88594..76df251e4 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLContextState.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLContextState.java @@ -30,7 +30,7 @@ import com.gtnewhorizons.angelica.glsm.states.AlphaState; import com.gtnewhorizons.angelica.glsm.states.BlendState; import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState; import com.gtnewhorizons.angelica.glsm.states.Color4; -import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState; +import com.gtnewhorizons.angelica.glsm.states.PixelStoreState; import com.gtnewhorizons.angelica.glsm.states.ImageUnitArray; import com.gtnewhorizons.angelica.glsm.states.SamplerUnitArray; import com.gtnewhorizons.angelica.glsm.states.TextureUnitArray; @@ -231,9 +231,16 @@ public class GLContextState { public int boundCopyReadBuffer; public int boundCopyWriteBuffer; public final Int2IntMap boundOtherBuffers = new Int2IntOpenHashMap(); - public PixelUnpackState pixelUnpackState = PixelUnpackState.DEFAULT; + public PixelStoreState pixelUnpackState = PixelStoreState.DEFAULT; + public PixelStoreState pixelPackState = PixelStoreState.DEFAULT; + public final int[] clientAttribSavedMask = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH]; + public final PixelStoreState[] clientAttribSavedPixelUnpack = new PixelStoreState[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH]; + public final PixelStoreState[] clientAttribSavedPixelPack = new PixelStoreState[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH]; + public final int[] clientAttribSavedUnpackBuffer = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH]; + public final int[] clientAttribSavedPackBuffer = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH]; public final int[] clientAttribSavedTextureUnit = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH]; public final int[] clientAttribSavedVertexFlags = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH]; + public final boolean[] clientAttribSavedVertexArray = new boolean[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH]; public int clientAttribStackPointer = 0; public int restoreChangedMask; public long restoreUnitChangedMask; diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java index 04c7309d1..53397c6e2 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java @@ -61,7 +61,7 @@ import com.gtnewhorizons.angelica.glsm.states.BlendState; import com.gtnewhorizons.angelica.glsm.states.ClipPlaneState; import com.gtnewhorizons.angelica.glsm.states.Color4; import com.gtnewhorizons.angelica.glsm.states.ColorMask; -import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState; +import com.gtnewhorizons.angelica.glsm.states.PixelStoreState; import com.gtnewhorizons.angelica.glsm.states.PolygonState; import com.gtnewhorizons.angelica.glsm.states.SamplerUnitArray; import com.gtnewhorizons.angelica.glsm.states.ImageUnitBinding; @@ -327,6 +327,7 @@ public class GLStateManager { public static final int MAX_TEXTURE_STACK_DEPTH = 4; public static final int MAX_CLIP_PLANES = 8; public static final int MAX_TEXTURE_UNITS = RENDER_BACKEND.getInteger(GL20.GL_MAX_TEXTURE_IMAGE_UNITS); + public static final int MAX_LEGACY_TEXTURE_UNITS = Math.min(VertexKey.MAX_UNITS, MAX_TEXTURE_UNITS); public static final GLFeatureSet HAS_MULTIPLE_SET = new GLFeatureSet(); @@ -1192,11 +1193,35 @@ public class GLStateManager { case GL11.GL_LINE_STIPPLE_PATTERN -> glCtx.lineState.getStipplePattern() & 0xFFFF; case GL11.GL_LINE_STIPPLE_REPEAT -> glCtx.lineState.getStippleFactor(); case GL11.GL_LIST_INDEX -> DisplayListManager.isRecording() ? Math.max(DisplayListManager.getRecordingListId(), 0) : 0; + case GL11.GL_ATTRIB_STACK_DEPTH -> glCtx.attribDepth; + case GL11.GL_CLIENT_ATTRIB_STACK_DEPTH -> glCtx.clientAttribStackPointer; + case GL11.GL_TEXTURE_STACK_DEPTH -> getMatrixStackDepth(glCtx.textures.getTextureUnitMatrix(glCtx.activeTextureUnit.getValue())); + case GL11.GL_MAX_ATTRIB_STACK_DEPTH -> MAX_ATTRIB_STACK_DEPTH; + case GL11.GL_MAX_CLIENT_ATTRIB_STACK_DEPTH -> CLIENT_ATTRIB_STACK_DEPTH; + case GL11.GL_MAX_MODELVIEW_STACK_DEPTH -> MAX_MODELVIEW_STACK_DEPTH; + case GL11.GL_MAX_PROJECTION_STACK_DEPTH -> MAX_PROJECTION_STACK_DEPTH; + case GL11.GL_MAX_TEXTURE_STACK_DEPTH -> MAX_TEXTURE_STACK_DEPTH; + case GL13.GL_MAX_TEXTURE_UNITS -> MAX_LEGACY_TEXTURE_UNITS; + case GL11.GL_MAX_LIGHTS -> glCtx.lightStates.length; + case GL11.GL_STENCIL_BITS -> drawFramebufferBits(glCtx, true); + case GL11.GL_DEPTH_BITS -> drawFramebufferBits(glCtx, false); default -> RENDER_BACKEND.getInteger(pname); }; }; } + private static int drawFramebufferBits(GLContextState glCtx, boolean stencil) { + final int sizePname = stencil ? GL30.GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE : GL30.GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE; + if (glCtx.drawFramebuffer == 0) { + return RENDER_BACKEND.getFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, stencil ? GL11.GL_STENCIL : GL11.GL_DEPTH, sizePname); + } + final int attachment = stencil ? GL30.GL_STENCIL_ATTACHMENT : GL30.GL_DEPTH_ATTACHMENT; + if (RENDER_BACKEND.getFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, attachment, GL30.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE) == GL11.GL_NONE) { + return 0; + } + return RENDER_BACKEND.getFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, attachment, sizePname); + } + public static void glGetInteger(int pname, IntBuffer params) { if (!isCachingEnabled()) { RENDER_BACKEND.getInteger(pname, params); @@ -1245,6 +1270,48 @@ public class GLStateManager { } } + private static void putLightingInts(FloatBuffer values, int pname, boolean material, IntBuffer params) { + final int count = switch (pname) { + case GL11.GL_AMBIENT, GL11.GL_DIFFUSE, GL11.GL_SPECULAR -> 4; + case GL11.GL_EMISSION -> material ? 4 : 0; + case GL11.GL_COLOR_INDEXES -> material ? 3 : 0; + case GL11.GL_SHININESS -> material ? 1 : 0; + case GL11.GL_POSITION -> material ? 0 : 4; + case GL11.GL_SPOT_DIRECTION -> material ? 0 : 3; + case GL11.GL_SPOT_EXPONENT, GL11.GL_SPOT_CUTOFF, GL11.GL_CONSTANT_ATTENUATION, GL11.GL_LINEAR_ATTENUATION, + GL11.GL_QUADRATIC_ATTENUATION -> material ? 0 : 1; + default -> 0; + }; + final boolean color = count == 4 && pname != GL11.GL_POSITION; + final int pos = params.position(); + for (int i = 0; i < count; i++) { + final float value = values.get(i); + final int converted; + if (color) { + converted = (int) (2147483647.0 * value); + } else if (material) { + converted = value < 0 ? -Math.round(-value) : Math.round(value); + } else { + converted = (int) value; + } + params.put(pos + i, converted); + } + } + + public static void glGetMaterial(int face, int pname, IntBuffer params) { + final FloatBuffer scratch = ctx().queryScratch; + scratch.clear(); + glGetMaterial(face, pname, scratch); + putLightingInts(scratch, pname, true, params); + } + + public static void glGetLight(int light, int pname, IntBuffer params) { + final FloatBuffer scratch = ctx().queryScratch; + scratch.clear(); + glGetLight(light, pname, scratch); + putLightingInts(scratch, pname, false, params); + } + public static void glGetLight(int light, int pname, FloatBuffer params) { final LightStateStack state = ctx().lightDataStates[light - GL11.GL_LIGHT0]; switch (pname) { @@ -1297,12 +1364,25 @@ public class GLStateManager { params.put(pos + 2, normal.z); } case GL11.GL_CURRENT_TEXTURE_COORDS -> { - final Vector4f tc = glCtx.ffp.currentTexCoords[0]; + final int unit = glCtx.activeTextureUnit.getValue(); final int pos = params.position(); - params.put(pos, tc.x); - params.put(pos + 1, tc.y); - params.put(pos + 2, tc.z); - params.put(pos + 3, tc.w); + if (unit == 1) { + params.put(pos, glCtx.lastBrightnessX); + params.put(pos + 1, glCtx.lastBrightnessY); + params.put(pos + 2, 0.0f); + params.put(pos + 3, 1.0f); + } else if (unit >= 0 && unit < glCtx.ffp.currentTexCoords.length) { + final Vector4f tc = glCtx.ffp.currentTexCoords[unit]; + params.put(pos, tc.x); + params.put(pos + 1, tc.y); + params.put(pos + 2, tc.z); + params.put(pos + 3, tc.w); + } else { + params.put(pos, 0.0f); + params.put(pos + 1, 0.0f); + params.put(pos + 2, 0.0f); + params.put(pos + 3, 1.0f); + } } case GL11.GL_FOG_COLOR -> { final FloatBuffer fogBuf = glCtx.fogState.getFogColorBuffer(); @@ -3258,22 +3338,63 @@ public class GLStateManager { }; } + private static boolean isPositionArrayEnabled(GLContextState glCtx) { + final VAOManager.Attrib position = glCtx.vaos.attrib(Usage.POSITION.getAttributeLocation()); + return position != null && position.enabled; + } + static final int CLIENT_ATTRIB_STACK_DEPTH = 16; public static void glPushClientAttrib(int mask) { final GLContextState glCtx = ctx(); - if (glCtx.clientAttribStackPointer < CLIENT_ATTRIB_STACK_DEPTH) { - glCtx.clientAttribSavedTextureUnit[glCtx.clientAttribStackPointer] = glCtx.clientActiveTextureUnit; - glCtx.clientAttribSavedVertexFlags[glCtx.clientAttribStackPointer] = glCtx.vaos.getVertexFlags(); - glCtx.clientAttribStackPointer++; + final int sp = glCtx.clientAttribStackPointer; + if (sp < CLIENT_ATTRIB_STACK_DEPTH) { + glCtx.clientAttribSavedMask[sp] = mask; + if ((mask & GL11.GL_CLIENT_PIXEL_STORE_BIT) != 0) { + glCtx.clientAttribSavedPixelPack[sp] = glCtx.pixelPackState; + glCtx.clientAttribSavedPixelUnpack[sp] = glCtx.pixelUnpackState; + glCtx.clientAttribSavedPackBuffer[sp] = glCtx.boundPixelPackBuffer; + glCtx.clientAttribSavedUnpackBuffer[sp] = glCtx.boundPixelUnpackBuffer; + } + if ((mask & GL11.GL_CLIENT_VERTEX_ARRAY_BIT) != 0) { + glCtx.clientAttribSavedTextureUnit[sp] = glCtx.clientActiveTextureUnit; + glCtx.clientAttribSavedVertexFlags[sp] = glCtx.vaos.getVertexFlags(); + glCtx.clientAttribSavedVertexArray[sp] = isPositionArrayEnabled(glCtx); + } + glCtx.clientAttribStackPointer = sp + 1; } } public static void glPopClientAttrib() { final GLContextState glCtx = ctx(); if (glCtx.clientAttribStackPointer > 0) { - glCtx.clientAttribStackPointer--; - glCtx.clientActiveTextureUnit = glCtx.clientAttribSavedTextureUnit[glCtx.clientAttribStackPointer]; - glCtx.vaos.setVertexFlags(glCtx.clientAttribSavedVertexFlags[glCtx.clientAttribStackPointer]); + final int sp = --glCtx.clientAttribStackPointer; + final int mask = glCtx.clientAttribSavedMask[sp]; + if ((mask & GL11.GL_CLIENT_PIXEL_STORE_BIT) != 0) { + final PixelStoreState pack = glCtx.clientAttribSavedPixelPack[sp]; + final PixelStoreState unpack = glCtx.clientAttribSavedPixelUnpack[sp]; + glCtx.clientAttribSavedPixelPack[sp] = null; + glCtx.clientAttribSavedPixelUnpack[sp] = null; + PixelStoreState.applyDiff(glCtx.pixelPackState, pack, true); + glCtx.pixelPackState = pack; + PixelStoreState.applyDiff(glCtx.pixelUnpackState, unpack, false); + glCtx.pixelUnpackState = unpack; + final int packBuffer = glCtx.clientAttribSavedPackBuffer[sp]; + if (glCtx.boundPixelPackBuffer != packBuffer) glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, packBuffer); + final int unpackBuffer = glCtx.clientAttribSavedUnpackBuffer[sp]; + if (glCtx.boundPixelUnpackBuffer != unpackBuffer) glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, unpackBuffer); + } + if ((mask & GL11.GL_CLIENT_VERTEX_ARRAY_BIT) != 0) { + glCtx.clientActiveTextureUnit = glCtx.clientAttribSavedTextureUnit[sp]; + glCtx.vaos.setVertexFlags(glCtx.clientAttribSavedVertexFlags[sp]); + final boolean vertexArray = glCtx.clientAttribSavedVertexArray[sp]; + if (isPositionArrayEnabled(glCtx) != vertexArray) { + if (vertexArray) { + glEnableClientState(GL11.GL_VERTEX_ARRAY); + } else { + glDisableClientState(GL11.GL_VERTEX_ARRAY); + } + } + } } } @@ -4530,6 +4651,10 @@ public class GLStateManager { return; } } + if (ctx().attribDepth == 0) { + warnOnce("popattrib-underflow", "glPopAttrib called with an empty attrib stack; ignoring"); + return; + } GLDebug.popGroup(); GLDebug.pushGroup("popState"); try { @@ -6411,7 +6536,11 @@ public class GLStateManager { public static void glPixelStorei(int pname, int param) { final GLContextState glCtx = ctx(); if (isCachingEnabled()) { - glCtx.pixelUnpackState = glCtx.pixelUnpackState.with(pname, param); + if (PixelStoreState.isPack(pname)) { + glCtx.pixelPackState = glCtx.pixelPackState.with(pname, param); + } else { + glCtx.pixelUnpackState = glCtx.pixelUnpackState.with(pname, param); + } } RENDER_BACKEND.pixelStorei(pname, param); } @@ -6712,12 +6841,12 @@ public class GLStateManager { } } - public static void forcePixelUnpackState(PixelUnpackState target) { - PixelUnpackState.applyDiff(ctx().pixelUnpackState, target); + public static void forcePixelUnpackState(PixelStoreState target) { + PixelStoreState.applyDiff(ctx().pixelUnpackState, target, false); } - public static void restorePixelUnpackState(PixelUnpackState applied) { - PixelUnpackState.applyDiff(applied, ctx().pixelUnpackState); + public static void restorePixelUnpackState(PixelStoreState applied) { + PixelStoreState.applyDiff(applied, ctx().pixelUnpackState, false); } private static boolean warnedUntrackedPixelBuffer; diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/LTWWorkaround.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/LTWWorkaround.java index 3e8d760cb..a98aa2e21 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/LTWWorkaround.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/LTWWorkaround.java @@ -1,6 +1,6 @@ package com.gtnewhorizons.angelica.glsm; -import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState; +import com.gtnewhorizons.angelica.glsm.states.PixelStoreState; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; @@ -25,10 +25,10 @@ public final class LTWWorkaround { oneTexel = BufferUtils.createByteBuffer(4); } oneTexel.clear(); - GLStateManager.forcePixelUnpackState(PixelUnpackState.DEFAULT); + GLStateManager.forcePixelUnpackState(PixelStoreState.DEFAULT); GLStateManager.suspendPixelUnpackBuffer(); RENDER_BACKEND.texSubImage2D(target, level, 0, 0, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, oneTexel); GLStateManager.restorePixelUnpackBuffer(); - GLStateManager.restorePixelUnpackState(PixelUnpackState.DEFAULT); + GLStateManager.restorePixelUnpackState(PixelStoreState.DEFAULT); } } diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/recording/commands/TexImage2DCmd.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/recording/commands/TexImage2DCmd.java index b641204d6..708da6c9f 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/recording/commands/TexImage2DCmd.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/recording/commands/TexImage2DCmd.java @@ -3,7 +3,7 @@ package com.gtnewhorizons.angelica.glsm.recording.commands; import com.gtnewhorizons.angelica.glsm.GLDebug; import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.glsm.GLTypes; -import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState; +import com.gtnewhorizons.angelica.glsm.states.PixelStoreState; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -28,7 +28,7 @@ public record TexImage2DCmd( int format, int type, @Nullable ByteBuffer pixels, - @Nullable PixelUnpackState unpack + @Nullable PixelStoreState unpack ) implements DisplayListCommand { public TexImage2DCmd { @@ -37,19 +37,19 @@ public record TexImage2DCmd( } } - public static TexImage2DCmd fromIntBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable IntBuffer pixels, PixelUnpackState unpack) { + public static TexImage2DCmd fromIntBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable IntBuffer pixels, PixelStoreState unpack) { return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack); } - public static TexImage2DCmd fromFloatBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable FloatBuffer pixels, PixelUnpackState unpack) { + public static TexImage2DCmd fromFloatBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable FloatBuffer pixels, PixelStoreState unpack) { return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack); } - public static TexImage2DCmd fromDoubleBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable DoubleBuffer pixels, PixelUnpackState unpack) { + public static TexImage2DCmd fromDoubleBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable DoubleBuffer pixels, PixelStoreState unpack) { return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack); } - public static TexImage2DCmd fromByteBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable ByteBuffer pixels, PixelUnpackState unpack) { + public static TexImage2DCmd fromByteBuffer(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable ByteBuffer pixels, PixelStoreState unpack) { return new TexImage2DCmd(target, level, internalformat, width, height, border, format, type, PixelDataSnapshot.copy(pixels), unpack); } diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/recording/commands/TexSubImage2DCmd.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/recording/commands/TexSubImage2DCmd.java index 2b824ff58..3563a1d42 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/recording/commands/TexSubImage2DCmd.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/recording/commands/TexSubImage2DCmd.java @@ -3,7 +3,7 @@ package com.gtnewhorizons.angelica.glsm.recording.commands; import com.gtnewhorizons.angelica.glsm.GLDebug; import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.glsm.GLTypes; -import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState; +import com.gtnewhorizons.angelica.glsm.states.PixelStoreState; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -28,7 +28,7 @@ public record TexSubImage2DCmd( int format, int type, @Nullable ByteBuffer pixels, - @Nullable PixelUnpackState unpack + @Nullable PixelStoreState unpack ) implements DisplayListCommand { public TexSubImage2DCmd { @@ -37,19 +37,19 @@ public record TexSubImage2DCmd( } } - public static TexSubImage2DCmd fromIntBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable IntBuffer pixels, PixelUnpackState unpack) { + public static TexSubImage2DCmd fromIntBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable IntBuffer pixels, PixelStoreState unpack) { return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack); } - public static TexSubImage2DCmd fromFloatBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable FloatBuffer pixels, PixelUnpackState unpack) { + public static TexSubImage2DCmd fromFloatBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable FloatBuffer pixels, PixelStoreState unpack) { return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack); } - public static TexSubImage2DCmd fromDoubleBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable DoubleBuffer pixels, PixelUnpackState unpack) { + public static TexSubImage2DCmd fromDoubleBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable DoubleBuffer pixels, PixelStoreState unpack) { return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack); } - public static TexSubImage2DCmd fromByteBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable ByteBuffer pixels, PixelUnpackState unpack) { + public static TexSubImage2DCmd fromByteBuffer(int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, @Nullable ByteBuffer pixels, PixelStoreState unpack) { return new TexSubImage2DCmd(target, level, xoffset, yoffset, width, height, format, type, PixelDataSnapshot.copy(pixels), unpack); } diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirector.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirector.java index 5fda855f5..f06cb6ccf 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirector.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirector.java @@ -626,6 +626,7 @@ public class GLSMRedirector { .add("glGenRenderbuffers") .add("glRenderbufferStorage") .add("glFramebufferRenderbuffer") + .add("glGenerateMipmap") ); methodRedirects.put(EXTFramebufferObject, RedirectMap.newMap() .add("glBindFramebufferEXT", "glBindFramebuffer") @@ -638,6 +639,7 @@ public class GLSMRedirector { .add("glGenRenderbuffersEXT", "glGenRenderbuffers") .add("glRenderbufferStorageEXT", "glRenderbufferStorage") .add("glFramebufferRenderbufferEXT", "glFramebufferRenderbuffer") + .add("glGenerateMipmapEXT", "glGenerateMipmap") ); methodRedirects.put(ARBOcclusionQuery, RedirectMap.newMap() .add("glGenQueriesARB", "glGenQueries") diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/states/PixelStoreState.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/states/PixelStoreState.java new file mode 100644 index 000000000..c8ac9d09d --- /dev/null +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/states/PixelStoreState.java @@ -0,0 +1,57 @@ +package com.gtnewhorizons.angelica.glsm.states; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND; + +public record PixelStoreState( + int alignment, + int rowLength, + int skipRows, + int skipPixels, + int imageHeight, + int skipImages, + boolean swapBytes, + boolean lsbFirst +) { + public static final PixelStoreState DEFAULT = new PixelStoreState(4, 0, 0, 0, 0, 0, false, false); + + public boolean isDefault() { + return this.equals(DEFAULT); + } + + public static boolean isPack(int pname) { + return switch (pname) { + case GL11.GL_PACK_ALIGNMENT, GL11.GL_PACK_ROW_LENGTH, GL11.GL_PACK_SKIP_ROWS, GL11.GL_PACK_SKIP_PIXELS, + GL12.GL_PACK_IMAGE_HEIGHT, GL12.GL_PACK_SKIP_IMAGES, GL11.GL_PACK_SWAP_BYTES, GL11.GL_PACK_LSB_FIRST -> true; + default -> false; + }; + } + + public PixelStoreState with(int pname, int param) { + return switch (pname) { + case GL11.GL_UNPACK_ALIGNMENT, GL11.GL_PACK_ALIGNMENT -> (param == alignment || (param != 1 && param != 2 && param != 4 && param != 8)) ? this : new PixelStoreState(param, rowLength, skipRows, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst); + case GL11.GL_UNPACK_ROW_LENGTH, GL11.GL_PACK_ROW_LENGTH -> (param == rowLength || param < 0) ? this : new PixelStoreState(alignment, param, skipRows, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst); + case GL11.GL_UNPACK_SKIP_ROWS, GL11.GL_PACK_SKIP_ROWS -> (param == skipRows || param < 0) ? this : new PixelStoreState(alignment, rowLength, param, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst); + case GL11.GL_UNPACK_SKIP_PIXELS, GL11.GL_PACK_SKIP_PIXELS -> (param == skipPixels || param < 0) ? this : new PixelStoreState(alignment, rowLength, skipRows, param, imageHeight, skipImages, swapBytes, lsbFirst); + case GL12.GL_UNPACK_IMAGE_HEIGHT, GL12.GL_PACK_IMAGE_HEIGHT -> (param == imageHeight || param < 0) ? this : new PixelStoreState(alignment, rowLength, skipRows, skipPixels, param, skipImages, swapBytes, lsbFirst); + case GL12.GL_UNPACK_SKIP_IMAGES, GL12.GL_PACK_SKIP_IMAGES -> (param == skipImages || param < 0) ? this : new PixelStoreState(alignment, rowLength, skipRows, skipPixels, imageHeight, param, swapBytes, lsbFirst); + case GL11.GL_UNPACK_SWAP_BYTES, GL11.GL_PACK_SWAP_BYTES -> (param != 0) == swapBytes ? this : new PixelStoreState(alignment, rowLength, skipRows, skipPixels, imageHeight, skipImages, param != 0, lsbFirst); + case GL11.GL_UNPACK_LSB_FIRST, GL11.GL_PACK_LSB_FIRST -> (param != 0) == lsbFirst ? this : new PixelStoreState(alignment, rowLength, skipRows, skipPixels, imageHeight, skipImages, swapBytes, param != 0); + default -> this; + }; + } + + public static void applyDiff(PixelStoreState from, PixelStoreState to, boolean pack) { + if (from == to) return; + if (from.alignment != to.alignment) RENDER_BACKEND.pixelStorei(pack ? GL11.GL_PACK_ALIGNMENT : GL11.GL_UNPACK_ALIGNMENT, to.alignment); + if (from.rowLength != to.rowLength) RENDER_BACKEND.pixelStorei(pack ? GL11.GL_PACK_ROW_LENGTH : GL11.GL_UNPACK_ROW_LENGTH, to.rowLength); + if (from.skipRows != to.skipRows) RENDER_BACKEND.pixelStorei(pack ? GL11.GL_PACK_SKIP_ROWS : GL11.GL_UNPACK_SKIP_ROWS, to.skipRows); + if (from.skipPixels != to.skipPixels) RENDER_BACKEND.pixelStorei(pack ? GL11.GL_PACK_SKIP_PIXELS : GL11.GL_UNPACK_SKIP_PIXELS, to.skipPixels); + if (from.imageHeight != to.imageHeight) RENDER_BACKEND.pixelStorei(pack ? GL12.GL_PACK_IMAGE_HEIGHT : GL12.GL_UNPACK_IMAGE_HEIGHT, to.imageHeight); + if (from.skipImages != to.skipImages) RENDER_BACKEND.pixelStorei(pack ? GL12.GL_PACK_SKIP_IMAGES : GL12.GL_UNPACK_SKIP_IMAGES, to.skipImages); + if (from.swapBytes != to.swapBytes) RENDER_BACKEND.pixelStorei(pack ? GL11.GL_PACK_SWAP_BYTES : GL11.GL_UNPACK_SWAP_BYTES, to.swapBytes ? 1 : 0); + if (from.lsbFirst != to.lsbFirst) RENDER_BACKEND.pixelStorei(pack ? GL11.GL_PACK_LSB_FIRST : GL11.GL_UNPACK_LSB_FIRST, to.lsbFirst ? 1 : 0); + } +} diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/states/PixelUnpackState.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/states/PixelUnpackState.java deleted file mode 100644 index d5423f05f..000000000 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/states/PixelUnpackState.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.gtnewhorizons.angelica.glsm.states; - -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL12; - -import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND; - -/** - * Immutable client-side pixel unpack state (glPixelStore UNPACK_* params) - */ -public record PixelUnpackState( - int alignment, - int rowLength, - int skipRows, - int skipPixels, - int imageHeight, - int skipImages, - boolean swapBytes, - boolean lsbFirst -) { - public static final PixelUnpackState DEFAULT = new PixelUnpackState(4, 0, 0, 0, 0, 0, false, false); - - public boolean isDefault() { - return this.equals(DEFAULT); - } - - public PixelUnpackState with(int pname, int param) { - return switch (pname) { - case GL11.GL_UNPACK_ALIGNMENT -> (param == 1 || param == 2 || param == 4 || param == 8) ? new PixelUnpackState(param, rowLength, skipRows, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst) : this; - case GL11.GL_UNPACK_ROW_LENGTH -> param >= 0 ? new PixelUnpackState(alignment, param, skipRows, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst) : this; - case GL11.GL_UNPACK_SKIP_ROWS -> param >= 0 ? new PixelUnpackState(alignment, rowLength, param, skipPixels, imageHeight, skipImages, swapBytes, lsbFirst) : this; - case GL11.GL_UNPACK_SKIP_PIXELS -> param >= 0 ? new PixelUnpackState(alignment, rowLength, skipRows, param, imageHeight, skipImages, swapBytes, lsbFirst) : this; - case GL12.GL_UNPACK_IMAGE_HEIGHT -> param >= 0 ? new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, param, skipImages, swapBytes, lsbFirst) : this; - case GL12.GL_UNPACK_SKIP_IMAGES -> param >= 0 ? new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, imageHeight, param, swapBytes, lsbFirst) : this; - case GL11.GL_UNPACK_SWAP_BYTES -> new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, imageHeight, skipImages, param != 0, lsbFirst); - case GL11.GL_UNPACK_LSB_FIRST -> new PixelUnpackState(alignment, rowLength, skipRows, skipPixels, imageHeight, skipImages, swapBytes, param != 0); - default -> this; - }; - } - - public static void applyDiff(PixelUnpackState from, PixelUnpackState to) { - if (from.alignment != to.alignment) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_ALIGNMENT, to.alignment); - if (from.rowLength != to.rowLength) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_ROW_LENGTH, to.rowLength); - if (from.skipRows != to.skipRows) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_SKIP_ROWS, to.skipRows); - if (from.skipPixels != to.skipPixels) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, to.skipPixels); - if (from.imageHeight != to.imageHeight) RENDER_BACKEND.pixelStorei(GL12.GL_UNPACK_IMAGE_HEIGHT, to.imageHeight); - if (from.skipImages != to.skipImages) RENDER_BACKEND.pixelStorei(GL12.GL_UNPACK_SKIP_IMAGES, to.skipImages); - if (from.swapBytes != to.swapBytes) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_SWAP_BYTES, to.swapBytes ? 1 : 0); - if (from.lsbFirst != to.lsbFirst) RENDER_BACKEND.pixelStorei(GL11.GL_UNPACK_LSB_FIRST, to.lsbFirst ? 1 : 0); - } -} diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_LegacyStackQueries_GLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_LegacyStackQueries_GLTest.java new file mode 100644 index 000000000..96236c5bb --- /dev/null +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_LegacyStackQueries_GLTest.java @@ -0,0 +1,737 @@ +package com.gtnewhorizons.angelica.glsm; + +import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags; +import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage; +import com.gtnewhorizons.angelica.glsm.ffp.VAOManager; +import com.gtnewhorizons.angelica.glsm.ffp.VertexKey; +import com.gtnewhorizons.angelica.glsm.states.PixelStoreState; +import org.junit.jupiter.api.Test; +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL14; +import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL21; +import org.lwjgl.opengl.GL30; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +@GLCoreTest +public class GLSM_LegacyStackQueries_GLTest { + + private static final int[] SINGLE_VALUE_QUERIES = { + GL11.GL_ATTRIB_STACK_DEPTH, + GL11.GL_CLIENT_ATTRIB_STACK_DEPTH, + GL11.GL_TEXTURE_STACK_DEPTH, + GL11.GL_MAX_ATTRIB_STACK_DEPTH, + GL11.GL_MAX_CLIENT_ATTRIB_STACK_DEPTH, + GL11.GL_MAX_MODELVIEW_STACK_DEPTH, + GL11.GL_MAX_PROJECTION_STACK_DEPTH, + GL11.GL_MAX_TEXTURE_STACK_DEPTH, + GL13.GL_MAX_TEXTURE_UNITS, + GL11.GL_MAX_LIGHTS, + GL11.GL_STENCIL_BITS, + GL11.GL_DEPTH_BITS, + }; + + private static boolean positionArrayEnabled() { + final VAOManager.Attrib position = GLStateManager.ctx().vaos.attrib(Usage.POSITION.getAttributeLocation()); + return position != null && position.enabled; + } + + private static boolean colorArrayEnabled() { + return (GLStateManager.ctx().vaos.getVertexFlags() & VertexFlags.COLOR_BIT) != 0; + } + + private static void drainErrors() { + while (GL11.glGetError() != GL11.GL_NO_ERROR) {} + } + + private static void drainClientAttribStack() { + while (GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH) > 0) { + GLStateManager.glPopClientAttrib(); + } + } + + private static void drainAttribStack() { + while (GLStateManager.getAttribDepth() > 0) { + GLStateManager.glPopAttrib(); + } + } + + private static int pushToLimit(int depthPname, int maxPname) { + final int max = GLStateManager.glGetInteger(maxPname); + for (int i = 0; i < max + 4; i++) { + GLStateManager.glPushMatrix(); + } + return GLStateManager.glGetInteger(depthPname); + } + + private static void popToBase(int depthPname) { + for (int i = 0; i < 128 && GLStateManager.glGetInteger(depthPname) > 1; i++) { + GLStateManager.glPopMatrix(); + } + } + + @Test + void attribDepthTracksPushAndPop() { + final int base = GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH); + try { + GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT); + assertEquals(base + 1, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH)); + GLStateManager.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT); + assertEquals(base + 2, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH)); + GLStateManager.glPopAttrib(); + assertEquals(base + 1, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH)); + GLStateManager.glPopAttrib(); + assertEquals(base, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH)); + assertEquals(GLStateManager.MAX_ATTRIB_STACK_DEPTH, GLStateManager.glGetInteger(GL11.GL_MAX_ATTRIB_STACK_DEPTH)); + } finally { + while (GLStateManager.getAttribDepth() > base) { + GLStateManager.glPopAttrib(); + } + } + } + + @Test + void clientAttribDepthTracksPushAndPop() { + try { + assertEquals(0, GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH)); + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT); + assertEquals(1, GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH)); + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + assertEquals(2, GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH)); + GLStateManager.glPopClientAttrib(); + assertEquals(1, GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH)); + GLStateManager.glPopClientAttrib(); + assertEquals(0, GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH)); + GLStateManager.glPopClientAttrib(); + assertEquals(0, GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH)); + + final int max = GLStateManager.glGetInteger(GL11.GL_MAX_CLIENT_ATTRIB_STACK_DEPTH); + for (int i = 0; i < max + 2; i++) { + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT); + } + assertEquals(max, GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH)); + } finally { + drainClientAttribStack(); + } + } + + @Test + void textureStackDepthIsPerActiveUnit() { + try { + GLStateManager.glMatrixMode(GL11.GL_TEXTURE); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + assertEquals(1, GLStateManager.glGetInteger(GL11.GL_TEXTURE_STACK_DEPTH)); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); + GLStateManager.glPushMatrix(); + assertEquals(2, GLStateManager.glGetInteger(GL11.GL_TEXTURE_STACK_DEPTH)); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + assertEquals(1, GLStateManager.glGetInteger(GL11.GL_TEXTURE_STACK_DEPTH)); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); + assertEquals(2, GLStateManager.glGetInteger(GL11.GL_TEXTURE_STACK_DEPTH)); + GLStateManager.glPopMatrix(); + assertEquals(1, GLStateManager.glGetInteger(GL11.GL_TEXTURE_STACK_DEPTH)); + } finally { + GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); + GLStateManager.glMatrixMode(GL11.GL_TEXTURE); + popToBase(GL11.GL_TEXTURE_STACK_DEPTH); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); + } + } + + @Test + void maxModelviewStackDepthIsExactlyReachable() { + try { + GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); + assertEquals(1, GLStateManager.glGetInteger(GL11.GL_MODELVIEW_STACK_DEPTH)); + assertEquals(GLStateManager.glGetInteger(GL11.GL_MAX_MODELVIEW_STACK_DEPTH), pushToLimit(GL11.GL_MODELVIEW_STACK_DEPTH, GL11.GL_MAX_MODELVIEW_STACK_DEPTH)); + } finally { + GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); + popToBase(GL11.GL_MODELVIEW_STACK_DEPTH); + } + } + + @Test + void maxProjectionStackDepthIsExactlyReachable() { + try { + GLStateManager.glMatrixMode(GL11.GL_PROJECTION); + assertEquals(1, GLStateManager.glGetInteger(GL11.GL_PROJECTION_STACK_DEPTH)); + assertEquals(GLStateManager.glGetInteger(GL11.GL_MAX_PROJECTION_STACK_DEPTH), pushToLimit(GL11.GL_PROJECTION_STACK_DEPTH, GL11.GL_MAX_PROJECTION_STACK_DEPTH)); + } finally { + GLStateManager.glMatrixMode(GL11.GL_PROJECTION); + popToBase(GL11.GL_PROJECTION_STACK_DEPTH); + GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); + } + } + + @Test + void maxTextureStackDepthIsExactlyReachable() { + try { + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glMatrixMode(GL11.GL_TEXTURE); + assertEquals(1, GLStateManager.glGetInteger(GL11.GL_TEXTURE_STACK_DEPTH)); + assertEquals(GLStateManager.glGetInteger(GL11.GL_MAX_TEXTURE_STACK_DEPTH), pushToLimit(GL11.GL_TEXTURE_STACK_DEPTH, GL11.GL_MAX_TEXTURE_STACK_DEPTH)); + } finally { + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glMatrixMode(GL11.GL_TEXTURE); + popToBase(GL11.GL_TEXTURE_STACK_DEPTH); + GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); + } + } + + @Test + void constantQueriesReportEmulatedLimits() { + assertEquals(Math.min(VertexKey.MAX_UNITS, GLStateManager.MAX_TEXTURE_UNITS), GLStateManager.glGetInteger(GL13.GL_MAX_TEXTURE_UNITS)); + assertEquals(8, GLStateManager.glGetInteger(GL11.GL_MAX_LIGHTS)); + } + + @Test + void stencilAndDepthBitsMatchDefaultFramebuffer() { + final GLContextState glCtx = GLStateManager.ctx(); + final int savedDraw = glCtx.drawFramebuffer; + final int savedRead = glCtx.readFramebuffer; + try { + GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0); + drainErrors(); + assertEquals( + GL30.glGetFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL11.GL_STENCIL, GL30.GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE), + GLStateManager.glGetInteger(GL11.GL_STENCIL_BITS)); + assertEquals( + GL30.glGetFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL11.GL_DEPTH, GL30.GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE), + GLStateManager.glGetInteger(GL11.GL_DEPTH_BITS)); + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); + } finally { + GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, savedDraw); + GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, savedRead); + } + } + + @Test + void stencilAndDepthBitsFollowBoundDrawFramebuffer() { + final GLContextState glCtx = GLStateManager.ctx(); + final int savedDraw = glCtx.drawFramebuffer; + final int savedRead = glCtx.readFramebuffer; + final int fbo = GLStateManager.glGenFramebuffers(); + final int depthOnly = GLStateManager.glGenRenderbuffers(); + final int depthStencil = GLStateManager.glGenRenderbuffers(); + try { + drainErrors(); + GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depthOnly); + GLStateManager.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL14.GL_DEPTH_COMPONENT24, 16, 16); + GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depthStencil); + GLStateManager.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_DEPTH24_STENCIL8, 16, 16); + GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, 0); + + GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, fbo); + assertEquals(0, GLStateManager.glGetInteger(GL11.GL_STENCIL_BITS)); + assertEquals(0, GLStateManager.glGetInteger(GL11.GL_DEPTH_BITS)); + + GLStateManager.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, GL30.GL_RENDERBUFFER, depthOnly); + assertEquals(0, GLStateManager.glGetInteger(GL11.GL_STENCIL_BITS)); + final int depthOnlyBits = GL30.glGetFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, GL30.GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE); + assertTrue(depthOnlyBits >= 24, "driver depth size " + depthOnlyBits); + assertEquals(depthOnlyBits, GLStateManager.glGetInteger(GL11.GL_DEPTH_BITS)); + + GLStateManager.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, GL30.GL_RENDERBUFFER, depthStencil); + GLStateManager.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, depthStencil); + assertEquals(8, GLStateManager.glGetInteger(GL11.GL_STENCIL_BITS)); + assertEquals(24, GLStateManager.glGetInteger(GL11.GL_DEPTH_BITS)); + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); + } finally { + GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, savedDraw); + GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, savedRead); + GLStateManager.glDeleteFramebuffers(fbo); + GLStateManager.glDeleteRenderbuffers(depthOnly); + GLStateManager.glDeleteRenderbuffers(depthStencil); + } + } + + @Test + void intBufferOverloadMatchesAndDriverIsNotAsked() { + final int base = GLStateManager.getAttribDepth(); + final IntBuffer buf = BufferUtils.createIntBuffer(16); + try { + GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT); + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1); + drainErrors(); + GL11.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH); + assumeTrue(GL11.glGetError() != GL11.GL_NO_ERROR, "driver accepts GL_ATTRIB_STACK_DEPTH, so a clean error state proves nothing"); + for (final int pname : SINGLE_VALUE_QUERIES) { + final int direct = GLStateManager.glGetInteger(pname); + buf.put(0, -12345); + GLStateManager.glGetInteger(pname, buf); + assertEquals(direct, buf.get(0), "IntBuffer overload for 0x" + Integer.toHexString(pname)); + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "driver error after querying 0x" + Integer.toHexString(pname)); + } + } finally { + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + drainClientAttribStack(); + while (GLStateManager.getAttribDepth() > base) { + GLStateManager.glPopAttrib(); + } + } + } + + @Test + void attribDepthUnchangedWhileCompilingDisplayList() { + final int base = GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH); + final int list = GLStateManager.glGenLists(1); + try { + GLStateManager.glNewList(list, GL11.GL_COMPILE); + GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT); + assertEquals(base, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH)); + GLStateManager.glPopAttrib(); + assertEquals(base, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH)); + GLStateManager.glEndList(); + assertEquals(base, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH)); + } finally { + if (DisplayListManager.isRecording()) { + GLStateManager.glEndList(); + } + GLStateManager.glDeleteLists(list, 1); + } + } + + @Test + void popAttribOnEmptyStackIsIgnored() { + drainAttribStack(); + drainErrors(); + assertEquals(0, GLStateManager.getAttribDepth()); + assertDoesNotThrow(GLStateManager::glPopAttrib); + assertEquals(0, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH)); + assertDoesNotThrow(GLStateManager::glPopAttrib); + assertEquals(0, GLStateManager.getAttribDepth()); + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); + } + + @Test + void pixelStoreBitRestoresPixelStoreAndPboBindings() { + final GLContextState glCtx = GLStateManager.ctx(); + final int unpackBuffer = GLStateManager.glGenBuffers(); + final int packBuffer = GLStateManager.glGenBuffers(); + try { + GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4); + GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4); + GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 0); + GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0); + GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0); + final PixelStoreState unpackBefore = glCtx.pixelUnpackState; + final PixelStoreState packBefore = glCtx.pixelPackState; + + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT); + GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); + GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 2); + GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 7); + GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, unpackBuffer); + GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, packBuffer); + assertEquals(1, glCtx.pixelUnpackState.alignment()); + assertEquals(2, glCtx.pixelPackState.alignment()); + assertEquals(7, glCtx.pixelPackState.rowLength()); + assertEquals(2, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT)); + GLStateManager.glPopClientAttrib(); + + assertSame(unpackBefore, glCtx.pixelUnpackState); + assertSame(packBefore, glCtx.pixelPackState); + assertEquals(4, GLStateManager.glGetInteger(GL11.GL_UNPACK_ALIGNMENT)); + assertEquals(0, GLStateManager.glGetInteger(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING)); + assertEquals(0, GLStateManager.glGetInteger(GL21.GL_PIXEL_PACK_BUFFER_BINDING)); + assertEquals(4, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT)); + assertEquals(4, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT)); + assertEquals(0, GL11.glGetInteger(GL11.GL_PACK_ROW_LENGTH)); + assertEquals(0, GL11.glGetInteger(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING)); + assertEquals(0, GL11.glGetInteger(GL21.GL_PIXEL_PACK_BUFFER_BINDING)); + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); + } finally { + drainClientAttribStack(); + GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4); + GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4); + GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 0); + GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0); + GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0); + GLStateManager.glDeleteBuffers(unpackBuffer); + GLStateManager.glDeleteBuffers(packBuffer); + } + } + + @Test + void pixelStoreIsNotRestoredWithoutPixelStoreBit() { + final GLContextState glCtx = GLStateManager.ctx(); + final int unpackBuffer = GLStateManager.glGenBuffers(); + final int packBuffer = GLStateManager.glGenBuffers(); + try { + GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4); + GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4); + GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0); + GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0); + + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); + GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 2); + GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, unpackBuffer); + GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, packBuffer); + GLStateManager.glPopClientAttrib(); + + assertEquals(1, glCtx.pixelUnpackState.alignment()); + assertEquals(2, glCtx.pixelPackState.alignment()); + assertEquals(unpackBuffer, GLStateManager.glGetInteger(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING)); + assertEquals(packBuffer, GLStateManager.glGetInteger(GL21.GL_PIXEL_PACK_BUFFER_BINDING)); + assertEquals(1, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT)); + assertEquals(2, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT)); + assertEquals(unpackBuffer, GL11.glGetInteger(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING)); + assertEquals(packBuffer, GL11.glGetInteger(GL21.GL_PIXEL_PACK_BUFFER_BINDING)); + } finally { + drainClientAttribStack(); + GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4); + GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4); + GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0); + GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0); + GLStateManager.glDeleteBuffers(unpackBuffer); + GLStateManager.glDeleteBuffers(packBuffer); + } + } + + @Test + void pixelStoreWithReturnsSameInstanceForUnchangedValue() { + final GLContextState glCtx = GLStateManager.ctx(); + try { + GLStateManager.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 9); + GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 11); + final PixelStoreState unpack = glCtx.pixelUnpackState; + final PixelStoreState pack = glCtx.pixelPackState; + + GLStateManager.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 9); + GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, unpack.alignment()); + GLStateManager.glPixelStorei(GL11.GL_UNPACK_SWAP_BYTES, 0); + GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 11); + GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, pack.alignment()); + GLStateManager.glPixelStorei(GL11.GL_PACK_LSB_FIRST, 0); + + assertSame(unpack, glCtx.pixelUnpackState); + assertSame(pack, glCtx.pixelPackState); + assertSame(unpack, unpack.with(GL11.GL_UNPACK_ROW_LENGTH, 9)); + assertSame(pack, pack.with(GL11.GL_PACK_ROW_LENGTH, 11)); + } finally { + GLStateManager.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0); + GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 0); + } + } + + @Test + void packAndUnpackRestoreToTheirOwnPriorValues() { + final GLContextState glCtx = GLStateManager.ctx(); + try { + GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2); + GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 8); + GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 3); + GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 5); + + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT); + GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); + GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4); + GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 6); + GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 7); + assertEquals(1, glCtx.pixelUnpackState.alignment()); + assertEquals(4, glCtx.pixelPackState.alignment()); + assertEquals(6, glCtx.pixelUnpackState.skipRows()); + assertEquals(7, glCtx.pixelPackState.skipRows()); + assertEquals(6, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS)); + assertEquals(7, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS)); + GLStateManager.glPopClientAttrib(); + + assertEquals(2, glCtx.pixelUnpackState.alignment()); + assertEquals(8, glCtx.pixelPackState.alignment()); + assertEquals(3, glCtx.pixelUnpackState.skipRows()); + assertEquals(5, glCtx.pixelPackState.skipRows()); + assertEquals(2, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT)); + assertEquals(8, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT)); + assertEquals(3, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS)); + assertEquals(5, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS)); + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); + } finally { + drainClientAttribStack(); + GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4); + GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4); + GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0); + GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 0); + } + } + + @Test + void vertexArrayEnableRestoredOnlyWithVertexArrayBit() { + final int savedVao = GLStateManager.getBoundVAO(); + final int vao = GLStateManager.glGenVertexArrays(); + final IntBuffer enabled = BufferUtils.createIntBuffer(16); + final int position = Usage.POSITION.getAttributeLocation(); + try { + GLStateManager.glBindVertexArray(vao); + drainErrors(); + assertFalse(positionArrayEnabled()); + + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY); + assertTrue(positionArrayEnabled()); + GLStateManager.glPopClientAttrib(); + assertFalse(positionArrayEnabled()); + GL20.glGetVertexAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED, enabled); + assertEquals(GL11.GL_FALSE, enabled.get(0)); + + GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glPopClientAttrib(); + assertTrue(positionArrayEnabled()); + GL20.glGetVertexAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED, enabled); + assertEquals(GL11.GL_TRUE, enabled.get(0)); + GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); + + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT); + GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glPopClientAttrib(); + assertTrue(positionArrayEnabled()); + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); + } finally { + drainClientAttribStack(); + GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glBindVertexArray(savedVao); + GLStateManager.glDeleteVertexArrays(vao); + } + } + + @Test + void vertexArrayStateRestoredOnlyWithVertexArrayBit() { + final int savedVao = GLStateManager.getBoundVAO(); + final int vao = GLStateManager.glGenVertexArrays(); + try { + GLStateManager.glBindVertexArray(vao); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + final int flagsBefore = GLStateManager.ctx().vaos.getVertexFlags(); + assertFalse(colorArrayEnabled()); + + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1); + assertTrue(colorArrayEnabled()); + GLStateManager.glPopClientAttrib(); + assertFalse(colorArrayEnabled()); + assertEquals(flagsBefore, GLStateManager.ctx().vaos.getVertexFlags()); + assertEquals(0, GLStateManager.getClientActiveTextureUnit()); + + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT); + GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1); + GLStateManager.glPopClientAttrib(); + assertTrue(colorArrayEnabled()); + assertEquals(1, GLStateManager.getClientActiveTextureUnit()); + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); + } finally { + drainClientAttribStack(); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY); + GLStateManager.glBindVertexArray(savedVao); + GLStateManager.glDeleteVertexArrays(vao); + } + } + + @Test + void intLightGettersConvertLikeMesa() { + final FloatBuffer savedDiffuse = BufferUtils.createFloatBuffer(16); + final FloatBuffer savedPosition = BufferUtils.createFloatBuffer(16); + final FloatBuffer savedDirection = BufferUtils.createFloatBuffer(16); + final FloatBuffer savedCutoff = BufferUtils.createFloatBuffer(16); + final FloatBuffer savedAttenuation = BufferUtils.createFloatBuffer(16); + GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_DIFFUSE, savedDiffuse); + GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_POSITION, savedPosition); + GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_SPOT_DIRECTION, savedDirection); + GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_SPOT_CUTOFF, savedCutoff); + GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_LINEAR_ATTENUATION, savedAttenuation); + final IntBuffer ints = BufferUtils.createIntBuffer(16); + GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); + GLStateManager.glPushMatrix(); + try { + GLStateManager.glLoadIdentity(); + final FloatBuffer values = BufferUtils.createFloatBuffer(4); + values.put(0, 0.25f).put(1, 0.5f).put(2, 1.0f).put(3, -0.5f); + GLStateManager.glLight(GL11.GL_LIGHT1, GL11.GL_DIFFUSE, values); + values.put(0, 3.9f).put(1, -2.7f).put(2, 0.5f).put(3, 1.0f); + GLStateManager.glLight(GL11.GL_LIGHT1, GL11.GL_POSITION, values); + values.put(0, 2.5f).put(1, -1.5f).put(2, -3.9f).put(3, 0.0f); + GLStateManager.glLight(GL11.GL_LIGHT1, GL11.GL_SPOT_DIRECTION, values); + GLStateManager.glLightf(GL11.GL_LIGHT1, GL11.GL_SPOT_CUTOFF, 45.75f); + GLStateManager.glLightf(GL11.GL_LIGHT1, GL11.GL_LINEAR_ATTENUATION, 2.5f); + drainErrors(); + + ints.position(2); + GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_DIFFUSE, ints); + assertEquals(2, ints.position()); + assertEquals(536870911, ints.get(2)); + assertEquals(1073741823, ints.get(3)); + assertEquals(2147483647, ints.get(4)); + assertEquals(-1073741823, ints.get(5)); + ints.position(0); + + GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_POSITION, ints); + assertEquals(3, ints.get(0)); + assertEquals(-2, ints.get(1)); + assertEquals(0, ints.get(2)); + assertEquals(1, ints.get(3)); + + ints.put(3, -777); + GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_SPOT_DIRECTION, ints); + assertEquals(2, ints.get(0)); + assertEquals(-1, ints.get(1)); + assertEquals(-3, ints.get(2)); + assertEquals(-777, ints.get(3)); + + ints.put(1, -777); + GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_SPOT_CUTOFF, ints); + assertEquals(45, ints.get(0)); + assertEquals(-777, ints.get(1)); + GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_LINEAR_ATTENUATION, ints); + assertEquals(2, ints.get(0)); + + ints.put(0, -777); + GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_EMISSION, ints); + assertEquals(-777, ints.get(0)); + GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_SHININESS, ints); + assertEquals(-777, ints.get(0)); + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); + } finally { + GLStateManager.glLoadIdentity(); + savedDiffuse.limit(4); + GLStateManager.glLight(GL11.GL_LIGHT1, GL11.GL_DIFFUSE, savedDiffuse); + savedPosition.limit(4); + GLStateManager.glLight(GL11.GL_LIGHT1, GL11.GL_POSITION, savedPosition); + savedDirection.limit(4); + GLStateManager.glLight(GL11.GL_LIGHT1, GL11.GL_SPOT_DIRECTION, savedDirection); + GLStateManager.glLightf(GL11.GL_LIGHT1, GL11.GL_SPOT_CUTOFF, savedCutoff.get(0)); + GLStateManager.glLightf(GL11.GL_LIGHT1, GL11.GL_LINEAR_ATTENUATION, savedAttenuation.get(0)); + GLStateManager.glPopMatrix(); + } + } + + @Test + void intMaterialGettersConvertLikeMesa() { + final FloatBuffer savedSpecular = BufferUtils.createFloatBuffer(16); + final FloatBuffer savedShininess = BufferUtils.createFloatBuffer(16); + final FloatBuffer savedIndexes = BufferUtils.createFloatBuffer(16); + GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, savedSpecular); + GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SHININESS, savedShininess); + GLStateManager.glGetMaterial(GL11.GL_BACK, GL11.GL_COLOR_INDEXES, savedIndexes); + final IntBuffer ints = BufferUtils.createIntBuffer(16); + try { + final FloatBuffer values = BufferUtils.createFloatBuffer(4); + values.put(0, 0.5f).put(1, 0.75f).put(2, 1.0f).put(3, 0.0f); + GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, values); + GLStateManager.glMaterialf(GL11.GL_FRONT, GL11.GL_SHININESS, 12.6f); + values.put(0, 2.5f).put(1, -2.5f).put(2, 7.4f).put(3, 0.0f); + GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_COLOR_INDEXES, values); + drainErrors(); + + ints.position(1); + GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, ints); + assertEquals(1, ints.position()); + assertEquals(1073741823, ints.get(1)); + assertEquals(1610612735, ints.get(2)); + assertEquals(2147483647, ints.get(3)); + assertEquals(0, ints.get(4)); + ints.position(0); + + ints.put(1, -777); + GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SHININESS, ints); + assertEquals(13, ints.get(0)); + assertEquals(-777, ints.get(1)); + + ints.put(3, -777); + GLStateManager.glGetMaterial(GL11.GL_BACK, GL11.GL_COLOR_INDEXES, ints); + assertEquals(3, ints.get(0)); + assertEquals(-3, ints.get(1)); + assertEquals(7, ints.get(2)); + assertEquals(-777, ints.get(3)); + + ints.put(0, -777); + GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_POSITION, ints); + assertEquals(-777, ints.get(0)); + GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SPOT_CUTOFF, ints); + assertEquals(-777, ints.get(0)); + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); + } finally { + savedSpecular.limit(4); + GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, savedSpecular); + GLStateManager.glMaterialf(GL11.GL_FRONT, GL11.GL_SHININESS, savedShininess.get(0)); + savedIndexes.limit(4); + GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_COLOR_INDEXES, savedIndexes); + } + } + + @Test + void currentTextureCoordsFollowActiveUnit() { + final FloatBuffer buf = BufferUtils.createFloatBuffer(16); + final float[][] saved = new float[4][4]; + try { + for (int unit = 0; unit < 4; unit++) { + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit); + GLStateManager.glGetFloat(GL11.GL_CURRENT_TEXTURE_COORDS, buf); + for (int i = 0; i < 4; i++) { + saved[unit][i] = buf.get(i); + } + } + GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0, 0.25f, 0.5f); + GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE1, 240.0f, 16.0f); + GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE2, 0.75f, 0.125f); + GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE3, 0.375f, 0.625f); + drainErrors(); + + final float[][] expected = { { 0.25f, 0.5f }, { 240.0f, 16.0f }, { 0.75f, 0.125f }, { 0.375f, 0.625f } }; + for (int unit = 0; unit < 4; unit++) { + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit); + GLStateManager.glGetFloat(GL11.GL_CURRENT_TEXTURE_COORDS, buf); + assertEquals(expected[unit][0], buf.get(0), "s of unit " + unit); + assertEquals(expected[unit][1], buf.get(1), "t of unit " + unit); + assertEquals(0.0f, buf.get(2), "r of unit " + unit); + assertEquals(1.0f, buf.get(3), "q of unit " + unit); + } + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); + } finally { + for (int unit = 0; unit < 4; unit++) { + GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0 + unit, saved[unit][0], saved[unit][1]); + } + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + } + } + + @Test + void currentTextureCoordsOfUntrackedUnitAreInitialValue() { + final FloatBuffer buf = BufferUtils.createFloatBuffer(16); + final int unit = GLStateManager.ctx().ffp.currentTexCoords.length + 1; + try { + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0, 0.25f, 0.5f); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit); + drainErrors(); + for (int i = 0; i < 4; i++) { + buf.put(i, -7.0f); + } + GLStateManager.glGetFloat(GL11.GL_CURRENT_TEXTURE_COORDS, buf); + assertEquals(0.0f, buf.get(0)); + assertEquals(0.0f, buf.get(1)); + assertEquals(0.0f, buf.get(2)); + assertEquals(1.0f, buf.get(3)); + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); + } finally { + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0, 0.0f, 0.0f); + } + } +} diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirectorTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirectorTest.java index 036997861..e0e7d6e29 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirectorTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirectorTest.java @@ -252,6 +252,32 @@ class GLSMRedirectorTest { assertTrue(unmappedSeen().isEmpty()); } + @Test + void arbGenerateMipmapAliasIsRedirected() { + final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/ARBFramebufferObject", "glGenerateMipmap", "(I)V"); + + final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn); + + assertTrue(changed); + assertEquals(GLSM, firstCall(cn).owner); + assertEquals("glGenerateMipmap", firstCall(cn).name); + assertEquals("(I)V", firstCall(cn).desc); + assertTrue(unmappedSeen().isEmpty()); + } + + @Test + void extGenerateMipmapAliasIsRedirected() { + final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/EXTFramebufferObject", "glGenerateMipmapEXT", "(I)V"); + + final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn); + + assertTrue(changed); + assertEquals(GLSM, firstCall(cn).owner); + assertEquals("glGenerateMipmap", firstCall(cn).name); + assertEquals("(I)V", firstCall(cn).desc); + assertTrue(unmappedSeen().isEmpty()); + } + @Test void newlyMappedReadPixelsIsRedirected() { final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/GL11", "glReadPixels", "(IIIIIILjava/nio/IntBuffer;)V"); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/states/PixelStoreStateTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/states/PixelStoreStateTest.java new file mode 100644 index 000000000..2a0a1f796 --- /dev/null +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/states/PixelStoreStateTest.java @@ -0,0 +1,115 @@ +package com.gtnewhorizons.angelica.glsm.states; + +import org.junit.jupiter.api.Test; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class PixelStoreStateTest { + + @Test + void defaults() { + assertTrue(PixelStoreState.DEFAULT.isDefault()); + assertEquals(4, PixelStoreState.DEFAULT.alignment()); + assertEquals(0, PixelStoreState.DEFAULT.rowLength()); + assertFalse(PixelStoreState.DEFAULT.swapBytes()); + assertFalse(PixelStoreState.DEFAULT.lsbFirst()); + } + + @Test + void withUpdatesUnpackParams() { + PixelStoreState s = PixelStoreState.DEFAULT + .with(GL11.GL_UNPACK_ALIGNMENT, 1) + .with(GL11.GL_UNPACK_ROW_LENGTH, 64) + .with(GL11.GL_UNPACK_SKIP_ROWS, 2) + .with(GL11.GL_UNPACK_SKIP_PIXELS, 3) + .with(GL12.GL_UNPACK_IMAGE_HEIGHT, 32) + .with(GL12.GL_UNPACK_SKIP_IMAGES, 1) + .with(GL11.GL_UNPACK_SWAP_BYTES, 1) + .with(GL11.GL_UNPACK_LSB_FIRST, 1); + assertEquals(new PixelStoreState(1, 64, 2, 3, 32, 1, true, true), s); + assertFalse(s.isDefault()); + } + + @Test + void invalidValuesIgnored() { + assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(GL11.GL_UNPACK_ALIGNMENT, 3)); + assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(GL11.GL_UNPACK_ALIGNMENT, 0)); + assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(GL11.GL_UNPACK_ROW_LENGTH, -1)); + assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(GL11.GL_UNPACK_SKIP_ROWS, -5)); + assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(GL12.GL_UNPACK_IMAGE_HEIGHT, -1)); + } + + @Test + void booleansAcceptAnyNonzero() { + assertTrue(PixelStoreState.DEFAULT.with(GL11.GL_UNPACK_SWAP_BYTES, 2).swapBytes()); + assertTrue(PixelStoreState.DEFAULT.with(GL11.GL_UNPACK_LSB_FIRST, -1).lsbFirst()); + PixelStoreState on = PixelStoreState.DEFAULT.with(GL11.GL_UNPACK_SWAP_BYTES, 1); + assertFalse(on.with(GL11.GL_UNPACK_SWAP_BYTES, 0).swapBytes()); + } + + @Test + void packPnamesSetTheSameFields() { + PixelStoreState s = PixelStoreState.DEFAULT + .with(GL11.GL_PACK_ALIGNMENT, 1) + .with(GL11.GL_PACK_ROW_LENGTH, 64) + .with(GL11.GL_PACK_SKIP_ROWS, 2) + .with(GL11.GL_PACK_SKIP_PIXELS, 3) + .with(GL12.GL_PACK_IMAGE_HEIGHT, 32) + .with(GL12.GL_PACK_SKIP_IMAGES, 1) + .with(GL11.GL_PACK_SWAP_BYTES, 1) + .with(GL11.GL_PACK_LSB_FIRST, 1); + assertEquals(new PixelStoreState(1, 64, 2, 3, 32, 1, true, true), s); + assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(GL11.GL_PACK_ALIGNMENT, 3)); + assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(GL11.GL_PACK_ROW_LENGTH, -1)); + } + + @Test + void unknownPnamesIgnored() { + assertSame(PixelStoreState.DEFAULT, PixelStoreState.DEFAULT.with(0x1234, 1)); + } + + @Test + void unchangedValueReturnsSameInstance() { + final PixelStoreState s = new PixelStoreState(1, 64, 2, 3, 32, 1, true, true); + assertSame(s, s.with(GL11.GL_UNPACK_ALIGNMENT, 1)); + assertSame(s, s.with(GL11.GL_PACK_ALIGNMENT, 1)); + assertSame(s, s.with(GL11.GL_UNPACK_ROW_LENGTH, 64)); + assertSame(s, s.with(GL11.GL_PACK_ROW_LENGTH, 64)); + assertSame(s, s.with(GL11.GL_UNPACK_SKIP_ROWS, 2)); + assertSame(s, s.with(GL11.GL_PACK_SKIP_ROWS, 2)); + assertSame(s, s.with(GL11.GL_UNPACK_SKIP_PIXELS, 3)); + assertSame(s, s.with(GL11.GL_PACK_SKIP_PIXELS, 3)); + assertSame(s, s.with(GL12.GL_UNPACK_IMAGE_HEIGHT, 32)); + assertSame(s, s.with(GL12.GL_PACK_IMAGE_HEIGHT, 32)); + assertSame(s, s.with(GL12.GL_UNPACK_SKIP_IMAGES, 1)); + assertSame(s, s.with(GL12.GL_PACK_SKIP_IMAGES, 1)); + assertSame(s, s.with(GL11.GL_UNPACK_SWAP_BYTES, 5)); + assertSame(s, s.with(GL11.GL_PACK_SWAP_BYTES, 5)); + assertSame(s, s.with(GL11.GL_UNPACK_LSB_FIRST, 1)); + assertSame(s, s.with(GL11.GL_PACK_LSB_FIRST, 1)); + } + + @Test + void isPackSeparatesTheFamilies() { + final int[] pack = { + GL11.GL_PACK_ALIGNMENT, GL11.GL_PACK_ROW_LENGTH, GL11.GL_PACK_SKIP_ROWS, GL11.GL_PACK_SKIP_PIXELS, + GL12.GL_PACK_IMAGE_HEIGHT, GL12.GL_PACK_SKIP_IMAGES, GL11.GL_PACK_SWAP_BYTES, GL11.GL_PACK_LSB_FIRST, + }; + final int[] unpack = { + GL11.GL_UNPACK_ALIGNMENT, GL11.GL_UNPACK_ROW_LENGTH, GL11.GL_UNPACK_SKIP_ROWS, GL11.GL_UNPACK_SKIP_PIXELS, + GL12.GL_UNPACK_IMAGE_HEIGHT, GL12.GL_UNPACK_SKIP_IMAGES, GL11.GL_UNPACK_SWAP_BYTES, GL11.GL_UNPACK_LSB_FIRST, + }; + for (final int pname : pack) { + assertTrue(PixelStoreState.isPack(pname)); + } + for (final int pname : unpack) { + assertFalse(PixelStoreState.isPack(pname)); + } + assertFalse(PixelStoreState.isPack(0x1234)); + } +} diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/states/PixelUnpackStateTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/states/PixelUnpackStateTest.java deleted file mode 100644 index 05d0f6d8b..000000000 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/states/PixelUnpackStateTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.gtnewhorizons.angelica.glsm.states; - -import org.junit.jupiter.api.Test; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL12; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; - -class PixelUnpackStateTest { - - @Test - void defaults() { - assertTrue(PixelUnpackState.DEFAULT.isDefault()); - assertEquals(4, PixelUnpackState.DEFAULT.alignment()); - assertEquals(0, PixelUnpackState.DEFAULT.rowLength()); - assertFalse(PixelUnpackState.DEFAULT.swapBytes()); - assertFalse(PixelUnpackState.DEFAULT.lsbFirst()); - } - - @Test - void withUpdatesUnpackParams() { - PixelUnpackState s = PixelUnpackState.DEFAULT - .with(GL11.GL_UNPACK_ALIGNMENT, 1) - .with(GL11.GL_UNPACK_ROW_LENGTH, 64) - .with(GL11.GL_UNPACK_SKIP_ROWS, 2) - .with(GL11.GL_UNPACK_SKIP_PIXELS, 3) - .with(GL12.GL_UNPACK_IMAGE_HEIGHT, 32) - .with(GL12.GL_UNPACK_SKIP_IMAGES, 1) - .with(GL11.GL_UNPACK_SWAP_BYTES, 1) - .with(GL11.GL_UNPACK_LSB_FIRST, 1); - assertEquals(new PixelUnpackState(1, 64, 2, 3, 32, 1, true, true), s); - assertFalse(s.isDefault()); - } - - @Test - void invalidValuesIgnored() { - assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_ALIGNMENT, 3)); - assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_ALIGNMENT, 0)); - assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_ROW_LENGTH, -1)); - assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_SKIP_ROWS, -5)); - assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL12.GL_UNPACK_IMAGE_HEIGHT, -1)); - } - - @Test - void booleansAcceptAnyNonzero() { - assertTrue(PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_SWAP_BYTES, 2).swapBytes()); - assertTrue(PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_LSB_FIRST, -1).lsbFirst()); - PixelUnpackState on = PixelUnpackState.DEFAULT.with(GL11.GL_UNPACK_SWAP_BYTES, 1); - assertFalse(on.with(GL11.GL_UNPACK_SWAP_BYTES, 0).swapBytes()); - } - - @Test - void packAndUnknownPnamesIgnored() { - assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_PACK_ALIGNMENT, 1)); - assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(GL11.GL_PACK_ROW_LENGTH, 16)); - assertSame(PixelUnpackState.DEFAULT, PixelUnpackState.DEFAULT.with(0x1234, 1)); - } -} diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLWJGLServiceRoutingTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLWJGLServiceRoutingTest.java index 41a709509..2054525d1 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLWJGLServiceRoutingTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLWJGLServiceRoutingTest.java @@ -1,7 +1,7 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import com.gtnewhorizons.angelica.glsm.states.PixelUnpackState; +import com.gtnewhorizons.angelica.glsm.states.PixelStoreState; import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import com.gtnewhorizons.angelica.sdlgpu.SDLGPULWJGLService; import org.junit.jupiter.api.BeforeAll; @@ -35,7 +35,7 @@ class GlsmSdlLWJGLServiceRoutingTest { } private static int glsmUnpackAlignment() { - final PixelUnpackState state = Reflect.get(glsmContext(), "pixelUnpackState"); + final PixelStoreState state = Reflect.get(glsmContext(), "pixelUnpackState"); return state.alignment(); }