Fix HUD caching flicker and match non-cached GL state (#2201)

This commit is contained in:
Jason Mitchell authored and GitHub committed 2026-10-03 13:19:29 -07:00
1 parent fe82cd320a
commit 7cd97d1216
33 files changed
+801 -175

No files matched your search

@@ -21,6 +21,7 @@ import com.gtnewhorizons.angelica.glsm.stacks.MaterialStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.MatrixModeStack;
import com.gtnewhorizons.angelica.glsm.stacks.PointStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.PolygonStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.RetainedState;
import com.gtnewhorizons.angelica.glsm.stacks.ScissorStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.StackIdAllocator;
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
@@ -106,6 +107,7 @@ public class GLContextState {
public int maxBoundTextureUnit = 0;
public int maxBoundImageUnit = 0;
public final StateSet[] attribSets = new StateSet[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
public RetainedState retainedOwner;
public int attribDepth = 0;
public boolean savesState(int id) {
@@ -49,6 +49,7 @@ import com.gtnewhorizons.angelica.glsm.stacks.MaterialStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.MatrixModeStack;
import com.gtnewhorizons.angelica.glsm.stacks.PointStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.PolygonStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.RetainedState;
import com.gtnewhorizons.angelica.glsm.stacks.ScissorStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.StackIdAllocator;
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
@@ -313,6 +314,8 @@ public class GLStateManager {
// Software stack depths for FFP emulation
public static final int MAX_ATTRIB_STACK_DEPTH = 32;
public static final int RETAINED_SLOT = MAX_ATTRIB_STACK_DEPTH;
public static final int STATE_SLOTS = MAX_ATTRIB_STACK_DEPTH + 1;
public static final int MAX_MODELVIEW_STACK_DEPTH = 32 + 2;
public static final int MAX_PROJECTION_STACK_DEPTH = 4;
public static final int MAX_TEXTURE_STACK_DEPTH = 4;
@@ -542,6 +545,7 @@ public class GLStateManager {
final GLContextState glCtx = ctx();
DisplayListManager.abortCompilation();
glCtx.attribDepth = 0;
glCtx.retainedOwner = null;
for (int i = 0; i < glCtx.attribSets.length; i++) {
glCtx.attribSets[i] = null;
}
@@ -1604,58 +1608,25 @@ public class GLStateManager {
}
public static void glBlendFunc(int srcFactor, int dstFactor) {
final GLContextState glCtx = ctx();
final RecordMode mode = DisplayListManager.getRecordMode();
if (mode != RecordMode.NONE) {
DisplayListManager.recordBlendFunc(srcFactor, dstFactor, srcFactor, dstFactor);
if (mode == RecordMode.COMPILE) {
return;
}
}
final boolean snapshotted = snapshotVanillaBlendFunc();
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
if (bh != null && bh.isBlendLocked()) {
mod(glCtx.blendState);
bh.deferBlendFunc(srcFactor, dstFactor, srcFactor, dstFactor);
postVanillaBlendChangeIfMoved(snapshotted);
return;
}
final boolean caching = isCachingEnabled();
tryBlendFuncSeparate(srcFactor, dstFactor, srcFactor, dstFactor);
}
private static void issueBlendFunc(BlendStateStack blend) {
if (GLSMConfig.hudCacheOverride) {
mod(glCtx.blendState);
glCtx.blendState.setAll(srcFactor, dstFactor, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
glCtx.blendState.clearFuncUnknownState();
RENDER_BACKEND.blendFuncSeparate(srcFactor, dstFactor, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
if (GLSMHooks.BLEND_FUNC_CHANGE.hasListeners()) {
GLSMHooks.blendFuncChangeEvent.srcRgb = srcFactor;
GLSMHooks.blendFuncChangeEvent.dstRgb = dstFactor;
GLSMHooks.blendFuncChangeEvent.srcAlpha = GL11.GL_ONE;
GLSMHooks.blendFuncChangeEvent.dstAlpha = GL11.GL_ONE_MINUS_SRC_ALPHA;
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
}
postVanillaBlendChangeIfMoved(snapshotted);
return;
RENDER_BACKEND.blendFuncSeparate(blend.getSrcRgb(), blend.getDstRgb(), GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
} else {
RENDER_BACKEND.blendFuncSeparate(blend.getSrcRgb(), blend.getDstRgb(), blend.getSrcAlpha(), blend.getDstAlpha());
}
final boolean bypass = !caching;
if (bypass
|| glCtx.blendState.getSrcRgb() != srcFactor
|| glCtx.blendState.getDstRgb() != dstFactor
|| glCtx.blendState.getSrcAlpha() != srcFactor
|| glCtx.blendState.getDstAlpha() != dstFactor
|| glCtx.blendState.isFuncUnknown()) {
mod(glCtx.blendState);
glCtx.blendState.setAll(srcFactor, dstFactor, srcFactor, dstFactor);
glCtx.blendState.clearFuncUnknownState();
RENDER_BACKEND.blendFunc(srcFactor, dstFactor);
if (GLSMHooks.BLEND_FUNC_CHANGE.hasListeners()) {
GLSMHooks.blendFuncChangeEvent.srcRgb = srcFactor;
GLSMHooks.blendFuncChangeEvent.dstRgb = dstFactor;
GLSMHooks.blendFuncChangeEvent.srcAlpha = srcFactor;
GLSMHooks.blendFuncChangeEvent.dstAlpha = dstFactor;
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
}
}
postVanillaBlendChangeIfMoved(snapshotted);
}
public static void setHudCacheOverride(boolean enabled) {
if (GLSMConfig.hudCacheOverride == enabled) return;
GLSMConfig.hudCacheOverride = enabled;
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
if (bh != null && bh.isOverrideHeld()) return;
final BlendStateStack blend = ctx().blendState;
issueBlendFunc(blend);
blend.clearFuncUnknownState();
}
public static void glBlendEquation(int mode) {
@@ -1707,22 +1678,19 @@ public class GLStateManager {
postVanillaBlendChangeIfMoved(snapshotted);
return;
}
if (GLSMConfig.hudCacheOverride && dstAlpha != GL11.GL_ONE_MINUS_SRC_ALPHA) {
srcAlpha = GL11.GL_ONE;
dstAlpha = GL11.GL_ONE_MINUS_SRC_ALPHA;
}
final boolean caching = isCachingEnabled();
final boolean bypass = !caching;
if (bypass || glCtx.blendState.getSrcRgb() != srcRgb || glCtx.blendState.getDstRgb() != dstRgb || glCtx.blendState.getSrcAlpha() != srcAlpha || glCtx.blendState.getDstAlpha() != dstAlpha || glCtx.blendState.isFuncUnknown()) {
mod(glCtx.blendState);
glCtx.blendState.setAll(srcRgb, dstRgb, srcAlpha, dstAlpha);
glCtx.blendState.clearFuncUnknownState();
RENDER_BACKEND.blendFuncSeparate(srcRgb, dstRgb, srcAlpha, dstAlpha);
issueBlendFunc(glCtx.blendState);
if (GLSMHooks.BLEND_FUNC_CHANGE.hasListeners()) {
final boolean hudCache = GLSMConfig.hudCacheOverride;
GLSMHooks.blendFuncChangeEvent.srcRgb = srcRgb;
GLSMHooks.blendFuncChangeEvent.dstRgb = dstRgb;
GLSMHooks.blendFuncChangeEvent.srcAlpha = srcAlpha;
GLSMHooks.blendFuncChangeEvent.dstAlpha = dstAlpha;
GLSMHooks.blendFuncChangeEvent.srcAlpha = hudCache ? GL11.GL_ONE : srcAlpha;
GLSMHooks.blendFuncChangeEvent.dstAlpha = hudCache ? GL11.GL_ONE_MINUS_SRC_ALPHA : dstAlpha;
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
}
}
@@ -4177,6 +4145,89 @@ public class GLStateManager {
}
}
public static void retainModifiedState(int depth, RetainedState out) {
final GLContextState glCtx = ctx();
if (depth < 0 || depth != glCtx.attribDepth - 1) {
throw new IllegalStateException("retainModifiedState: depth " + depth + " is not the open push (attrib depth " + glCtx.attribDepth + ")");
}
glCtx.retainedOwner = out;
final int count = glCtx.modifiedCount[depth];
final int[] ids = glCtx.modifiedIds[depth];
final CowStateStack<?>[] stackById = glCtx.stackById;
out.ensureCapacity(count);
for (int i = 0; i < count; i++) {
final int id = ids[i];
stackById[id].captureSlot(RETAINED_SLOT);
out.ids[i] = id;
}
out.count = count;
out.program = glCtx.activeProgram;
out.programGen = programLifetimeGeneration;
}
public static void applyRetainedState(RetainedState in) {
final GLContextState glCtx = ctx();
if (glCtx.retainedOwner != in) return;
final CowStateStack<?>[] stackById = glCtx.stackById;
final byte[] kindById = glCtx.kindById;
final int[] ids = in.ids;
final int count = in.count;
int first = 0;
while (first < count && !CowDispatch.slotChanged(kindById[ids[first]], stackById[ids[first]], RETAINED_SLOT)) first++;
if (first < count) applyRetainedChanges(glCtx, in, first);
if (in.program != glCtx.activeProgram && (in.program == 0 || in.programGen == programLifetimeGeneration)) glUseProgram(in.program);
}
private static void applyRetainedChanges(GLContextState glCtx, RetainedState in, int first) {
beforeUncapturedStateChange();
final boolean blendSnapshotted = snapshotVanillaBlendFunc();
final int alphaFuncId = glCtx.alphaState.stackId();
final int alphaTestId = glCtx.alphaTest.stackId();
boolean alphaChanged = false;
final boolean prevPoppingAttributes = glCtx.poppingAttributes;
glCtx.poppingAttributes = true;
try {
final CowStateStack<?>[] stackById = glCtx.stackById;
final int[] restoreBitById = glCtx.restoreBitById;
final int[] restoreUnitById = glCtx.restoreUnitById;
final byte[] kindById = glCtx.kindById;
int changed = 0;
long unitsChanged = 0L;
for (int i = first; i < in.count; i++) {
final int id = in.ids[i];
final CowStateStack<?> cow = stackById[id];
final byte kind = kindById[id];
if (!CowDispatch.slotChanged(kind, cow, RETAINED_SLOT)) continue;
cow.beforeModify();
final int bit = restoreBitById[id];
if (bit != 0) {
changed |= bit;
final int unit = restoreUnitById[id];
if (unit >= 0) unitsChanged |= 1L << Math.min(unit, 63);
}
CowDispatch.restoreSlot(kind, cow, RETAINED_SLOT);
if (id == alphaFuncId || id == alphaTestId) alphaChanged = true;
}
glCtx.restoreChangedMask = changed;
glCtx.restoreUnitChangedMask = unitsChanged;
issueRestoredBackendState(glCtx, -1);
glCtx.lightingGeneration++;
glCtx.fragmentGeneration++;
glCtx.colorGeneration++;
glCtx.ffp.normalGeneration++;
glCtx.ffp.texCoordGeneration++;
glCtx.dirtyColorAttrib = true;
glCtx.dirtyNormalAttrib = true;
glCtx.dirtyTexCoordAttrib = true;
postVanillaBlendChangeIfMoved(blendSnapshotted);
} finally {
glCtx.poppingAttributes = prevPoppingAttributes;
}
if (alphaChanged && GLSMHooks.ALPHA_STATE_CHANGE.hasListeners()) {
GLSMHooks.ALPHA_STATE_CHANGE.post(GLSMHooks.alphaStateChangeEvent);
}
}
private static boolean isDepthColorOverridden() {
final DeferredDepthColorHandler dch = GLSMHooks.depthColorHandler;
return dch != null && dch.isOverrideHeld();
@@ -4195,7 +4246,7 @@ public class GLStateManager {
RENDER_BACKEND.depthMask(glCtx.depthState.isEnabled());
RENDER_BACKEND.clearDepth(glCtx.depthState.getClearValue());
RENDER_BACKEND.blendFuncSeparate(glCtx.blendState.getSrcRgb(), glCtx.blendState.getDstRgb(), glCtx.blendState.getSrcAlpha(), glCtx.blendState.getDstAlpha());
issueBlendFunc(glCtx.blendState);
glCtx.blendState.clearFuncUnknownState();
RENDER_BACKEND.blendEquationSeparate(glCtx.blendState.getEquationRgb(), glCtx.blendState.getEquationAlpha());
RENDER_BACKEND.blendColor(glCtx.blendState.getBlendColorR(), glCtx.blendState.getBlendColorG(), glCtx.blendState.getBlendColorB(), glCtx.blendState.getBlendColorA());
@@ -4256,12 +4307,7 @@ public class GLStateManager {
}
}
/**
* After popping GLSM stacks, apply restored state to the GL driver.
*/
private static void applyRestoredState(StateSet set, boolean blendSnapshotted) {
final GLContextState glCtx = ctx();
final int restore = set.restore;
private static void issueRestoredBackendState(GLContextState glCtx, int restore) {
final int changed = glCtx.restoreChangedMask;
if ((restore & StateSet.R_DEPTH) != 0 && (changed & StateSet.R_DEPTH) != 0) {
attribValueRestores++;
@@ -4280,7 +4326,7 @@ public class GLStateManager {
attribValueRestores++;
final DeferredBlendHandler restoreBlendHandler = GLSMHooks.blendHandler;
if (restoreBlendHandler == null || !restoreBlendHandler.isOverrideHeld()) {
RENDER_BACKEND.blendFuncSeparate(blend.getSrcRgb(), blend.getDstRgb(), blend.getSrcAlpha(), blend.getDstAlpha());
issueBlendFunc(blend);
attribBackendCalls++;
blend.clearFuncUnknownState();
}
@@ -4377,6 +4423,16 @@ public class GLStateManager {
attribBackendCalls++;
}
}
}
/**
* After popping GLSM stacks, apply restored state to the GL driver.
*/
private static void applyRestoredState(StateSet set, boolean blendSnapshotted) {
final GLContextState glCtx = ctx();
final int restore = set.restore;
final int changed = glCtx.restoreChangedMask;
issueRestoredBackendState(glCtx, restore);
final int bump = set.bump;
final int depth = glCtx.attribDepth;
@@ -14,8 +14,8 @@ public final class AlphaStateStack extends AlphaState implements CowStateStack<A
public AlphaStateStack(int id) {
cow.id = id;
stack = new AlphaState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new AlphaState[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new AlphaState();
}
}
@@ -25,6 +25,11 @@ public final class AlphaStateStack extends AlphaState implements CowStateStack<A
return cow;
}
@Override
public boolean slotChanged(int slot) {
return VanillaStateLayer.isHeld(vanillaLayer) || !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
VanillaStateLayer.capture(vanillaLayer, stack[s].set(this));
@@ -17,8 +17,8 @@ public final class BlendStateStack extends BlendState implements CowStateStack<B
public BlendStateStack(int id) {
cow.id = id;
stack = new BlendState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new BlendState[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new BlendState();
}
}
@@ -28,6 +28,11 @@ public final class BlendStateStack extends BlendState implements CowStateStack<B
return cow;
}
@Override
public boolean slotChanged(int slot) {
return funcUnknown || VanillaStateLayer.isHeld(vanillaLayer) || !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
VanillaStateLayer.capture(vanillaLayer, stack[s].set(this));
@@ -44,7 +44,7 @@ public class BooleanStateStack extends BooleanState implements CowStateStack<Boo
public BooleanStateStack(int glCap, boolean initialState, boolean ffpStateOnly) {
super(glCap, ffpStateOnly);
this.enabled = initialState;
stack = new boolean[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
stack = new boolean[GLStateManager.STATE_SLOTS];
}
@Override
@@ -52,6 +52,11 @@ public class BooleanStateStack extends BooleanState implements CowStateStack<Boo
return cow;
}
@Override
public boolean slotChanged(int slot) {
return stateUnknown || (vanillaLayer != null && vanillaLayer.isOverrideHeld()) || enabled != stack[slot];
}
@Override
public void captureSlot(int slot) {
stack[slot] = vanillaValue();
@@ -10,8 +10,8 @@ public final class Color4Stack extends Color4 implements CowStateStack<Color4Sta
public Color4Stack(int id) {
cow.id = id;
stack = new Color4[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new Color4[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new Color4();
}
}
@@ -33,6 +33,11 @@ public final class Color4Stack extends Color4 implements CowStateStack<Color4Sta
return cow;
}
@Override
public boolean slotChanged(int slot) {
return !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
stack[s].set(this);
@@ -15,8 +15,8 @@ public final class ColorMaskStack extends ColorMask implements CowStateStack<Col
public ColorMaskStack(int id) {
cow.id = id;
stack = new ColorMask[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new ColorMask[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new ColorMask();
}
}
@@ -26,6 +26,11 @@ public final class ColorMaskStack extends ColorMask implements CowStateStack<Col
return cow;
}
@Override
public boolean slotChanged(int slot) {
return VanillaStateLayer.isHeld(vanillaLayer) || !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
VanillaStateLayer.capture(vanillaLayer, stack[s].set(this));
@@ -101,6 +101,36 @@ public final class CowDispatch {
};
}
public static boolean slotChanged(byte kind, CowStateStack<?> s, int slot) {
return switch (kind) {
case BLEND -> ((BlendStateStack) s).slotChanged(slot);
case BOOLEAN -> ((BooleanStateStack) s).slotChanged(slot);
case CLIP_PLANE_BOOLEAN -> ((ClipPlaneBooleanState) s).slotChanged(slot);
case TEXTURE_UNIT_BOOLEAN -> ((TextureUnitBooleanStateStack) s).slotChanged(slot);
case COLOR_MASK -> ((ColorMaskStack) s).slotChanged(slot);
case LIGHT_STATE -> ((LightStateStack) s).slotChanged(slot);
case MATERIAL -> ((MaterialStateStack) s).slotChanged(slot);
case STENCIL -> ((StencilStateStack) s).slotChanged(slot);
case MATRIX_MODE -> ((MatrixModeStack) s).slotChanged(slot);
case LIGHT_MODEL -> ((LightModelStateStack) s).slotChanged(slot);
case ALPHA -> ((AlphaStateStack) s).slotChanged(slot);
case POINT -> ((PointStateStack) s).slotChanged(slot);
case FOG -> ((FogStateStack) s).slotChanged(slot);
case VEC4F -> ((Vec4fStack) s).slotChanged(slot);
case VEC3F -> ((Vec3fStack) s).slotChanged(slot);
case INTEGER -> ((IntegerStateStack) s).slotChanged(slot);
case DEPTH -> ((DepthStateStack) s).slotChanged(slot);
case VIEWPORT -> ((ViewPortStateStack) s).slotChanged(slot);
case POLYGON -> ((PolygonStateStack) s).slotChanged(slot);
case COLOR4 -> ((Color4Stack) s).slotChanged(slot);
case LINE -> ((LineStateStack) s).slotChanged(slot);
case TEXTURE_BINDING -> ((TextureBindingStack) s).slotChanged(slot);
case TEX_ENV -> ((TexEnvState) s).slotChanged(slot);
case SCISSOR -> ((ScissorStateStack) s).slotChanged(slot);
default -> throw new IllegalStateException("Unknown kind: " + kind);
};
}
public static void restoreSlot(byte kind, CowStateStack<?> s, int slot) {
switch (kind) {
case BLEND -> ((BlendStateStack) s).restoreSlot(slot);
@@ -20,6 +20,8 @@ public interface CowStateStack<T> extends IStateStack<T> {
void restoreSlot(int slot);
boolean slotChanged(int slot);
default int stackId() {
return cowDepths().id;
}
@@ -17,8 +17,8 @@ public final class DepthStateStack extends DepthState implements CowStateStack<D
public DepthStateStack(int id) {
cow.id = id;
stack = new DepthState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new DepthState[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new DepthState();
}
}
@@ -28,6 +28,11 @@ public final class DepthStateStack extends DepthState implements CowStateStack<D
return cow;
}
@Override
public boolean slotChanged(int slot) {
return VanillaStateLayer.isHeld(vanillaLayer) || !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
VanillaStateLayer.capture(vanillaLayer, stack[s].set(this));
@@ -10,8 +10,8 @@ public final class FogStateStack extends FogState implements CowStateStack<FogSt
public FogStateStack(int id) {
cow.id = id;
stack = new FogState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new FogState[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new FogState();
}
}
@@ -21,6 +21,11 @@ public final class FogStateStack extends FogState implements CowStateStack<FogSt
return cow;
}
@Override
public boolean slotChanged(int slot) {
return !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
stack[s].set(this);
@@ -11,8 +11,8 @@ public final class IntegerStateStack extends IntegerState implements CowStateSta
public IntegerStateStack(int val, int id) {
cow.id = id;
setValue(val);
stack = new IntegerState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new IntegerState[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new IntegerState();
stack[i].setValue(val);
}
@@ -30,6 +30,11 @@ public final class IntegerStateStack extends IntegerState implements CowStateSta
return cow;
}
@Override
public boolean slotChanged(int slot) {
return !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
stack[s].set(this);
@@ -11,8 +11,8 @@ public final class LightModelStateStack extends LightModelState implements CowSt
public LightModelStateStack(int id) {
super();
cow.id = id;
stack = new LightModelState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new LightModelState[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new LightModelState();
}
}
@@ -22,6 +22,11 @@ public final class LightModelStateStack extends LightModelState implements CowSt
return cow;
}
@Override
public boolean slotChanged(int slot) {
return !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
stack[s].set(this);
@@ -11,8 +11,8 @@ public final class LightStateStack extends LightState implements CowStateStack<L
public LightStateStack(int light, int id) {
super(light);
cow.id = id;
stack = new LightState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new LightState[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new LightState(light);
}
}
@@ -22,6 +22,11 @@ public final class LightStateStack extends LightState implements CowStateStack<L
return cow;
}
@Override
public boolean slotChanged(int slot) {
return !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
stack[s].set(this);
@@ -11,8 +11,8 @@ public final class LineStateStack extends LineState implements CowStateStack<Lin
public LineStateStack(int id) {
cow.id = id;
stack = new LineState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new LineState[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new LineState();
}
}
@@ -22,6 +22,11 @@ public final class LineStateStack extends LineState implements CowStateStack<Lin
return cow;
}
@Override
public boolean slotChanged(int slot) {
return !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
stack[s].set(this);
@@ -11,8 +11,8 @@ public final class MaterialStateStack extends MaterialState implements CowStateS
public MaterialStateStack(int face, int id) {
super(face);
cow.id = id;
stack = new MaterialState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new MaterialState[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new MaterialState(face);
}
}
@@ -22,6 +22,11 @@ public final class MaterialStateStack extends MaterialState implements CowStateS
return cow;
}
@Override
public boolean slotChanged(int slot) {
return !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
stack[s].set(this);
@@ -10,8 +10,8 @@ public final class MatrixModeStack extends MatrixMode implements CowStateStack<M
public MatrixModeStack(int id) {
cow.id = id;
stack = new MatrixMode[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new MatrixMode[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new MatrixMode();
}
}
@@ -21,6 +21,11 @@ public final class MatrixModeStack extends MatrixMode implements CowStateStack<M
return cow;
}
@Override
public boolean slotChanged(int slot) {
return !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
stack[s].set(this);
@@ -11,8 +11,8 @@ public final class PointStateStack extends PointState implements CowStateStack<P
public PointStateStack(int id) {
cow.id = id;
stack = new PointState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new PointState[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new PointState();
}
}
@@ -22,6 +22,11 @@ public final class PointStateStack extends PointState implements CowStateStack<P
return cow;
}
@Override
public boolean slotChanged(int slot) {
return !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
stack[s].set(this);
@@ -11,8 +11,8 @@ public final class PolygonStateStack extends PolygonState implements CowStateSta
public PolygonStateStack(int id) {
cow.id = id;
stack = new PolygonState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new PolygonState[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new PolygonState();
}
}
@@ -22,6 +22,11 @@ public final class PolygonStateStack extends PolygonState implements CowStateSta
return cow;
}
@Override
public boolean slotChanged(int slot) {
return !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
stack[s].set(this);
@@ -0,0 +1,12 @@
package com.gtnewhorizons.angelica.glsm.stacks;
public final class RetainedState {
public int[] ids = new int[64];
public int count;
public int program;
public long programGen;
public void ensureCapacity(int capacity) {
if (ids.length < capacity) ids = new int[capacity];
}
}
@@ -11,8 +11,8 @@ public final class ScissorStateStack extends ScissorState implements CowStateSta
public ScissorStateStack(int id) {
cow.id = id;
stack = new ScissorState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new ScissorState[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new ScissorState();
}
}
@@ -22,6 +22,11 @@ public final class ScissorStateStack extends ScissorState implements CowStateSta
return cow;
}
@Override
public boolean slotChanged(int slot) {
return !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
stack[s].set(this);
@@ -11,8 +11,8 @@ public final class StencilStateStack extends StencilState implements CowStateSta
public StencilStateStack(int id) {
cow.id = id;
stack = new StencilState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new StencilState[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new StencilState();
}
}
@@ -22,6 +22,11 @@ public final class StencilStateStack extends StencilState implements CowStateSta
return cow;
}
@Override
public boolean slotChanged(int slot) {
return !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
stack[s].set(this);
@@ -12,8 +12,8 @@ public final class TextureBindingStack extends TextureBinding implements CowStat
public TextureBindingStack(int id, int unit) {
cow = new CowDepths(StateSet.R_TEXTURE, unit);
cow.id = id;
stack = new TextureBinding[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new TextureBinding[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new TextureBinding();
}
}
@@ -23,6 +23,11 @@ public final class TextureBindingStack extends TextureBinding implements CowStat
return cow;
}
@Override
public boolean slotChanged(int slot) {
return !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
stack[s].set(this);
@@ -15,7 +15,7 @@ public final class Vec3fStack implements CowStateStack<Vec3fStack> {
public Vec3fStack(Vector3f value, int id) {
this.value = value;
cow.id = id;
stack = new float[GLStateManager.MAX_ATTRIB_STACK_DEPTH][3];
stack = new float[GLStateManager.STATE_SLOTS][3];
}
@Override
@@ -23,6 +23,11 @@ public final class Vec3fStack implements CowStateStack<Vec3fStack> {
return cow;
}
@Override
public boolean slotChanged(int slot) {
return value.x != stack[slot][0] || value.y != stack[slot][1] || value.z != stack[slot][2];
}
@Override
public void captureSlot(int s) {
stack[s][0] = value.x;
@@ -15,7 +15,7 @@ public final class Vec4fStack implements CowStateStack<Vec4fStack> {
public Vec4fStack(Vector4f value, int id) {
this.value = value;
cow.id = id;
stack = new float[GLStateManager.MAX_ATTRIB_STACK_DEPTH][4];
stack = new float[GLStateManager.STATE_SLOTS][4];
}
@Override
@@ -23,6 +23,11 @@ public final class Vec4fStack implements CowStateStack<Vec4fStack> {
return cow;
}
@Override
public boolean slotChanged(int slot) {
return value.x != stack[slot][0] || value.y != stack[slot][1] || value.z != stack[slot][2] || value.w != stack[slot][3];
}
@Override
public void captureSlot(int s) {
stack[s][0] = value.x;
@@ -11,8 +11,8 @@ public final class ViewPortStateStack extends ViewportState implements CowStateS
public ViewPortStateStack(int id) {
cow.id = id;
stack = new ViewportState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new ViewportState[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new ViewportState();
}
}
@@ -22,6 +22,11 @@ public final class ViewPortStateStack extends ViewportState implements CowStateS
return cow;
}
@Override
public boolean slotChanged(int slot) {
return !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
stack[s].set(this);
@@ -6,6 +6,8 @@ import com.gtnewhorizons.angelica.glsm.stacks.CowStateStack;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import java.util.Arrays;
/**
* Per-texture-unit GL_TEXTURE_ENV state for FFP emulation. Tracks the simple texenv mode and all GL_COMBINE sub-parameters.
*/
@@ -45,8 +47,8 @@ public final class TexEnvState implements CowStateStack<TexEnvState> {
stack = null;
cow = null;
} else {
stack = new TexEnvState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack = new TexEnvState[GLStateManager.STATE_SLOTS];
for (int i = 0; i < GLStateManager.STATE_SLOTS; i++) {
stack[i] = new TexEnvState(true);
}
cow = new CowDepths();
@@ -70,6 +72,14 @@ public final class TexEnvState implements CowStateStack<TexEnvState> {
this.envColorA = other.envColorA;
}
private boolean sameAs(TexEnvState other) {
return mode == other.mode && combineRgb == other.combineRgb && combineAlpha == other.combineAlpha
&& Arrays.equals(sourceRgb, other.sourceRgb) && Arrays.equals(sourceAlpha, other.sourceAlpha)
&& Arrays.equals(operandRgb, other.operandRgb) && Arrays.equals(operandAlpha, other.operandAlpha)
&& scaleRgb == other.scaleRgb && scaleAlpha == other.scaleAlpha
&& envColorR == other.envColorR && envColorG == other.envColorG && envColorB == other.envColorB && envColorA == other.envColorA;
}
public void reset() {
mode = GL11.GL_MODULATE;
combineRgb = GL11.GL_MODULATE;
@@ -89,6 +99,11 @@ public final class TexEnvState implements CowStateStack<TexEnvState> {
return cow;
}
@Override
public boolean slotChanged(int slot) {
return !sameAs(stack[slot]);
}
@Override
public void captureSlot(int s) {
stack[s].copyFrom(this);
@@ -0,0 +1,83 @@
package com.gtnewhorizons.angelica.glsm;
import org.junit.jupiter.api.Test;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL14;
import static org.junit.jupiter.api.Assertions.assertEquals;
@GLCoreTest
public class GLSM_HudCacheBlendOverride_GLTest {
private static void assertDriverFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
assertEquals(srcRgb, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB));
assertEquals(dstRgb, GL11.glGetInteger(GL14.GL_BLEND_DST_RGB));
assertEquals(srcAlpha, GL11.glGetInteger(GL14.GL_BLEND_SRC_ALPHA));
assertEquals(dstAlpha, GL11.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
}
private static void assertCachedFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
assertEquals(srcRgb, GLStateManager.glGetInteger(GL14.GL_BLEND_SRC_RGB));
assertEquals(dstRgb, GLStateManager.glGetInteger(GL14.GL_BLEND_DST_RGB));
assertEquals(srcAlpha, GLStateManager.glGetInteger(GL14.GL_BLEND_SRC_ALPHA));
assertEquals(dstAlpha, GLStateManager.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
}
@Test
void overrideChangesTheDriverFuncButNotTheCache() {
try {
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
GLStateManager.setHudCacheOverride(true);
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_SRC_ALPHA, GL11.GL_ONE);
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
assertCachedFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
assertDriverFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
GLStateManager.setHudCacheOverride(false);
assertCachedFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
assertDriverFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ONE);
} finally {
GLStateManager.setHudCacheOverride(false);
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
}
}
@Test
void popUnderOverrideKeepsTheOverriddenAlphaOnTheDriver() {
try {
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
final int d = GLStateManager.pushState(StateSet.BLEND);
GLStateManager.setHudCacheOverride(true);
GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
GLStateManager.popStateTo(d);
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
GLStateManager.setHudCacheOverride(false);
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
} finally {
GLStateManager.setHudCacheOverride(false);
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
}
}
@Test
void separateAlphaIsForcedEvenWhenDstAlphaAlreadyMatches() {
try {
GLStateManager.setHudCacheOverride(true);
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE_MINUS_SRC_ALPHA);
assertCachedFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE_MINUS_SRC_ALPHA);
assertDriverFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
} finally {
GLStateManager.setHudCacheOverride(false);
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
}
}
}
@@ -0,0 +1,256 @@
package com.gtnewhorizons.angelica.glsm;
import com.gtnewhorizons.angelica.glsm.stacks.RetainedState;
import org.junit.jupiter.api.Test;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@GLCoreTest
public class GLSM_RetainedState_GLTest {
private static final StateSet ALL = StateSet.forMask(GL11.GL_ALL_ATTRIB_BITS);
private static int driverTextureBinding(int unit) {
final int saved = GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE);
GL13.glActiveTexture(GL13.GL_TEXTURE0 + unit);
final int binding = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
GL13.glActiveTexture(saved);
return binding;
}
private static void baseline() {
GLStateManager.enableCull();
GLStateManager.disableBlend();
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
GLStateManager.glDepthMask(true);
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1F);
GLStateManager.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0);
}
private static void mutate(int texture) {
GLStateManager.disableCull();
GLStateManager.enableBlend();
GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ONE);
GLStateManager.glDepthMask(false);
GLStateManager.glDepthFunc(GL11.GL_ALWAYS);
GLStateManager.glAlphaFunc(GL11.GL_GEQUAL, 0.5F);
GLStateManager.glColor4f(0.25F, 0.5F, 0.75F, 0.5F);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
}
private static void assertBaseline() {
assertTrue(GLStateManager.glIsEnabled(GL11.GL_CULL_FACE));
assertTrue(GL11.glIsEnabled(GL11.GL_CULL_FACE));
assertFalse(GLStateManager.glIsEnabled(GL11.GL_BLEND));
assertFalse(GL11.glIsEnabled(GL11.GL_BLEND));
assertEquals(GL11.GL_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB));
assertEquals(GL11.GL_ZERO, GL11.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
assertTrue(GL11.glGetBoolean(GL11.GL_DEPTH_WRITEMASK));
assertEquals(GL11.GL_LEQUAL, GL11.glGetInteger(GL11.GL_DEPTH_FUNC));
assertEquals(GL11.GL_GREATER, GLStateManager.glGetInteger(GL11.GL_ALPHA_TEST_FUNC));
assertEquals(1.0F, GLStateManager.getColor().getRed(), 0.0F);
assertEquals(0, driverTextureBinding(0));
}
private static void assertMutated(int texture) {
assertFalse(GLStateManager.glIsEnabled(GL11.GL_CULL_FACE));
assertFalse(GL11.glIsEnabled(GL11.GL_CULL_FACE));
assertTrue(GLStateManager.glIsEnabled(GL11.GL_BLEND));
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND));
assertEquals(GL11.GL_DST_COLOR, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB));
assertEquals(GL11.GL_ZERO, GL11.glGetInteger(GL14.GL_BLEND_DST_RGB));
assertEquals(GL11.GL_ONE, GL11.glGetInteger(GL14.GL_BLEND_SRC_ALPHA));
assertEquals(GL11.GL_ONE, GL11.glGetInteger(GL14.GL_BLEND_DST_ALPHA));
assertEquals(GL11.GL_DST_COLOR, GLStateManager.glGetInteger(GL14.GL_BLEND_SRC_RGB));
assertFalse(GL11.glGetBoolean(GL11.GL_DEPTH_WRITEMASK));
assertEquals(GL11.GL_ALWAYS, GL11.glGetInteger(GL11.GL_DEPTH_FUNC));
assertEquals(GL11.GL_GEQUAL, GLStateManager.glGetInteger(GL11.GL_ALPHA_TEST_FUNC));
assertEquals(0.25F, GLStateManager.getColor().getRed(), 0.0F);
assertEquals(0.5F, GLStateManager.getColor().getAlpha(), 0.0F);
assertEquals(texture, driverTextureBinding(0));
assertEquals(texture, GLStateManager.getBoundTextureForServerState(0));
}
@Test
void retainedDeltaReappliesAfterPop() {
final int texture = GL11.glGenTextures();
final RetainedState retained = new RetainedState();
try {
baseline();
final int d = GLStateManager.pushState(ALL);
mutate(texture);
GLStateManager.retainModifiedState(d, retained);
GLStateManager.popStateTo(d);
assertBaseline();
GLStateManager.applyRetainedState(retained);
assertMutated(texture);
baseline();
assertBaseline();
GLStateManager.applyRetainedState(retained);
assertMutated(texture);
} finally {
baseline();
GL11.glDeleteTextures(texture);
}
}
@Test
void appliedDeltaIsRestoredByAnEnclosingPush() {
final int texture = GL11.glGenTextures();
final RetainedState retained = new RetainedState();
try {
baseline();
final int d = GLStateManager.pushState(ALL);
mutate(texture);
GLStateManager.retainModifiedState(d, retained);
GLStateManager.popStateTo(d);
final int outer = GLStateManager.pushState(ALL);
GLStateManager.applyRetainedState(retained);
assertMutated(texture);
GLStateManager.popStateTo(outer);
assertBaseline();
} finally {
baseline();
GL11.glDeleteTextures(texture);
}
}
@Test
void statesRestoredByANestedPushAreNotRetained() {
final RetainedState retained = new RetainedState();
try {
baseline();
final int d = GLStateManager.pushState(ALL);
GLStateManager.enableBlend();
final int nested = GLStateManager.pushState(ALL);
GLStateManager.disableCull();
GLStateManager.popStateTo(nested);
GLStateManager.retainModifiedState(d, retained);
GLStateManager.popStateTo(d);
GLStateManager.applyRetainedState(retained);
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND));
assertTrue(GL11.glIsEnabled(GL11.GL_CULL_FACE));
assertTrue(GLStateManager.glIsEnabled(GL11.GL_CULL_FACE));
} finally {
baseline();
}
}
@Test
void onlyDifferingStateIsReissued() {
final int texture = GL11.glGenTextures();
final RetainedState retained = new RetainedState();
try {
baseline();
final int d = GLStateManager.pushState(ALL);
mutate(texture);
GLStateManager.retainModifiedState(d, retained);
GLStateManager.popStateTo(d);
GLStateManager.applyRetainedState(retained);
final GLContextState glCtx = GLStateManager.ctx();
final long backendCalls = GLStateManager.attribBackendCalls;
final int colorGen = glCtx.colorGeneration;
final int fragmentGen = glCtx.fragmentGeneration;
final int batchGen = glCtx.batchStateGeneration;
GLStateManager.applyRetainedState(retained);
assertEquals(backendCalls, GLStateManager.attribBackendCalls);
assertEquals(colorGen, glCtx.colorGeneration);
assertEquals(fragmentGen, glCtx.fragmentGeneration);
assertEquals(batchGen, glCtx.batchStateGeneration);
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
GLStateManager.applyRetainedState(retained);
assertEquals(backendCalls + 3, GLStateManager.attribBackendCalls);
assertMutated(texture);
} finally {
baseline();
GL11.glDeleteTextures(texture);
}
}
@Test
void aSecondCaptureInvalidatesTheFirst() {
final RetainedState first = new RetainedState();
final RetainedState second = new RetainedState();
try {
baseline();
int d = GLStateManager.pushState(ALL);
GLStateManager.enableBlend();
GLStateManager.retainModifiedState(d, first);
GLStateManager.popStateTo(d);
d = GLStateManager.pushState(ALL);
GLStateManager.disableCull();
GLStateManager.retainModifiedState(d, second);
GLStateManager.popStateTo(d);
GLStateManager.applyRetainedState(first);
assertFalse(GL11.glIsEnabled(GL11.GL_BLEND));
assertTrue(GL11.glIsEnabled(GL11.GL_CULL_FACE));
GLStateManager.applyRetainedState(second);
assertFalse(GL11.glIsEnabled(GL11.GL_BLEND));
assertFalse(GL11.glIsEnabled(GL11.GL_CULL_FACE));
} finally {
baseline();
}
}
@Test
void writesInAnOuterPushAreNotRetained() {
final RetainedState retained = new RetainedState();
try {
baseline();
GLStateManager.glClearColor(0.25F, 0.5F, 0.75F, 1.0F);
final int outer = GLStateManager.pushState(ALL);
GLStateManager.glClearColor(0.0F, 0.0F, 0.0F, 0.0F);
final int inner = GLStateManager.pushState(ALL);
GLStateManager.enableBlend();
GLStateManager.retainModifiedState(inner, retained);
GLStateManager.popStateTo(outer);
GLStateManager.applyRetainedState(retained);
assertTrue(GL11.glIsEnabled(GL11.GL_BLEND));
assertEquals(0.25F, GLStateManager.getClearColor().getRed(), 0.0F);
assertEquals(1.0F, GLStateManager.getClearColor().getAlpha(), 0.0F);
} finally {
baseline();
GLStateManager.glClearColor(0.0F, 0.0F, 0.0F, 0.0F);
}
}
@Test
void aDeletedProgramIsNotRebound() {
final RetainedState retained = new RetainedState();
final int program = GLSM_StateSet_GLTest.linkTrivialProgram();
try {
baseline();
final int d = GLStateManager.pushState(ALL);
GLStateManager.glUseProgram(program);
GLStateManager.retainModifiedState(d, retained);
GLStateManager.popStateTo(d);
GLStateManager.glUseProgram(0);
GLStateManager.glDeleteProgram(program);
while (GL11.glGetError() != GL11.GL_NO_ERROR) {}
GLStateManager.applyRetainedState(retained);
assertEquals(0, GLStateManager.getActiveProgram());
assertEquals(GL11.GL_NO_ERROR, GL11.glGetError());
} finally {
GLStateManager.glUseProgram(0);
baseline();
}
}
}
@@ -45,7 +45,7 @@ public class GLSM_StateSet_GLTest {
private static final String TRIVIAL_VS = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }\n";
private static final String TRIVIAL_FS = "#version 330 core\nout vec4 o;\nvoid main(){ o = vec4(1.0); }\n";
private static int linkTrivialProgram() {
static int linkTrivialProgram() {
final int v = compileShader(GL20.GL_VERTEX_SHADER, TRIVIAL_VS);
final int f = compileShader(GL20.GL_FRAGMENT_SHADER, TRIVIAL_FS);
final int p = GL20.glCreateProgram();
@@ -1,32 +1,30 @@
package com.gtnewhorizons.angelica.hudcaching;
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizon.gtnhlib.client.renderer.postprocessing.CustomFramebuffer;
import com.gtnewhorizon.gtnhlib.client.renderer.postprocessing.SharedDepthFramebuffer;
import com.gtnewhorizon.gtnhlib.client.renderer.vao.IVertexArrayObject;
import com.gtnewhorizon.gtnhlib.client.renderer.vao.VertexBufferType;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
import com.gtnewhorizons.angelica.compat.ModStatus;
import com.gtnewhorizons.angelica.compat.holoinventory.HoloInventoryReflectionCompat;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.event.ClientEvent;
import com.gtnewhorizons.angelica.event.ClientEventType;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
import com.gtnewhorizons.angelica.glsm.StateSet;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import com.gtnewhorizons.angelica.glsm.stacks.RetainedState;
import com.gtnewhorizons.angelica.mixins.interfaces.GuiIngameAccessor;
import com.gtnewhorizons.angelica.mixins.interfaces.GuiIngameForgeAccessor;
import com.gtnewhorizons.angelica.mixins.interfaces.RenderGameOverlayEventAccessor;
import com.gtnewhorizons.angelica.render.PanoramaRenderer;
import com.kentington.thaumichorizons.common.ThaumicHorizons;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiIngame;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraftforge.client.GuiIngameForge;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import org.embeddedt.embeddium.impl.gl.shader.GlProgram;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL30;
import thaumcraft.common.Thaumcraft;
import xaero.common.core.XaeroMinimapCore;
@@ -35,13 +33,20 @@ import xaero.common.core.XaeroMinimapCore;
public class HUDCaching {
private static final Minecraft mc = Minecraft.getMinecraft();
public static SharedDepthFramebuffer framebuffer;
public static CustomFramebuffer framebuffer;
private static boolean dirty = true;
private static long nextHudRefresh;
private static IVertexArrayObject quadVAO;
private static float quadWidth;
private static float quadHeight;
private static final RetainedState hudExitState = new RetainedState();
private static StateSet allState;
private static GlProgram<Object> compositeProgram;
private static int compositeVao;
private static final Tracy.ZoneId Z_CACHE_WRAP = Tracy.zoneId("hudCacheWrap", Tracy.COLOR_CLIENT);
private static final Tracy.ZoneId Z_DRAW = Tracy.zoneId("hudDraw", Tracy.COLOR_CLIENT);
private static final Tracy.ZoneId Z_CAPTURED_BITS = Tracy.zoneId("hudCapturedBits", Tracy.COLOR_CLIENT);
private static final Tracy.ZoneId Z_COMPOSITE = Tracy.zoneId("hudComposite", Tracy.COLOR_CLIENT);
private static final Tracy.ZoneId Z_APPLY_RETAINED = Tracy.zoneId("hudApplyRetained", Tracy.COLOR_CLIENT);
public static boolean renderingCacheOverride;
@@ -61,16 +66,79 @@ public class HUDCaching {
private final static RenderGameOverlayEvent fakeTextEvent = new RenderGameOverlayEvent.Text(new RenderGameOverlayEvent(0, null, 0, 0), null, null);
private final static RenderGameOverlayEvent.Post fakePostEvent = new RenderGameOverlayEvent.Post(new RenderGameOverlayEvent(0, null, 0, 0), RenderGameOverlayEvent.ElementType.HELMET);
private static ScaledResolution resolution;
private static int resolutionWidth, resolutionHeight, resolutionGuiScale;
private static boolean resolutionUnicode;
private static ScaledResolution resolution() {
final int width = mc.displayWidth, height = mc.displayHeight, guiScale = mc.gameSettings.guiScale;
final boolean unicode = mc.func_152349_b();
if (resolution == null || width != resolutionWidth || height != resolutionHeight || guiScale != resolutionGuiScale || unicode != resolutionUnicode) {
resolutionWidth = width;
resolutionHeight = height;
resolutionGuiScale = guiScale;
resolutionUnicode = unicode;
resolution = new ScaledResolution(mc, width, height);
}
return resolution;
}
private static void setupOverlay(ScaledResolution scaled) {
GLStateManager.glClear(GL11.GL_DEPTH_BUFFER_BIT);
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
GLStateManager.glLoadIdentity();
GLStateManager.glOrtho(0.0D, scaled.getScaledWidth_double(), scaled.getScaledHeight_double(), 0.0D, 1000.0D, 3000.0D);
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
GLStateManager.glLoadIdentity();
GLStateManager.glTranslatef(0.0F, 0.0F, -2000.0F);
}
public static void init() {
framebuffer = new SharedDepthFramebuffer(CustomFramebuffer.STENCIL_BUFFER);
framebuffer = new CustomFramebuffer(CustomFramebuffer.DEPTH_TEXTURE | CustomFramebuffer.STENCIL_BUFFER);
}
public static void resetAfterCrash() {
renderingCacheOverride = false;
GLSMConfig.hudCacheOverride = false;
GLStateManager.setHudCacheOverride(false);
dirty = true;
}
private static void initComposite() {
compositeProgram = PanoramaRenderer.loadProgram("angelica:hud_composite", "angelica:panorama_blur", "angelica:hud_composite", ctx -> ctx);
compositeProgram.bind();
GLStateManager.glUniform1i(GLStateManager.glGetUniformLocation(compositeProgram.handle(), "u_Depth"), 1);
compositeProgram.unbind();
compositeVao = GLStateManager.glGenVertexArrays();
}
private static void composite() {
if (compositeProgram == null) initComposite();
final int program = GLStateManager.getActiveProgram();
final int vao = GLStateManager.glGetInteger(GL30.GL_VERTEX_ARRAY_BINDING);
final int stateDepth = GLStateManager.pushState(allState);
try {
GLStateManager.enableBlend();
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
GLStateManager.enableDepthTest();
GLStateManager.glDepthFunc(GL11.GL_ALWAYS);
GLStateManager.glDepthMask(true);
GLStateManager.glColorMask(true, true, true, true);
GLStateManager.disableCull();
GLStateManager.disableScissorTest();
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, framebuffer.depthAttachment);
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, framebuffer.framebufferTexture);
compositeProgram.bind();
GLStateManager.glBindVertexArray(compositeVao);
GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
} finally {
GLStateManager.glBindVertexArray(vao);
GLStateManager.glUseProgram(program);
GLStateManager.popStateTo(stateDepth);
}
}
public static void renderCachedHud(EntityRenderer renderer, GuiIngame ingame, float partialTicks, boolean hasScreen, int mouseX, int mouseY) {
ClientEvent.post(ClientEventType.PRE_RENDER_GUI, renderer, partialTicks);
ClientEvent.post(ClientEventType.PRE_RENDER_HUD, renderer, partialTicks);
@@ -94,31 +162,45 @@ public class HUDCaching {
if (System.currentTimeMillis() > nextHudRefresh) {
dirty = true;
}
if (allState == null) allState = StateSet.forMask(GL11.GL_ALL_ATTRIB_BITS);
if (dirty) {
dirty = false;
nextHudRefresh = System.currentTimeMillis() + (1000 / AngelicaConfig.hudCachingFPS);
if (framebuffer.framebufferWidth != mc.displayWidth || framebuffer.framebufferHeight != mc.displayHeight) {
framebuffer.createBindFramebuffer(mc.displayWidth, mc.displayHeight);
} else {
framebuffer.clearBindFramebuffer();
}
renderingCacheOverride = true;
GLSMConfig.hudCacheOverride = true;
if (Tracy.ENABLED) Tracy.beginZone(Z_CACHE_WRAP);
final int outerDepth = GLStateManager.pushState(allState);
try {
ingame.renderGameOverlay(partialTicks, hasScreen, mouseX, mouseY);
if (framebuffer.framebufferWidth != mc.displayWidth || framebuffer.framebufferHeight != mc.displayHeight) {
framebuffer.createBindFramebuffer(mc.displayWidth, mc.displayHeight);
} else {
framebuffer.clearBindFramebuffer();
}
final int hudDepth = GLStateManager.pushState(allState);
renderingCacheOverride = true;
GLStateManager.setHudCacheOverride(true);
if (Tracy.ENABLED) Tracy.beginZone(Z_DRAW);
try {
ingame.renderGameOverlay(partialTicks, hasScreen, mouseX, mouseY);
} finally {
if (Tracy.ENABLED) Tracy.endZone();
}
GLStateManager.setHudCacheOverride(false);
GLStateManager.popStateTo(hudDepth + 1);
GLStateManager.retainModifiedState(hudDepth, hudExitState);
} finally {
renderingCacheOverride = false;
GLSMConfig.hudCacheOverride = false;
GLStateManager.setHudCacheOverride(false);
GLStateManager.popStateTo(outerDepth);
mc.getFramebuffer().bindFramebuffer(false);
if (Tracy.ENABLED) Tracy.endZone();
}
} else {
renderer.setupOverlayRendering();
}
ScaledResolution resolution = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
int width = resolution.getScaledWidth();
int height = resolution.getScaledHeight();
final ScaledResolution scaled = resolution();
setupOverlay(scaled);
int width = scaled.getScaledWidth();
int height = scaled.getScaledHeight();
if (Tracy.ENABLED) Tracy.beginZone(Z_CAPTURED_BITS);
GLStateManager.enableBlend();
// reset the color that may be applied by some items
@@ -135,7 +217,7 @@ public class HUDCaching {
if (ingame instanceof GuiIngameForge) {
GuiIngameForgeAccessor guiForge = ((GuiIngameForgeAccessor) ingame);
if (renderHelmetCaptured) {
guiForge.callRenderHelmet(resolution, partialTicks, hasScreen, mouseX, mouseY);
guiForge.callRenderHelmet(scaled, partialTicks, hasScreen, mouseX, mouseY);
if (ModStatus.isHoloInventoryLoaded) {
HoloInventoryReflectionCompat.setAngelicaOverride(false);
// only settings the partial ticks as mouseX and mouseY are not used in renderEvent
@@ -155,7 +237,7 @@ public class HUDCaching {
}
if (ModStatus.isThaumcraftLoaded || ModStatus.isThaumicHorizonsLoaded) {
((RenderGameOverlayEventAccessor) fakeTextEvent).setPartialTicks(partialTicks);
((RenderGameOverlayEventAccessor) fakeTextEvent).setResolution(resolution);
((RenderGameOverlayEventAccessor) fakeTextEvent).setResolution(scaled);
((RenderGameOverlayEventAccessor) fakeTextEvent).setMouseX(mouseX);
((RenderGameOverlayEventAccessor) fakeTextEvent).setMouseY(mouseY);
if (ModStatus.isThaumcraftLoaded) {
@@ -174,15 +256,14 @@ public class HUDCaching {
}
}
// render cached frame
GLStateManager.enableBlend();
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
GLStateManager.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
framebuffer.bindFramebufferTexture();
drawTexturedRect((float) resolution.getScaledWidth_double(), (float) resolution.getScaledHeight_double());
if (Tracy.ENABLED) Tracy.endZone();
GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
mc.getTextureManager().bindTexture(Gui.icons);
if (Tracy.ENABLED) Tracy.beginZone(Z_COMPOSITE);
composite();
if (Tracy.ENABLED) Tracy.endZone();
if (Tracy.ENABLED) Tracy.beginZone(Z_APPLY_RETAINED);
GLStateManager.applyRetainedState(hudExitState);
if (Tracy.ENABLED) Tracy.endZone();
ClientEvent.post(ClientEventType.POST_RENDER_HUD, renderer, partialTicks);
}
@@ -200,35 +281,6 @@ public class HUDCaching {
GLStateManager.disableBlend();
}
private static void drawTexturedRect(float width, float height) {
if (quadWidth != width || quadHeight != height) {
quadWidth = width;
quadHeight = height;
rebuildVAO(width, height);
}
GLStateManager.disableDepthTest();
GLStateManager.enableTexture();
quadVAO.render();
GLStateManager.enableDepthTest();
}
private static void rebuildVAO(float width, float height) {
final DirectTessellator tessellator = TessellatorManager.startCapturingDirect(DefaultVertexFormat.POSITION_TEXTURE);
tessellator.startDrawingQuads();
tessellator.addVertexWithUV(0, height, 0.0, 0, 0);
tessellator.addVertexWithUV(width, height, 0.0, 1, 0);
tessellator.addVertexWithUV(width, 0, 0.0, 1, 1);
tessellator.addVertexWithUV(0, 0, 0.0, 0, 1);
tessellator.draw();
if (quadVAO == null) {
quadVAO = tessellator.uploadToVBO(VertexBufferType.MUTABLE);
} else {
tessellator.updateToVBO(quadVAO.getVBO());
}
TessellatorManager.stopCapturingDirect();
}
public static void disableHoloInventory() {
if (ModStatus.isHoloInventoryLoaded) {
HoloInventoryReflectionCompat.setAngelicaOverride(true);
@@ -94,7 +94,7 @@ public class PanoramaRenderer {
blurProgram.unbind();
}
private static <T> GlProgram<T> loadProgram(String name, String vertBase, String fragBase, Function<ShaderBindingContext, T> factory) {
public static <T> GlProgram<T> loadProgram(String name, String vertBase, String fragBase, Function<ShaderBindingContext, T> factory) {
final GlShader vert = ShaderLoader.loadShader(ShaderType.VERTEX, vertBase + ".vert", ShaderConstants.EMPTY);
final GlShader frag = ShaderLoader.loadShader(ShaderType.FRAGMENT, fragBase + ".frag", ShaderConstants.EMPTY);
try {
@@ -0,0 +1,13 @@
#version 330 core
in vec2 v_TexCoord;
uniform sampler2D u_Texture;
uniform sampler2D u_Depth;
out vec4 fragColor;
void main() {
fragColor = texture(u_Texture, v_TexCoord);
gl_FragDepth = texture(u_Depth, v_TexCoord).r;
}