Compare commits

..
8 Commits
Author SHA1 Message Date
ah-OOG-ah 6d8cbf9c2f Add a flag to StateAwareTesselator for per-quad directional shading (#1989)
* Add a flag for no directional shading

* Mark StateAwareTessellator as an API

* Reset internal state better
2026-08-19 18:16:04 +00:00
boubou19 096dd01446 Avoid rescanning atlas for each CTM tile (#2029) 2026-08-19 09:32:42 -07:00
Eclipse-SolandMartin Robertz 22c9479946 Many bug fixes (#2017)
Co-authored-by: Martin Robertz <[email protected]>
2026-08-19 08:55:43 -07:00
Jason Mitchell b2f597a73a SDL presenter fixes and frame pacer adjustments (#2025) 2026-08-16 17:59:33 -07:00
Jason Mitchell 553dec5b6d Mark tiles at transform time instead of using reflection (#2024) 2026-08-16 16:05:04 -07:00
Jason Mitchell bc8116006c Tracy 0.14 (#2019) 2026-08-15 19:09:36 -07:00
Jason Mitchell bf9492dffb Defer ticking on TEs we eagerly create until they get a description packet (#2018) 2026-08-15 17:04:50 -07:00
Jason Mitchell 69aed42b89 Sign Fix (#2016) 2026-08-15 14:32:50 -07:00
146 changed files with 4089 additions and 1144 deletions

No files matched your search

+5 -3
View File
@@ -2,7 +2,8 @@
# Builds the libTracyClient shared library for the tracy-client module.
set -euo pipefail
TRACY_VERSION="${TRACY_VERSION:-v0.13.1}"
TRACY_VERSION="${TRACY_VERSION:-v0.14.0}"
TRACY_VERSION_BARE="${TRACY_VERSION#v}"
OUT_DIR="${OUT_DIR:-dist/tracy-natives}"
TRACY_REPO="https://github.com/wolfpld/tracy"
@@ -16,7 +17,7 @@ case "$(uname -m)" in
arm64|aarch64) PLAT_ARCH="arm64" ;;
*) PLAT_ARCH="x64" ;;
esac
RES_DIR="${RES_DIR:-$REPO_ROOT/tracy-client/src/main/resources/natives/tracy/$PLAT_OS-$PLAT_ARCH}"
RES_DIR="${RES_DIR:-$REPO_ROOT/tracy-client/src/main/resources/natives/tracy/$TRACY_VERSION_BARE/$PLAT_OS-$PLAT_ARCH}"
WORK_DIR="$(mktemp -d)"
trap 'rm -rf "$WORK_DIR"' EXIT
@@ -33,7 +34,8 @@ fi
BUILD_DIR="$WORK_DIR/build"
export MACOSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET:-11.0}"
cmake -S "$SRC_DIR" -B "$BUILD_DIR" \
cmake -S "$REPO_ROOT/tracy-client/src/main/native" -B "$BUILD_DIR" \
-DTRACY_SRC_DIR="$SRC_DIR" \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=ON \
-DTRACY_STATIC=OFF \
+12 -23
View File
@@ -6,10 +6,6 @@ on:
- "tracy-v*"
workflow_dispatch:
inputs:
tracy-version:
description: Tracy git tag to build
required: false
default: v0.13.1
publish:
description: Publish angelica-tracy.jar to the GTNH maven
type: boolean
@@ -20,25 +16,25 @@ jobs:
build:
strategy:
matrix:
platform: [linux-x64, windows-x64, macos-x64, macos-arm64]
include:
- os: ubuntu-22.04
platform: linux-x64
- os: windows-2025
platform: windows-x64
- os: macos-15-intel
platform: macos-x64
- os: macos-15
platform: macos-arm64
- platform: linux-x64
os: ubuntu-22.04
- platform: windows-x64
os: windows-2025
- platform: macos-x64
os: macos-15-intel
- platform: macos-arm64
os: macos-15
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- name: Build libTracyClient
shell: bash
env:
RAW_VERSION: ${{ github.ref_type == 'tag' && github.ref_name || inputs.tracy-version }}
OUT_DIR: dist/tracy-natives
run: |
export TRACY_VERSION="${RAW_VERSION#tracy-}"
export TRACY_VERSION="v$(sed -nE 's/^tracy = "(.*)"$/\1/p' gradle/libs.versions.toml)"
bash .github/scripts/build-tracy-natives.sh
- uses: actions/upload-artifact@v4
with:
@@ -56,14 +52,6 @@ jobs:
distribution: temurin
java-version: 21
- uses: gradle/actions/setup-gradle@v4
- name: Check tracy version matches the version catalog
shell: bash
env:
RAW_VERSION: ${{ github.ref_type == 'tag' && github.ref_name || inputs.tracy-version }}
run: |
ver="${RAW_VERSION#tracy-}"
grep -q "^tracy = \"${ver#v}\"$" gradle/libs.versions.toml \
|| { echo "libs.versions.toml tracy does not match $ver" >&2; exit 1; }
- uses: actions/download-artifact@v5
with:
pattern: tracy-natives-*
@@ -71,8 +59,9 @@ jobs:
- name: Stage natives into tracy-client resources
shell: bash
run: |
version="$(sed -nE 's/^tracy = "(.*)"$/\1/p' gradle/libs.versions.toml)"
for d in staging/tracy-natives-*; do
dest="tracy-client/src/main/resources/natives/tracy/${d#staging/tracy-natives-}"
dest="tracy-client/src/main/resources/natives/tracy/$version/${d#staging/tracy-natives-}"
mkdir -p "$dest"
cp "$d"/* "$dest/"
done
+2
View File
@@ -43,3 +43,5 @@ tracy-client/src/main/resources/natives/
tracy-client/angelica/
dist/tracy-natives/
*.tracy
tracy-client/angelica/
+4 -2
View File
@@ -52,8 +52,8 @@ configurations.configureEach {
}
DependencyHandlerScope.of(dependencies).apply {
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.166:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.166:dev")
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.187:dev")
runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.187:dev")
runtimeOnlyNonPublishable(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
@@ -161,6 +161,8 @@ DependencyHandlerScope.of(dependencies).apply {
compileOnly(rfg.deobf("curse.maven:xaeros-minimap-263420:5060684"))
compileOnly(rfg.deobf("curse.maven:security-craft-64760:2818228"))
compileOnly("ganymedes01.etfuturum:Et-Futurum-Requiem:2.6.49-GTNH:dev")
compileOnly("com.github.GTNewHorizons:OpenBlocks:1.12.19-GTNH:dev")
compileOnly("com.github.GTNewHorizons:Chisel:2.17.31-GTNH:dev")
compileOnly(rfg.deobf("curse.maven:biomes-o-plenty-220318:2499612"))
devOnlyNonPublishable("com.github.GTNewHorizons:CosmeticArmorReworked:1.0.6-GTNH:dev")
@@ -58,7 +58,7 @@ public final class SystemProperties {
public static final boolean DEBUG_F3_DETAIL = Boolean.getBoolean("angelica.debug.f3Detail");
public static final boolean ENABLE_TEST_BLOCKS = Boolean.getBoolean("angelica.debug.testBlocks");
public static final boolean DUMP_CLASS = Boolean.getBoolean("angelica.debug.dumpClass");
public static final boolean DUMP_SHADERS = Boolean.getBoolean("angelica.debug.dumpShaders") || isDeobf();
private static final boolean DUMP_SHADERS_PROP = Boolean.getBoolean("angelica.debug.dumpShaders");
public static final boolean REDIRECTOR_LOGSPAM = Boolean.getBoolean("angelica.debug.redirectorLogspam");
public static final boolean DEBUG_DISPLAY_LISTS = Boolean.getBoolean("angelica.debug.displayLists");
public static final boolean LOG_DISPLAY_LIST_COMPILATION = Boolean.getBoolean("angelica.debug.displayLists.compilation");
@@ -68,8 +68,12 @@ public final class SystemProperties {
// Set by us, read by celeritas
public static final String KEY_CELERITAS_ENABLE_GL_DEBUG = "celeritas.enableGLDebug";
public static boolean dumpShaders() {
return DUMP_SHADERS_PROP || isDeobf();
}
public static Path shaderDumpDir(String phase) {
return DUMP_SHADERS ? Paths.get(SHADER_DUMP_ROOT, phase) : null;
return dumpShaders() ? Paths.get(SHADER_DUMP_ROOT, phase) : null;
}
public enum UnmappedGLMode {
@@ -118,7 +122,7 @@ public final class SystemProperties {
}
}
private static boolean isDeobf() {
public static boolean isDeobf() {
return Launch.blackboard != null && Boolean.TRUE.equals(Launch.blackboard.get("fml.deobfuscatedEnvironment"));
}
@@ -18,6 +18,7 @@ import com.gtnewhorizons.angelica.glsm.stacks.PointStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.PolygonStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.ViewPortStateStack;
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;
@@ -181,6 +182,9 @@ class GLContextState {
public int clientAttribStackPointer = 0;
public final boolean[] restoreUnitChanged = new boolean[GLStateManager.MAX_TEXTURE_UNITS];
public boolean restoreDepthChanged, restoreBlendChanged, restoreColorMaskChanged, restoreClearColorChanged, restoreDrawBufferChanged, restoreLogicOpChanged, restoreStencilChanged, restoreViewportChanged, restoreLineChanged, restorePointChanged, restorePolygonChanged, restoreActiveUnitChanged;
public final BlendState vanillaBlendBefore = new BlendState();
public final BlendState vanillaBlendAfter = new BlendState();
public boolean vanillaBlendEnabledBefore;
public int drawFramebuffer = 0;
public int readFramebuffer = 0;
public int texGenGeneration;
@@ -45,6 +45,8 @@ import com.gtnewhorizons.angelica.glsm.stacks.PointStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.PolygonStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.StencilStateStack;
import com.gtnewhorizons.angelica.glsm.stacks.ViewPortStateStack;
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;
@@ -1302,12 +1304,16 @@ public class GLStateManager {
return;
}
}
final BooleanStateStack blend = ctx().blendMode;
final boolean wasEnabled = blend.isEffectivelyEnabled();
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
if (bh != null && bh.isBlendLocked()) {
blend.beforeModify();
bh.deferBlendModeToggle(true);
return;
} else {
blend.enable();
}
ctx().blendMode.enable();
if (!wasEnabled && blend.isEffectivelyEnabled()) postVanillaBlendChange();
}
public static void disableBlend() {
@@ -1318,12 +1324,80 @@ public class GLStateManager {
return;
}
}
final BooleanStateStack blend = ctx().blendMode;
final boolean wasEnabled = blend.isEffectivelyEnabled();
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
if (bh != null && bh.isBlendLocked()) {
blend.beforeModify();
bh.deferBlendModeToggle(false);
return;
} else {
blend.disable();
}
ctx().blendMode.disable();
if (wasEnabled && !blend.isEffectivelyEnabled()) postVanillaBlendChange();
}
private static void postVanillaBlendChange() {
if (GLSMHooks.VANILLA_BLEND_CHANGE.hasListeners()) {
GLSMHooks.VANILLA_BLEND_CHANGE.post(GLSMHooks.vanillaBlendChangeEvent);
}
}
private static boolean snapshotVanillaBlendFunc() {
if (!GLSMHooks.VANILLA_BLEND_CHANGE.hasListeners()) return false;
final GLContextState glCtx = ctx();
glCtx.blendState.readEffective(glCtx.vanillaBlendBefore);
glCtx.vanillaBlendEnabledBefore = glCtx.blendMode.isEffectivelyEnabled();
return true;
}
private static void postVanillaBlendChangeIfMoved(boolean snapshotted) {
if (!snapshotted) return;
final GLContextState glCtx = ctx();
final BlendState before = glCtx.vanillaBlendBefore;
final BlendState after = glCtx.blendState.readEffective(glCtx.vanillaBlendAfter);
if (glCtx.blendMode.isEffectivelyEnabled() != glCtx.vanillaBlendEnabledBefore
|| after.getSrcRgb() != before.getSrcRgb()
|| after.getDstRgb() != before.getDstRgb()
|| after.getSrcAlpha() != before.getSrcAlpha()
|| after.getDstAlpha() != before.getDstAlpha()) {
postVanillaBlendChange();
}
}
public static boolean isEffectiveBlendEnabled() {
return ctx().blendMode.isEffectivelyEnabled();
}
public static BlendState getEffectiveBlendState(BlendState out) {
return ctx().blendState.readEffective(out);
}
public static boolean isEffectiveDepthMaskEnabled() {
return ctx().depthState.isEffectiveMaskEnabled();
}
public static boolean isEffectiveAlphaTestEnabled() {
return ctx().alphaTest.isEffectivelyEnabled();
}
public static AlphaState getEffectiveAlphaState(AlphaState out) {
return ctx().alphaState.readEffective(out);
}
private static int foreignDrawDepth;
public static void beginForeignDraw() {
foreignDrawDepth++;
}
public static void endForeignDraw() {
if (foreignDrawDepth > 0 && --foreignDrawDepth == 0 && GLSMHooks.FOREIGN_DRAW_END.hasListeners()) {
GLSMHooks.FOREIGN_DRAW_END.post(GLSMHooks.foreignDrawEndEvent);
}
}
public static boolean isForeignDraw() {
return foreignDrawDepth > 0;
}
public static void enableScissorTest() {
@@ -1357,9 +1431,11 @@ public class GLStateManager {
return;
}
}
final boolean snapshotted = snapshotVanillaBlendFunc();
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
if (bh != null && bh.isBlendLocked()) {
bh.deferBlendFunc(srcFactor, dstFactor, srcFactor, dstFactor);
postVanillaBlendChangeIfMoved(snapshotted);
return;
}
final boolean caching = isCachingEnabled();
@@ -1373,6 +1449,7 @@ public class GLStateManager {
GLSMHooks.blendFuncChangeEvent.dstAlpha = GL11.GL_ONE_MINUS_SRC_ALPHA;
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
}
postVanillaBlendChangeIfMoved(snapshotted);
return;
}
final boolean bypass = !caching;
@@ -1391,6 +1468,7 @@ public class GLStateManager {
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
}
}
postVanillaBlendChangeIfMoved(snapshotted);
}
public static void glBlendEquation(int mode) {
@@ -1430,9 +1508,11 @@ public class GLStateManager {
return;
}
}
final boolean snapshotted = snapshotVanillaBlendFunc();
final DeferredBlendHandler bh = GLSMHooks.blendHandler;
if (bh != null && bh.isBlendLocked()) {
bh.deferBlendFunc(srcRgb, dstRgb, srcAlpha, dstAlpha);
postVanillaBlendChangeIfMoved(snapshotted);
return;
}
if (GLSMConfig.hudCacheOverride && dstAlpha != GL11.GL_ONE_MINUS_SRC_ALPHA) {
@@ -1452,6 +1532,7 @@ public class GLStateManager {
GLSMHooks.BLEND_FUNC_CHANGE.post(GLSMHooks.blendFuncChangeEvent);
}
}
postVanillaBlendChangeIfMoved(snapshotted);
}
public static void glBlendFuncSeparate(int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) {
@@ -1746,6 +1827,7 @@ public class GLStateManager {
}
final DeferredAlphaHandler ah = GLSMHooks.alphaHandler;
if (ah != null && ah.isAlphaTestLocked()) {
glCtx.alphaTest.beforeModify();
ah.deferAlphaTestToggle(true);
return;
}
@@ -1767,6 +1849,7 @@ public class GLStateManager {
}
final DeferredAlphaHandler ah = GLSMHooks.alphaHandler;
if (ah != null && ah.isAlphaTestLocked()) {
glCtx.alphaTest.beforeModify();
ah.deferAlphaTestToggle(false);
return;
}
@@ -3532,6 +3615,9 @@ public class GLStateManager {
final int mask = glCtx.attribs.popInt();
glCtx.attribDepth--;
// Snapshot before the stacks pop, so applyRestoredState can tell whether vanilla blend actually moved.
final boolean blendSnapshotted = (mask & (GL11.GL_COLOR_BUFFER_BIT | GL11.GL_ENABLE_BIT)) != 0 && snapshotVanillaBlendFunc();
// First: restore BooleanStateStack states that were actually modified (fast path)
// These use lazy copy-on-write with global depth tracking
final List<IStateStack<?>> modified = glCtx.modifiedAtDepth[glCtx.attribDepth];
@@ -3550,7 +3636,12 @@ public class GLStateManager {
// Third: apply restored state to the GL driver. BooleanStateStacks already issue GL calls via setEnabled(); non-boolean stacks are pure data
// containers, so we must explicitly drive GL here.
applyRestoredState(mask);
applyRestoredState(mask, blendSnapshotted);
}
private static boolean isDepthColorOverridden() {
final DeferredDepthColorHandler dch = GLSMHooks.depthColorHandler;
return dch != null && dch.isOverrideHeld();
}
private static void captureRestoreChanges(int mask) {
@@ -3639,20 +3730,25 @@ public class GLStateManager {
/**
* After popping GLSM stacks, apply restored state to the GL driver.
*/
private static void applyRestoredState(int mask) {
private static void applyRestoredState(int mask, boolean blendSnapshotted) {
final GLContextState glCtx = ctx();
if ((mask & GL11.GL_DEPTH_BUFFER_BIT) != 0 && glCtx.restoreDepthChanged) {
RENDER_BACKEND.depthFunc(glCtx.depthState.getFunc());
RENDER_BACKEND.depthMask(glCtx.depthState.isEnabled());
if (!isDepthColorOverridden()) {
RENDER_BACKEND.depthMask(glCtx.depthState.isEnabled());
}
RENDER_BACKEND.clearDepth(glCtx.depthState.getClearValue());
}
if ((mask & GL11.GL_COLOR_BUFFER_BIT) != 0) {
if (glCtx.restoreBlendChanged) {
RENDER_BACKEND.blendFuncSeparate(glCtx.blendState.getSrcRgb(), glCtx.blendState.getDstRgb(), glCtx.blendState.getSrcAlpha(), glCtx.blendState.getDstAlpha());
final DeferredBlendHandler restoreBlendHandler = GLSMHooks.blendHandler;
if (restoreBlendHandler == null || !restoreBlendHandler.isOverrideHeld()) {
RENDER_BACKEND.blendFuncSeparate(glCtx.blendState.getSrcRgb(), glCtx.blendState.getDstRgb(), glCtx.blendState.getSrcAlpha(), glCtx.blendState.getDstAlpha());
}
RENDER_BACKEND.blendEquationSeparate(glCtx.blendState.getEquationRgb(), glCtx.blendState.getEquationAlpha());
RENDER_BACKEND.blendColor(glCtx.blendState.getBlendColorR(), glCtx.blendState.getBlendColorG(), glCtx.blendState.getBlendColorB(), glCtx.blendState.getBlendColorA());
}
if (glCtx.restoreColorMaskChanged) {
if (glCtx.restoreColorMaskChanged && !isDepthColorOverridden()) {
RENDER_BACKEND.colorMask(glCtx.colorMask.red, glCtx.colorMask.green, glCtx.colorMask.blue, glCtx.colorMask.alpha);
}
if (glCtx.restoreClearColorChanged) {
@@ -3737,6 +3833,8 @@ public class GLStateManager {
glCtx.dirtyTexCoordAttrib = true;
}
}
postVanillaBlendChangeIfMoved(blendSnapshotted);
}
public static void glClear(int mask) {
@@ -4925,16 +5023,22 @@ public class GLStateManager {
final ShaderManager ffp = ShaderManager.getInstance();
if (program == 0 && ffp.isEnabled()) {
final int prev = glCtx.activeProgram;
final boolean caching = isCachingEnabled();
if (prev != 0) glCtx.programGeneration++;
final boolean changed = prev != 0;
if (changed) glCtx.programGeneration++;
glCtx.activeProgram = 0; // Track that FFP was requested
if (GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
if (changed && GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
GLSMHooks.programChangeEvent.previousProgram = prev;
GLSMHooks.programChangeEvent.newProgram = 0;
GLSMHooks.programChangeEvent.postBind = false;
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
}
ffp.activate();
if (changed && GLSMHooks.PROGRAM_CHANGE.hasListeners()) {
GLSMHooks.programChangeEvent.previousProgram = prev;
GLSMHooks.programChangeEvent.newProgram = 0;
GLSMHooks.programChangeEvent.postBind = true;
GLSMHooks.PROGRAM_CHANGE.post(GLSMHooks.programChangeEvent);
}
return;
}
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.hooks;
public interface DeferredBlendHandler {
boolean isBlendLocked();
boolean isOverrideHeld();
void deferBlendModeToggle(boolean enabled);
void deferBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha);
void flushDeferredBlend();
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.hooks;
public interface DeferredDepthColorHandler {
boolean isDepthColorLocked();
boolean isOverrideHeld();
void deferDepthEnable(boolean enabled);
void deferColorMask(boolean r, boolean g, boolean b, boolean a);
}
@@ -6,6 +6,14 @@ public final class GLSMConfig {
public static float lastBrightnessX;
public static float lastBrightnessY;
public static int packBrightness(float x, float y) {
return ((int) y << 16) | ((int) x & 0xFFFF);
}
public static int packedLastBrightness() {
return packBrightness(lastBrightnessX, lastBrightnessY);
}
public static boolean hudCacheOverride;
public static volatile boolean expandVertexFormats;
@@ -25,6 +25,8 @@ public final class GLSMHooks {
public static final EventBus<LightmapCoordsEvent> LIGHTMAP_COORDS = EventBus.create(LightmapCoordsEvent.class);
public static final EventBus<AlphaStateChangeEvent> ALPHA_STATE_CHANGE = EventBus.create(AlphaStateChangeEvent.class);
public static final EventBus<ShaderColorChangeEvent> SHADER_COLOR_CHANGE = EventBus.create(ShaderColorChangeEvent.class);
public static final EventBus<ForeignDrawEndEvent> FOREIGN_DRAW_END = EventBus.create(ForeignDrawEndEvent.class);
public static final EventBus<VanillaBlendChangeEvent> VANILLA_BLEND_CHANGE = EventBus.create(VanillaBlendChangeEvent.class);
public static final EventBus<LoadingCheckpointEvent> LOADING_CHECKPOINT = EventBus.create(LoadingCheckpointEvent.class);
// Reusable event instances
@@ -38,6 +40,8 @@ public final class GLSMHooks {
public static final LightmapCoordsEvent lightmapCoordsEvent = new LightmapCoordsEvent();
public static final AlphaStateChangeEvent alphaStateChangeEvent = new AlphaStateChangeEvent();
public static final ShaderColorChangeEvent shaderColorChangeEvent = new ShaderColorChangeEvent();
public static final ForeignDrawEndEvent foreignDrawEndEvent = new ForeignDrawEndEvent();
public static final VanillaBlendChangeEvent vanillaBlendChangeEvent = new VanillaBlendChangeEvent();
public static final LoadingCheckpointEvent loadingCheckpointEvent = new LoadingCheckpointEvent();
private GLSMHooks() {}
@@ -0,0 +1,8 @@
package com.gtnewhorizons.angelica.glsm.hooks;
public interface VanillaBooleanLayer {
boolean isOverrideHeld();
boolean getVanilla();
void setVanilla(boolean enabled);
}
@@ -0,0 +1,22 @@
package com.gtnewhorizons.angelica.glsm.hooks;
public interface VanillaStateLayer<T> {
boolean isOverrideHeld();
void readVanilla(T into);
void writeVanilla(T from);
static boolean isHeld(VanillaStateLayer<?> layer) {
return layer != null && layer.isOverrideHeld();
}
static <T> void capture(VanillaStateLayer<T> layer, T slot) {
if (isHeld(layer)) layer.readVanilla(slot);
}
static <T> boolean restore(VanillaStateLayer<T> layer, T saved) {
if (!isHeld(layer)) return false;
layer.writeVanilla(saved);
return true;
}
}
@@ -0,0 +1,6 @@
package com.gtnewhorizons.angelica.glsm.hooks.events;
import net.minecraftforge.eventbus.api.event.MutableEvent;
public final class ForeignDrawEndEvent extends MutableEvent {
}
@@ -0,0 +1,6 @@
package com.gtnewhorizons.angelica.glsm.hooks.events;
import net.minecraftforge.eventbus.api.event.MutableEvent;
public final class VanillaBlendChangeEvent extends MutableEvent {
}
@@ -0,0 +1,92 @@
package com.gtnewhorizons.angelica.glsm.profiling;
import java.util.ArrayList;
import java.util.List;
public final class SectionRegistry {
public interface Emitter {
long enter(int category, String text);
void leave(long nativeId);
void setup(int category, String name);
boolean isConnected();
}
private static final class Category {
final int id;
final String name;
Category(int id, String name) {
this.id = id;
this.name = name;
}
}
private static final class Section {
final long token;
final int category;
final String text;
long nativeId;
Section(long token, int category, String text, long nativeId) {
this.token = token;
this.category = category;
this.text = text;
this.nativeId = nativeId;
}
}
private final Emitter emitter;
private final List<Category> categories = new ArrayList<>();
private final List<Section> open = new ArrayList<>();
private long nextToken;
private boolean connected;
public SectionRegistry(Emitter emitter) {
this.emitter = emitter;
}
public synchronized void registerCategory(int category, String name) {
categories.add(new Category(category, name));
}
public synchronized long enter(int category, String text) {
syncConnection();
final long token = ++nextToken;
open.add(new Section(token, category, text, emitter.enter(category, text)));
return token;
}
public synchronized void leave(long token) {
for (int i = 0; i < open.size(); i++) {
final Section section = open.get(i);
if (section.token == token) {
open.remove(i);
if (section.nativeId != 0L) emitter.leave(section.nativeId);
return;
}
}
}
public synchronized void poll() {
syncConnection();
}
private void syncConnection() {
final boolean now = emitter.isConnected();
if (now == connected) return;
connected = now;
if (now) replay();
}
private void replay() {
for (int i = 0; i < categories.size(); i++) {
final Category category = categories.get(i);
emitter.setup(category.id, category.name);
}
for (int i = 0; i < open.size(); i++) {
final Section section = open.get(i);
section.nativeId = emitter.enter(section.category, section.text);
}
}
}
@@ -44,6 +44,18 @@ public final class Tracy {
public static final int COLOR_IRIS = 0x9E862E; // gold
public static final int COLOR_FFP = 0x7A4E9E; // purple
public static final int SECTION_WORLD = 1;
public static final int SECTION_SHADERS = 2;
public static final int SECTION_BENCHMARK = 3;
public static final int SECTION_UI = 4;
private static final SectionRegistry SECTIONS = new SectionRegistry(new SectionRegistry.Emitter() {
@Override public long enter(int category, String text) { return BACKEND.sectionEnter(category, text); }
@Override public void leave(long nativeId) { BACKEND.sectionLeave(nativeId); }
@Override public void setup(int category, String name) { BACKEND.sectionSetup(category, name); }
@Override public boolean isConnected() { return BACKEND.isConnected(); }
});
private static final ThreadLocal<ZoneStack> STACK = new ThreadLocal<>() {
@Override
protected ZoneStack initialValue() {
@@ -62,6 +74,10 @@ public final class Tracy {
FINE_ZONES = ENABLED && SystemProperties.TRACY_FINE_ZONES;
if (ENABLED) {
Runtime.getRuntime().addShutdownHook(new Thread(Tracy::shutdown, "Tracy-Shutdown"));
SECTIONS.registerCategory(SECTION_WORLD, "World");
SECTIONS.registerCategory(SECTION_SHADERS, "Shaders");
SECTIONS.registerCategory(SECTION_BENCHMARK, "Benchmark");
SECTIONS.registerCategory(SECTION_UI, "UI");
LOGGER.info("Tracy profiling enabled");
}
}
@@ -95,6 +111,7 @@ public final class Tracy {
LOGGER.warn("Tracy requested (-Dangelica.tracy=true) but no TracyBackend service present");
return null;
}
if (!implementsCurrentAbi(found)) return null;
final TracyBackend backend = found;
final CountDownLatch done = new CountDownLatch(1);
final AtomicReference<Boolean> result = new AtomicReference<>();
@@ -120,10 +137,28 @@ public final class Tracy {
return backend;
}
private static final String ABI_SENTINEL = "sectionEnter";
private static boolean implementsCurrentAbi(TracyBackend backend) {
try {
backend.getClass().getMethod(ABI_SENTINEL, int.class, String.class);
return true;
} catch (NoSuchMethodException e) {
LOGGER.warn("Tracy backend {} was built against an older TracyBackend interface; profiling disabled", backend.getClass().getName());
return false;
}
}
private record InitRunner(TracyBackend backend, CountDownLatch done, AtomicReference<Boolean> result) implements Runnable {
@Override
public void run() {
final boolean ok = backend.init();
boolean ok;
try {
ok = backend.init();
} catch (LinkageError e) {
LOGGER.warn("Tracy backend {} failed to link; profiling disabled: {}", backend.getClass().getName(), e);
ok = false;
}
if (!result.compareAndSet(null, ok) && ok) {
backend.shutdown();
}
@@ -168,7 +203,7 @@ public final class Tracy {
final String name = stack.poppedName();
if (name != null && WARNED_LEAKS.add(name)) {
LOGGER.warn("Tracy: unbalanced profiler section '{}' left open (leaked by its caller); closed at next root", name);
BACKEND.message("unbalanced profiler section '" + name + "' closed at root");
BACKEND.message("unbalanced profiler section '" + name + "' closed at root", TracyBackend.SEVERITY_WARNING);
}
}
}
@@ -274,6 +309,7 @@ public final class Tracy {
threadNameRefreshed = true;
BACKEND.setCurrentThreadName(Thread.currentThread().getName());
}
SECTIONS.poll();
BACKEND.frameMark();
}
@@ -330,8 +366,22 @@ public final class Tracy {
}
public static void message(String text) {
message(text, TracyBackend.SEVERITY_INFO);
}
public static void message(String text, int severity) {
if (!ENABLED || SHUTTING_DOWN || !CaptureGate.markersThisFrame) return;
BACKEND.message(text);
BACKEND.message(text, severity);
}
public static long sectionEnter(int category, String text) {
if (!ENABLED || SHUTTING_DOWN) return 0L;
return SECTIONS.enter(category, text);
}
public static void sectionLeave(long token) {
if (token == 0L || !ENABLED || SHUTTING_DOWN) return;
SECTIONS.leave(token);
}
public static boolean isConnected() {
@@ -5,6 +5,13 @@ public interface TracyBackend {
int PLOT_FORMAT_MEMORY = 1;
int PLOT_FORMAT_PERCENTAGE = 2;
int SEVERITY_TRACE = 0;
int SEVERITY_DEBUG = 1;
int SEVERITY_INFO = 2;
int SEVERITY_WARNING = 3;
int SEVERITY_ERROR = 4;
int SEVERITY_FATAL = 5;
boolean init();
long internSrcLoc(String name, int color);
@@ -24,7 +31,14 @@ public interface TracyBackend {
void plotInt(long namePtr, long value);
void plot(long namePtr, double value);
void message(String text);
void message(String text, int severity);
default void message(String text) { message(text, SEVERITY_INFO); }
long sectionEnter(int category, String text);
void sectionLeave(long id);
void sectionSetup(int category, String name);
void setCurrentThreadName(String name);
boolean isConnected();
void shutdown();
@@ -2,7 +2,7 @@ package com.gtnewhorizons.angelica.glsm.profiling;
public final class ZoneStack {
public static final long EMPTY = -1L;
static final int MAX_DEPTH = 512;
public static final int MAX_DEPTH = 512;
private long[] stack = new long[64];
private boolean[] gpu = new boolean[64];
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.glsm.stacks;
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
import lombok.Setter;
public class AlphaStateStack extends AlphaState implements IStateStack<AlphaState> {
@@ -10,6 +12,8 @@ public class AlphaStateStack extends AlphaState implements IStateStack<AlphaStat
protected int pointer;
@Setter private VanillaStateLayer<AlphaState> vanillaLayer;
public AlphaStateStack() {
stack = new AlphaState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
@@ -22,7 +26,7 @@ public class AlphaStateStack extends AlphaState implements IStateStack<AlphaStat
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
}
stack[pointer++].set(this);
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
return this;
}
@@ -31,10 +35,19 @@ public class AlphaStateStack extends AlphaState implements IStateStack<AlphaStat
throw new IllegalStateException("Stack underflow");
}
set(stack[--pointer]);
final AlphaState saved = stack[--pointer];
if (!VanillaStateLayer.restore(vanillaLayer, saved)) {
set(saved);
}
return this;
}
public AlphaState readEffective(AlphaState out) {
out.set(this);
VanillaStateLayer.capture(vanillaLayer, out);
return out;
}
public boolean isEmpty() {
return pointer == 0;
}
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.glsm.stacks;
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import com.gtnewhorizons.angelica.glsm.states.BlendState;
import lombok.Setter;
public class BlendStateStack extends BlendState implements IStateStack<BlendStateStack> {
@@ -10,6 +12,8 @@ public class BlendStateStack extends BlendState implements IStateStack<BlendStat
protected int pointer;
@Setter private VanillaStateLayer<BlendState> vanillaLayer;
public BlendStateStack() {
stack = new BlendState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
@@ -22,7 +26,7 @@ public class BlendStateStack extends BlendState implements IStateStack<BlendStat
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
}
stack[pointer++].set(this);
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
return this;
}
@@ -31,10 +35,21 @@ public class BlendStateStack extends BlendState implements IStateStack<BlendStat
throw new IllegalStateException("Stack underflow");
}
set(stack[--pointer]);
final BlendState saved = stack[--pointer];
if (VanillaStateLayer.restore(vanillaLayer, saved)) {
setExceptFunc(saved);
} else {
set(saved);
}
return this;
}
public BlendState readEffective(BlendState out) {
out.set(this);
VanillaStateLayer.capture(vanillaLayer, out);
return out;
}
public boolean isEmpty() {
return pointer == 0;
}
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.glsm.stacks;
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaBooleanLayer;
import com.gtnewhorizons.angelica.glsm.states.BooleanState;
import lombok.Setter;
/**
* A stack for boolean GL state with lazy copy-on-write optimization.
@@ -14,6 +16,7 @@ import com.gtnewhorizons.angelica.glsm.states.BooleanState;
public class BooleanStateStack extends BooleanState implements IStateStack<BooleanStateStack> {
protected final boolean[] stack;
@Setter private VanillaBooleanLayer vanillaLayer;
/**
* The depth at which state has been saved. Compared against GLStateManager.getAttribDepth()
@@ -56,7 +59,7 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
if (savedDepth >= stack.length) {
throw new IllegalStateException("Stack overflow size " + (savedDepth + 1) + " reached");
}
stack[savedDepth++] = enabled;
stack[savedDepth++] = vanillaValue();
return this;
}
@@ -65,12 +68,26 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
if (savedDepth == 0) {
throw new IllegalStateException("Stack underflow");
}
final boolean oldValue = stack[--savedDepth];
// Call setEnabledDirect to avoid triggering beforeModify during restore
setEnabledDirect(oldValue);
restore(stack[--savedDepth]);
return this;
}
public boolean isEffectivelyEnabled() {
return (vanillaLayer != null && vanillaLayer.isOverrideHeld()) ? vanillaLayer.getVanilla() : enabled;
}
private boolean vanillaValue() {
return isEffectivelyEnabled();
}
private void restore(boolean value) {
if (vanillaLayer != null && vanillaLayer.isOverrideHeld()) {
vanillaLayer.setVanilla(value);
} else {
setEnabledDirect(value);
}
}
@Override
public boolean isEmpty() {
return savedDepth == 0;
@@ -92,8 +109,7 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
public BooleanStateStack popDepth() {
// We're only called if we were modified, so savedDepth should match
if (savedDepth > 0) {
final boolean oldValue = stack[--savedDepth];
setEnabledDirect(oldValue);
restore(stack[--savedDepth]);
}
return this;
}
@@ -106,7 +122,7 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
public void beforeModify() {
final int globalDepth = GLStateManager.getAttribDepth();
if (savedDepth < globalDepth) {
stack[savedDepth++] = enabled;
stack[savedDepth++] = vanillaValue();
GLStateManager.registerModifiedState(this);
}
}
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.glsm.stacks;
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import com.gtnewhorizons.angelica.glsm.states.ColorMask;
import lombok.Setter;
public class ColorMaskStack extends ColorMask implements IStateStack<ColorMaskStack> {
@@ -10,6 +12,8 @@ public class ColorMaskStack extends ColorMask implements IStateStack<ColorMaskSt
protected int pointer;
@Setter private VanillaStateLayer<ColorMask> vanillaLayer;
public ColorMaskStack() {
stack = new ColorMask[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
@@ -22,7 +26,7 @@ public class ColorMaskStack extends ColorMask implements IStateStack<ColorMaskSt
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
}
stack[pointer++].set(this);
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
return this;
}
@@ -31,7 +35,10 @@ public class ColorMaskStack extends ColorMask implements IStateStack<ColorMaskSt
throw new IllegalStateException("Stack underflow");
}
set(stack[--pointer]);
final ColorMask saved = stack[--pointer];
if (!VanillaStateLayer.restore(vanillaLayer, saved)) {
set(saved);
}
return this;
}
@@ -2,7 +2,9 @@ package com.gtnewhorizons.angelica.glsm.stacks;
import com.gtnewhorizon.gtnhlib.client.renderer.stacks.IStateStack;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import com.gtnewhorizons.angelica.glsm.states.DepthState;
import lombok.Setter;
public class DepthStateStack extends DepthState implements IStateStack<DepthStateStack> {
@@ -10,6 +12,10 @@ public class DepthStateStack extends DepthState implements IStateStack<DepthStat
protected int pointer;
@Setter private VanillaStateLayer<DepthState> vanillaLayer;
private final DepthState effective = new DepthState();
public DepthStateStack() {
stack = new DepthState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
@@ -22,7 +28,7 @@ public class DepthStateStack extends DepthState implements IStateStack<DepthStat
throw new IllegalStateException("Stack overflow size " + (pointer + 1) + " reached");
}
stack[pointer++].set(this);
VanillaStateLayer.capture(vanillaLayer, stack[pointer++].set(this));
return this;
}
@@ -31,10 +37,21 @@ public class DepthStateStack extends DepthState implements IStateStack<DepthStat
throw new IllegalStateException("Stack underflow");
}
set(stack[--pointer]);
final DepthState saved = stack[--pointer];
if (VanillaStateLayer.restore(vanillaLayer, saved)) {
setExceptMask(saved);
} else {
set(saved);
}
return this;
}
public boolean isEffectiveMaskEnabled() {
if (!VanillaStateLayer.isHeld(vanillaLayer)) return isEnabled();
vanillaLayer.readVanilla(effective);
return effective.isEnabled();
}
public boolean isEmpty() {
return pointer == 0;
}
@@ -55,6 +55,15 @@ public class BlendState implements ISettableState<BlendState> {
return this;
}
public void setExceptFunc(BlendState state) {
final int sRgb = srcRgb, dRgb = dstRgb, sAlpha = srcAlpha, dAlpha = dstAlpha;
set(state);
this.srcRgb = sRgb;
this.dstRgb = dRgb;
this.srcAlpha = sAlpha;
this.dstAlpha = dAlpha;
}
@Override
public boolean sameAs(Object state) {
if (this == state) return true;
@@ -18,6 +18,12 @@ public class DepthState implements ISettableState<DepthState> {
return this;
}
public void setExceptMask(DepthState state) {
final boolean mask = enabled;
set(state);
this.enabled = mask;
}
@Override
public boolean sameAs(Object state) {
if (this == state) return true;
@@ -108,7 +108,7 @@ public class TessellatorStreamingDrawer {
final int defaultBrightness;
if (!tess.hasBrightness) {
defaultBrightness = ((int) GLSMConfig.lastBrightnessY << 16) | ((int) GLSMConfig.lastBrightnessX & 0xFFFF);
defaultBrightness = GLSMConfig.packedLastBrightness();
} else {
defaultBrightness = 0;
}
@@ -0,0 +1,113 @@
package com.gtnewhorizons.angelica.glsm.profiling;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class SectionRegistryTest {
private static final class RecordingEmitter implements SectionRegistry.Emitter {
final List<String> setups = new ArrayList<>();
final List<String> enters = new ArrayList<>();
final List<Long> leaves = new ArrayList<>();
long nextId;
boolean connected;
@Override
public long enter(int category, String text) {
enters.add(category + ":" + text);
return connected ? ++nextId : 0L;
}
@Override
public void leave(long nativeId) {
leaves.add(nativeId);
}
@Override
public void setup(int category, String name) {
setups.add(category + ":" + name);
}
@Override
public boolean isConnected() {
return connected;
}
}
private final RecordingEmitter emitter = new RecordingEmitter();
private final SectionRegistry registry = new SectionRegistry(emitter);
private void reconnect() {
emitter.connected = false;
registry.poll();
emitter.connected = true;
registry.poll();
}
@Test
void tokensOutliveEveryReplayAndLeaveUsesTheNewestId() {
final long openedWhileDisconnected = registry.enter(1, "world");
assertNotEquals(0L, openedWhileDisconnected, "token must be valid while disconnected");
assertEquals(0L, emitter.nextId, "no native id while disconnected");
emitter.connected = true;
registry.poll();
final long openedWhileConnected = registry.enter(2, "shaders");
assertNotEquals(openedWhileDisconnected, openedWhileConnected);
reconnect();
emitter.leaves.clear();
registry.leave(openedWhileDisconnected);
registry.leave(openedWhileConnected);
assertEquals(List.of(3L, 4L), emitter.leaves, "leave must use the post-reconnect ids");
}
@Test
void aSectionEnteredOnTheConnectingFrameIsNotEnteredTwice() {
emitter.connected = true;
registry.enter(2, "shaders pack / Overworld");
registry.poll();
assertEquals(List.of("2:shaders pack / Overworld"), emitter.enters, "must not re-enter a section already live on this connection");
}
@Test
void reconnectReplaysEveryCategoryAndEveryOpenSection() {
registry.registerCategory(1, "World");
registry.registerCategory(4, "UI");
emitter.connected = true;
registry.poll();
registry.enter(1, "world");
registry.enter(4, "pause");
emitter.setups.clear();
emitter.enters.clear();
reconnect();
assertEquals(List.of("1:World", "4:UI"), emitter.setups, "all categories re-registered");
assertEquals(List.of("1:world", "4:pause"), emitter.enters, "all open sections re-entered");
}
@Test
void leftSectionsAreNotResurrectedByAReconnect() {
emitter.connected = true;
registry.poll();
final long token = registry.enter(1, "transient");
registry.leave(token);
emitter.enters.clear();
emitter.leaves.clear();
reconnect();
registry.leave(token);
assertTrue(emitter.enters.isEmpty(), "closed sections must not be re-entered");
assertTrue(emitter.leaves.isEmpty(), "closed tokens must not be left twice");
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ eventbus = "7.0-beta.15"
glslTransformLib = "0.2.0-33.g3ec381e"
antlr4Runtime = "4.13.2"
jcpp = "1.4.14"
tracy = "0.13.1"
tracy = "0.14.0"
retroFuturaBootstrap = "1.1.0"
lombok = "1.18.46"
@@ -88,7 +88,6 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import org.lwjgl.sdl.SDLGPU;
import org.lwjgl.sdl.SDLSurface;
import org.lwjgl.sdl.SDL_GPUBlitInfo;
import org.lwjgl.sdl.SDL_GPUBufferLocation;
import org.lwjgl.sdl.SDL_GPUTextureLocation;
@@ -111,7 +110,7 @@ public class SDLGPURenderBackend extends RenderBackend {
private final Device device = SDLGPUGate.device();
private final FrameManager frameManager = new FrameManager(device);
private Presenter presenter;
private volatile Presenter presenter;
private final ResourceManager resourceManager = new ResourceManager(device, frameManager);
private final Image3DClear image3DClear = new Image3DClear();
private final ShaderManager shaderManager = new ShaderManager(device);
@@ -155,7 +154,8 @@ public class SDLGPURenderBackend extends RenderBackend {
private volatile boolean shutdown;
private OffscreenTarget splashTarget;
private volatile OffscreenTarget splashTarget;
private volatile boolean splashTeardownPending;
private static final ThreadRegistry<ContextState> registeredStates = new ThreadRegistry<>(new ContextState[0]);
private static final ThreadLocal<ContextState> tlState = new ThreadLocal<>();
@@ -241,16 +241,12 @@ public class SDLGPURenderBackend extends RenderBackend {
});
GLSMHooks.LOADING_CHECKPOINT
.addListener(event -> {
if (Thread.currentThread() == GLStateManager.getMainThread()) {
if (!GLStateManager.isSplashComplete()) {
SplashDispatcher.tryDispatch(this, splashTarget);
} else if (splashTarget != null) {
SplashDispatcher.signalFrameReady();
SplashDispatcher.tryDispatch(this, splashTarget);
splashTarget.destroy(resourceManager);
splashTarget = null;
SplashDispatcher.reset();
if (!GLStateManager.isSplashComplete()) {
if (Thread.currentThread() == GLStateManager.getMainThread()) {
SplashDispatcher.tryDispatch(this, splashTarget, false);
}
} else if (splashTarget != null) {
splashTeardownPending = true;
}
if (event.requiresSync) {
frameManager.requestSyncFlushOnAllRegisteredFrames();
@@ -411,12 +407,12 @@ public class SDLGPURenderBackend extends RenderBackend {
@Override public boolean wantsDisplayUpdateGateTiming() { return false; }
@Override public long lastFrameGateNanos() {
if (presenter != null && presenter.isEngaged()) return frameManager.windowFrame().lastFrameGateNanos;
if (presenter != null) return frameManager.windowFrame().lastFrameGateNanos;
return frameManager.lastFrameGateNanos();
}
@Override public long lastFrameGateEndNanos() {
if (presenter != null && presenter.isEngaged()) return frameManager.windowFrame().lastFrameGateEndNanos;
if (presenter != null) return frameManager.windowFrame().lastFrameGateEndNanos;
return frameManager.lastFrameGateEndNanos();
}
@@ -520,8 +516,9 @@ public class SDLGPURenderBackend extends RenderBackend {
@Override public boolean handleMakeCurrent(Object drawable) {
if (!isSDLManagedDrawable(drawable)) return false;
Lwjgl3GLCapabilitiesShim.installOnCurrentThread(advertisedCapabilities());
if (splashTarget != null && Thread.currentThread() != GLStateManager.getMainThread()) {
s().boundFboId = splashTarget.fboId();
final OffscreenTarget splash = splashTarget;
if (splash != null && Thread.currentThread() != GLStateManager.getMainThread()) {
s().boundFboId = splash.fboId();
frameManager.frame().swapchainUnavailable = true;
}
return true;
@@ -542,7 +539,8 @@ public class SDLGPURenderBackend extends RenderBackend {
}
@Override public boolean handleSwapBuffers() {
if (splashTarget != null && splashTarget.isFor(s()) && Thread.currentThread() != GLStateManager.getMainThread()) {
final OffscreenTarget splash = splashTarget;
if (splash != null && splash.isFor(s()) && Thread.currentThread() != GLStateManager.getMainThread()) {
endFrameUploadFlush();
frameManager.endFrame();
SplashDispatcher.signalFrameReady((int) s().viewportW, (int) s().viewportH);
@@ -560,13 +558,24 @@ public class SDLGPURenderBackend extends RenderBackend {
final long drawnSize = SplashDispatcher.getDrawnSize();
final int srcW = Math.max(1, Math.min((int) (drawnSize >> 32), target.width()));
final int srcH = Math.max(1, Math.min((int) drawnSize, target.height()));
final FrameState f = frameManager.frame();
frameManager.presentBlit(f, target.colorTexture(), srcW, srcH, SDLSurface.SDL_FLIP_VERTICAL);
f.presentedThisFrame = false;
frameManager.presentSplash(target.colorTexture(), srcW, srcH);
}
private void finishSplash() {
splashTeardownPending = false;
final OffscreenTarget target = splashTarget;
if (target == null) return;
SplashDispatcher.signalFrameReady();
SplashDispatcher.tryDispatch(this, target, true);
if (presenter != null) presenter.drain();
splashTarget = null;
target.destroy(resourceManager);
SplashDispatcher.reset();
}
@Override public void onFrameBegin() {
if (shutdown) return;
if (splashTeardownPending) finishSplash();
if (GLStateManager.isSplashComplete()) applyPendingVSync();
frameManager.beginFrame();
beginFrameInit();
@@ -30,6 +30,7 @@ import java.util.concurrent.atomic.AtomicLong;
import static org.lwjgl.sdl.SDLGPU.*;
import static org.lwjgl.sdl.SDLSurface.SDL_FLIP_NONE;
import static org.lwjgl.sdl.SDLSurface.SDL_FLIP_VERTICAL;
import static org.lwjgl.system.MemoryStack.*;
/**
@@ -61,7 +62,6 @@ public final class FrameManager {
public boolean fbo0UsedThisFrame;
public boolean presentedThisFrame;
public boolean swapchainUnavailable;
public boolean windowThreadChecked;
public long renderPassGeneration;
public boolean wantFenceOnNextSubmit;
@@ -464,6 +464,20 @@ public final class FrameManager {
}
}
private volatile boolean acquireThreadReported;
private void checkAcquireOnWindowThread() {
if (acquireThreadReported) return;
final Thread self = Thread.currentThread();
final Thread window = device.getWindowThread();
if (window == null || window == self) return;
acquireThreadReported = true;
final String message = "Swapchain acquired on '" + self.getName() + "', window created on '" + window.getName() + "'";
if (SystemProperties.isDeobf() && !SystemProperties.DISABLE_SDL_PRESENTER_THREAD) throw new IllegalStateException(message);
LOG.warn(message);
}
public long ensureCopyPass() {
final FrameState f = frame();
if (f.copyPass != 0) {
@@ -863,7 +877,7 @@ public final class FrameManager {
return frame().lastFrameGateEndNanos;
}
private Presenter presenter;
private volatile Presenter presenter;
public void setPresenter(Presenter presenter) {
this.presenter = presenter;
@@ -877,18 +891,24 @@ public final class FrameManager {
public void presentFinalTarget() {
if (!finalTarget.hasContent()) return;
final long texture = finalTarget.colorTexture();
final int width = finalTarget.width();
final int height = finalTarget.height();
present(finalTarget.colorTexture(), finalTarget.width(), finalTarget.height(), SDL_FLIP_NONE, false);
}
public void presentSplash(long srcTexture, int srcW, int srcH) {
present(srcTexture, srcW, srcH, SDL_FLIP_VERTICAL, true);
}
private void present(long srcTexture, int srcW, int srcH, int flipMode, boolean splash) {
final FrameState f = frame();
if (f.presentedThisFrame) return;
final Presenter p = presenter;
if (p != null && p.isEngaged()) {
final FrameState f = frame();
if (f.presentedThisFrame) return;
if (p != null) {
f.presentedThisFrame = true;
p.requestPresent(texture, width, height);
p.requestPresent(srcTexture, srcW, srcH, flipMode);
} else {
presentBlit(frame(), texture, width, height, SDL_FLIP_NONE);
presentBlit(f, srcTexture, srcW, srcH, flipMode);
}
if (splash) f.presentedThisFrame = false;
}
private final AtomicLong windowAcquireWaitNanos = new AtomicLong();
@@ -915,8 +935,8 @@ public final class FrameManager {
else f.presentSkipsThisFrame++;
}
public void presentOnWindowThread(long srcTexture, int srcW, int srcH) {
presentBlit(windowFrame, srcTexture, srcW, srcH, SDL_FLIP_NONE);
public void presentOnWindowThread(long srcTexture, int srcW, int srcH, int flipMode) {
presentBlit(windowFrame, srcTexture, srcW, srcH, flipMode);
windowFrame.presentedThisFrame = false;
windowFrame.swapchainUnavailable = false;
}
@@ -925,16 +945,7 @@ public final class FrameManager {
if (srcTexture == 0 || srcW <= 0 || srcH <= 0) return false;
if (f.presentedThisFrame || f.swapchainUnavailable) return false;
if (!f.windowThreadChecked) {
f.windowThreadChecked = true;
final Thread self = Thread.currentThread();
final Thread windowThread = device.getWindowThread();
if (windowThread != null && windowThread != self) {
LOG.warn("Swapchain acquired on '{}' but the SDL window was created on '{}'; SDL requires acquire on the window-creating thread", self.getName(), windowThread.getName());
} else {
LOG.info("First swapchain acquire on thread={}", self.getName());
}
}
checkAcquireOnWindowThread();
final long window = device.getClaimedWindow();
if (window == 0) return false;
@@ -1,7 +1,5 @@
package com.gtnewhorizons.angelica.sdlgpu.frame;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;
@@ -16,17 +14,13 @@ public final class Presenter {
this.windowThreadExecutor = windowThreadExecutor;
}
public boolean isEngaged() {
return windowThreadExecutor != null && GLStateManager.isSplashComplete();
}
public void requestPresent(long srcTexture, int srcW, int srcH) {
public void requestPresent(long srcTexture, int srcW, int srcH, int flipMode) {
pending.acquireUninterruptibly();
boolean submitted = false;
try {
windowThreadExecutor.execute(() -> {
try {
frameManager.presentOnWindowThread(srcTexture, srcW, srcH);
frameManager.presentOnWindowThread(srcTexture, srcW, srcH, flipMode);
} finally {
pending.release();
}
@@ -57,7 +57,6 @@ final class SdlFramePlots {
private static final long PLOT_PIPELINE_KEY_RECOMPUTES = Tracy.plotHandle("sdl.pipelineKeyRecomputes");
private static final long PLOT_PIPELINE_FAST_PATH_HITS = Tracy.plotHandle("sdl.pipelineFastPathHits");
private static final long PLOT_MID_FRAME_SUBMITS = Tracy.plotHandle("sdl.midFrameSubmits");
private static final long PLOT_GATE_US = Tracy.plotHandle("sdl.gateUs");
private static final int[] contextCounters = new int[SDLGPURenderBackend.CTR_COUNT];
@@ -110,6 +109,5 @@ final class SdlFramePlots {
Tracy.plotInt(PLOT_EMPTY_FRAMES, f.emptyFramesThisFrame);
Tracy.plotInt(PLOT_DROPPED_DRAWS, f.droppedDrawsThisFrame);
Tracy.plotInt(PLOT_MID_FRAME_SUBMITS, f.midFrameSubmitsThisFrame);
Tracy.plotInt(PLOT_GATE_US, f.lastFrameGateNanos / 1000);
}
}
@@ -681,7 +681,7 @@ public final class ShaderManager {
return null;
}
LOG.info("Vertex attribute conversion: program {} converted {}", program, attribs);
if (SystemProperties.DUMP_SHADERS) dumpVariantSource(program, src);
if (SystemProperties.dumpShaders()) dumpVariantSource(program, src);
return v;
} finally {
memFree(r.spirv());
@@ -80,7 +80,7 @@ public final class DxbcCrossCompile {
return new Output(copy, cached.entrypoint);
}
final int dumpId = SystemProperties.DUMP_SHADERS ? CrossCompileUtil.SHADER_DUMP_COUNTER.getAndIncrement() : -1;
final int dumpId = SystemProperties.dumpShaders() ? CrossCompileUtil.SHADER_DUMP_COUNTER.getAndIncrement() : -1;
if (dumpId >= 0) CrossCompileUtil.dumpSpirv(spirv, dumpId, glShaderType);
final String hlsl = compileToHlsl(spirv, glShaderType);
@@ -87,7 +87,7 @@ public final class MslCrossCompile {
return new Output(copy, cached.entrypoint);
}
final int dumpId = SystemProperties.DUMP_SHADERS ? CrossCompileUtil.SHADER_DUMP_COUNTER.getAndIncrement() : -1;
final int dumpId = SystemProperties.dumpShaders() ? CrossCompileUtil.SHADER_DUMP_COUNTER.getAndIncrement() : -1;
if (dumpId >= 0) CrossCompileUtil.dumpSpirv(spirv, dumpId, glShaderType);
try (MemoryStack stack = stackPush()) {
@@ -33,10 +33,10 @@ public final class SplashDispatcher {
return (long) w << 32 | (h & 0xffffffffL);
}
public static void tryDispatch(SDLGPURenderBackend backend, OffscreenTarget target) {
public static void tryDispatch(SDLGPURenderBackend backend, OffscreenTarget target, boolean force) {
if (!frameReady || target == null) return;
final long now = System.nanoTime();
if (now - lastBlitNanos < MIN_INTERVAL_NS) return;
if (!force && now - lastBlitNanos < MIN_INTERVAL_NS) return;
lastBlitNanos = now;
frameReady = false;
backend.dispatchSplashBlit(target);
@@ -10,6 +10,7 @@ import java.util.concurrent.Executor;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.lwjgl.sdl.SDLSurface.SDL_FLIP_NONE;
@Timeout(10)
class PresenterTest {
@@ -43,10 +44,10 @@ class PresenterTest {
final DeferredExecutor executor = new DeferredExecutor();
final Presenter p = presenter(executor);
p.requestPresent(1L, 10, 10);
p.requestPresent(1L, 10, 10, SDL_FLIP_NONE);
assertEquals(1, executor.tasks.size());
final Thread second = new Thread(() -> p.requestPresent(2L, 10, 10));
final Thread second = new Thread(() -> p.requestPresent(2L, 10, 10, SDL_FLIP_NONE));
second.start();
awaitWaiting(second);
assertEquals(1, executor.tasks.size(), "second present must not enqueue while the first is in flight");
@@ -61,7 +62,7 @@ class PresenterTest {
final DeferredExecutor executor = new DeferredExecutor();
final Presenter p = presenter(executor);
p.requestPresent(1L, 10, 10);
p.requestPresent(1L, 10, 10, SDL_FLIP_NONE);
final Thread drainer = new Thread(p::drain);
drainer.start();
@@ -74,8 +75,8 @@ class PresenterTest {
@Test
void anInlineExecutorReleasesTheSlotImmediately() {
final Presenter p = new Presenter(new FrameManager(new Device()), Runnable::run);
p.requestPresent(1L, 10, 10);
p.requestPresent(2L, 10, 10);
p.requestPresent(1L, 10, 10, SDL_FLIP_NONE);
p.requestPresent(2L, 10, 10, SDL_FLIP_NONE);
p.drain();
}
@@ -87,7 +88,7 @@ class PresenterTest {
fm.frame();
final int before = fm.registeredFrameCount();
p.requestPresent(1L, 10, 10);
p.requestPresent(1L, 10, 10, SDL_FLIP_NONE);
final Thread windowThread = new Thread(executor::runNext, "window-thread");
windowThread.start();
@@ -95,4 +96,18 @@ class PresenterTest {
assertEquals(before, fm.registeredFrameCount(), "the window thread must stay out of the frame registry");
}
@Test
void theSplashPresentGoesThroughTheWindowThreadToo() {
final DeferredExecutor executor = new DeferredExecutor();
final FrameManager fm = new FrameManager(new Device());
fm.setPresenter(new Presenter(fm, executor));
fm.frame();
final int before = fm.registeredFrameCount();
fm.presentSplash(1L, 10, 10);
assertEquals(1, executor.tasks.size(), "the splash blit must be enqueued on the window thread");
assertEquals(before, fm.registeredFrameCount(), "the splash present must not touch the calling thread's frame");
}
}
@@ -8,7 +8,9 @@ import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.config.FontConfig;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.ffp.FFPVertexLighting;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
import com.gtnewhorizons.angelica.glsm.states.BlendState;
import com.gtnewhorizons.angelica.glsm.streaming.PersistentStreamingBuffer;
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
@@ -17,9 +19,15 @@ import com.gtnewhorizons.angelica.rendering.tesr.ModelPartBatcher;
import com.gtnewhorizons.angelica.rendering.tesr.TesrBatchRenderer;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import lombok.Setter;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gl.program.Program;
import net.coderbot.iris.gl.program.ProgramBuilder;
import net.coderbot.iris.layer.GbufferPrograms;
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
import net.coderbot.iris.pipeline.PipelineManager;
import net.coderbot.iris.uniforms.CapturedRenderingState;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import org.embeddedt.embeddium.impl.render.shader.ShaderLoader;
@@ -41,6 +49,8 @@ import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryStack.stackPush;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAlloc;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memFree;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetByte;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memGetFloat;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutByte;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutShort;
@@ -87,6 +97,7 @@ public class BatchingFontRenderer {
private static class FontAAShader {
private static Program fontShader = null;
public static Program getProgram() {
if (fontShader == null) {
final String vsh = ShaderLoader.getShaderSource("angelica:fontFilter.vsh");
@@ -172,6 +183,9 @@ public class BatchingFontRenderer {
private int blendSrcRGB = GL11.GL_SRC_ALPHA;
private int blendDstRGB = GL11.GL_ONE_MINUS_SRC_ALPHA;
private final BlendState blendStateBefore = new BlendState();
private static final BlendState deferredBlendStateBefore = new BlendState();
private float lightmapU = 0.0f;
private float lightmapV = 0.0f;
private boolean lightmapActive = false;
@@ -217,6 +231,7 @@ public class BatchingFontRenderer {
private static final class TextSegment {
final Matrix4f mvp = new Matrix4f();
final Matrix4f modelView = new Matrix4f();
BatchingFontRenderer owner;
int cmdStart;
int cmdEnd;
@@ -224,6 +239,9 @@ public class BatchingFontRenderer {
float lightmapV;
boolean lightmapActive;
int lightmapTexture;
int packedLight;
float normalX, normalY, normalZ;
int blockEntityId;
}
// 16-bit EBO index range
@@ -235,7 +253,6 @@ public class BatchingFontRenderer {
private int deferredCmdWatermark;
private static final ObjectArrayList<TextSegment> deferredSegments = ObjectArrayList.wrap(new TextSegment[16], 0);
private static final ObjectArrayList<TextSegment> deferredSegmentPool = ObjectArrayList.wrap(new TextSegment[16], 0);
private boolean deferCurrentBatch;
private final Matrix4f batchProj = new Matrix4f();
private final Matrix4f batchModelView = new Matrix4f();
@@ -467,6 +484,10 @@ public class BatchingFontRenderer {
}
}
private void resolveModelView(Matrix4f dest) {
dest.set(batchMatrixValid ? batchModelView : GLStateManager.getModelViewMatrix());
}
private void sealBatchSegment() {
final int end = batchCommands.size();
if (end == batchSealedEnd) return;
@@ -475,10 +496,17 @@ public class BatchingFontRenderer {
segment.cmdStart = batchSealedEnd;
segment.cmdEnd = end;
resolveMvp(segment.mvp);
resolveModelView(segment.modelView);
segment.lightmapActive = lightmapActive;
segment.lightmapU = lightmapU;
segment.lightmapV = lightmapV;
segment.lightmapTexture = lightmapTextureId;
segment.packedLight = GLSMConfig.packedLastBrightness();
final Vector3f normal = ShaderManager.getCurrentNormal();
segment.normalX = normal.x;
segment.normalY = normal.y;
segment.normalZ = normal.z;
segment.blockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
batchSegments.add(segment);
batchSealedEnd = end;
}
@@ -499,27 +527,29 @@ public class BatchingFontRenderer {
}
batchDepth--;
if (batchDepth == 0) {
// We finished any nested batches
flushBatch();
// We finished any nested batches.
if (shouldDeferNow()) {
deferBatch();
} else {
flushBatch();
}
}
}
private static boolean shouldDeferNow() {
if (!GLStateManager.isMainThread()) return false;
return TesrBatchRenderer.INSTANCE.hasPendingGeometry() || ModelPartBatcher.INSTANCE.isActive();
}
public void beginDeferredBatch() {
deferCurrentBatch = batchDepth == 0 && TesrBatchRenderer.INSTANCE.hasActivePass() || ModelPartBatcher.INSTANCE.isActive();
if (deferCurrentBatch && deferredIdxPos / 6 > QUAD_BUDGET / 2) {
if (batchDepth == 0 && shouldDeferNow() && deferredIdxPos / 6 > QUAD_BUDGET / 2) {
flushDeferredText();
}
beginBatch();
}
public void endDeferredBatch() {
if (deferCurrentBatch && batchDepth == 1) {
deferCurrentBatch = false;
batchDepth = 0;
deferBatch();
} else {
endBatch();
}
endBatch();
}
private void deferBatch() {
@@ -544,38 +574,123 @@ public class BatchingFontRenderer {
public static void flushDeferredText() {
if (deferredSegments.isEmpty()) return;
if (deferredSegments.getFirst().owner.shouldDrawThroughPipeline()) {
flushDeferredThroughPipeline();
return;
}
GLStateManager.beginForeignDraw();
try {
flushDeferredTextInner();
} finally {
GLStateManager.endForeignDraw();
}
}
private static void flushDeferredThroughPipeline() {
final BatchingFontRenderer first = deferredSegments.getFirst().owner;
final boolean isTextureEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
final boolean isBlendEnabledBefore = GLStateManager.isEffectiveBlendEnabled();
final boolean isAlphaTestEnabledBefore = GLStateManager.isEffectiveAlphaTestEnabled();
GLStateManager.getEffectiveBlendState(deferredBlendStateBefore);
final int boundTextureBefore = GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
final boolean depthMaskBefore = GLStateManager.isEffectiveDepthMaskEnabled();
final int prevBlockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
boolean textureChanged = false;
GLStateManager.enableTexture();
GLStateManager.enableAlphaTest();
GLStateManager.enableBlend();
GLStateManager.tryBlendFuncSeparate(first.blendSrcRGB, first.blendDstRGB, GL11.GL_ONE, GL11.GL_ZERO);
GLStateManager.glDepthMask(false);
final Boolean prevTranslucency = GbufferPrograms.beginTranslucencyDeclaration(Boolean.TRUE);
GbufferPrograms.setBlockEntityDefaults();
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
GLStateManager.glPushMatrix();
try {
for (final TextSegment segment : deferredSegments) {
final BatchingFontRenderer owner = segment.owner;
if (segment.cmdStart == segment.cmdEnd) continue;
GLStateManager.setModelViewMatrix(segment.modelView);
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(segment.blockEntityId);
final FontDrawCmd[] cmdsData = owner.batchCommands.elements();
Arrays.sort(cmdsData, segment.cmdStart, segment.cmdEnd, FontDrawCmd.DRAW_ORDER_COMPARATOR);
textureChanged |= emitRangeThroughPipeline(owner, cmdsData, segment.cmdStart, segment.cmdEnd,
segment.packedLight, segment.normalX, segment.normalY, segment.normalZ);
}
} finally {
GLStateManager.glPopMatrix();
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(prevBlockEntityId);
GbufferPrograms.endTranslucencyDeclaration(prevTranslucency);
GLStateManager.glDepthMask(depthMaskBefore);
if (isTextureEnabledBefore) {
GLStateManager.enableTexture();
} else {
GLStateManager.disableTexture();
}
if (isBlendEnabledBefore) {
GLStateManager.enableBlend();
} else {
GLStateManager.disableBlend();
}
GLStateManager.tryBlendFuncSeparate(deferredBlendStateBefore.getSrcRgb(), deferredBlendStateBefore.getDstRgb(),
deferredBlendStateBefore.getSrcAlpha(), deferredBlendStateBefore.getDstAlpha());
if (isAlphaTestEnabledBefore) {
GLStateManager.enableAlphaTest();
} else {
GLStateManager.disableAlphaTest();
}
if (textureChanged) {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, boundTextureBefore);
}
discardDeferredText();
}
}
private static void flushDeferredTextInner() {
uploadVertexData(deferredVertexPos);
final int prevProgram = GLStateManager.glGetInteger(GL20.GL_CURRENT_PROGRAM);
final boolean isTextureEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
final boolean isBlendEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_BLEND);
final boolean isBlendEnabledBefore = GLStateManager.isEffectiveBlendEnabled();
final boolean isAlphaTestEnabledBefore = GLStateManager.isEffectiveAlphaTestEnabled();
GLStateManager.getEffectiveBlendState(deferredBlendStateBefore);
final int shadeModelBefore = GLStateManager.glGetInteger(GL11.GL_SHADE_MODEL);
final int boundTextureBefore = GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
final int lightmapBindingBefore = boundLightmapTexture();
final boolean depthTestBefore = GLStateManager.getDepthTest().isEnabled();
final boolean depthMaskBefore = GLStateManager.getDepthState().isEnabled();
final boolean depthMaskBefore = GLStateManager.isEffectiveDepthMaskEnabled();
deferredSegments.get(0).owner.setupFontDrawState();
GLStateManager.glDepthMask(false);
flushLastTexture = DUMMY_RESOURCE_LOCATION;
flushTextureChanged = false;
resetFlushLightmap();
try (MemoryStack stack = stackPush()) {
final FloatBuffer mvpBuf = stack.mallocFloat(16);
for (int i = 0, n = deferredSegments.size(); i < n; i++) {
final TextSegment segment = deferredSegments.get(i);
mvpBuf.clear();
segment.mvp.get(mvpBuf);
GLStateManager.glUniformMatrix4(segment.owner.mvpMatrixLocation, false, mvpBuf);
uploadLightmap(segment.owner.lightmapLocation, segment.lightmapActive, segment.lightmapU,
segment.lightmapV, segment.lightmapTexture);
drawCommands(segment.owner.batchCommands.elements(), segment.cmdStart, segment.cmdEnd, segment.owner);
try {
try (MemoryStack stack = stackPush()) {
final FloatBuffer mvpBuf = stack.mallocFloat(16);
for (final TextSegment segment : deferredSegments) {
mvpBuf.clear();
segment.mvp.get(mvpBuf);
GLStateManager.glUniformMatrix4(segment.owner.mvpMatrixLocation, false, mvpBuf);
uploadLightmap(segment.owner.lightmapLocation, segment.lightmapActive, segment.lightmapU,
segment.lightmapV, segment.lightmapTexture);
drawCommands(segment.owner.batchCommands.elements(), segment.cmdStart, segment.cmdEnd, segment.owner);
}
}
} finally {
restoreFontDrawState(prevProgram, isTextureEnabledBefore, isBlendEnabledBefore, boundTextureBefore,
lightmapBindingBefore, isAlphaTestEnabledBefore, deferredBlendStateBefore, shadeModelBefore);
restoreDepth(depthTestBefore, depthMaskBefore);
discardDeferredText();
}
restoreFontDrawState(prevProgram, isTextureEnabledBefore, isBlendEnabledBefore, boundTextureBefore, lightmapBindingBefore);
restoreDepth(depthTestBefore, depthMaskBefore);
discardDeferredText();
}
public static void discardDeferredText() {
@@ -614,18 +729,47 @@ public class BatchingFontRenderer {
}
private static final Matrix4f scratchMvp = new Matrix4f();
private static final Matrix4f scratchModelView = new Matrix4f();
private int fontAAModeLast = -1;
private int fontAAStrengthLast = -1;
private void flushBatch() {
final boolean locked = GLStateManager.acquireDrawLock();
try {
flushBatchInner();
if (shouldDrawThroughPipeline()) {
flushBatchThroughPipeline();
} else {
GLStateManager.beginForeignDraw();
try {
flushBatchInner();
} finally {
GLStateManager.endForeignDraw();
}
}
} finally {
if (locked) GLStateManager.releaseDrawLock();
}
}
private boolean shouldDrawThroughPipeline() {
if (FontConfig.fontAAMode != 0) {
return false;
}
if (!GLStateManager.isMainThread()) {
return false;
}
final PipelineManager pipelines = Iris.getPipelineManagerNullable();
if (pipelines == null) {
return false;
}
return !Tessellator.instance.isDrawing
&& pipelines.getPipelineNullable() instanceof DeferredWorldRenderingPipeline drp
&& drp.shouldOverrideShaders();
}
private void flushBatchInner() {
final int cmdCount = batchCommands.size();
final int segmentCount = batchSegments.size();
@@ -646,11 +790,14 @@ public class BatchingFontRenderer {
Arrays.sort(cmds, batchSealedEnd, cmdCount, FontDrawCmd.DRAW_ORDER_COMPARATOR);
final boolean isTextureEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
final boolean isBlendEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_BLEND);
final boolean isBlendEnabledBefore = GLStateManager.isEffectiveBlendEnabled();
final boolean isAlphaTestEnabledBefore = GLStateManager.isEffectiveAlphaTestEnabled();
GLStateManager.getEffectiveBlendState(blendStateBefore);
final int shadeModelBefore = GLStateManager.glGetInteger(GL11.GL_SHADE_MODEL);
final int boundTextureBefore = GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
final int lightmapBindingBefore = boundLightmapTexture();
final boolean depthTestBefore = GLStateManager.getDepthTest().isEnabled();
final boolean depthMaskBefore = GLStateManager.getDepthState().isEnabled();
final boolean depthMaskBefore = GLStateManager.isEffectiveDepthMaskEnabled();
setupFontDrawState();
flushLastTexture = DUMMY_RESOURCE_LOCATION;
@@ -675,7 +822,8 @@ public class BatchingFontRenderer {
drawCommands(cmds, batchSealedEnd, cmdCount, this);
}
}
restoreFontDrawState(prevProgram, isTextureEnabledBefore, isBlendEnabledBefore, boundTextureBefore, lightmapBindingBefore);
restoreFontDrawState(prevProgram, isTextureEnabledBefore, isBlendEnabledBefore, boundTextureBefore,
lightmapBindingBefore, isAlphaTestEnabledBefore, blendStateBefore, shadeModelBefore);
restoreDepth(depthTestBefore, depthMaskBefore);
truncateBatchToWatermark();
@@ -783,7 +931,9 @@ public class BatchingFontRenderer {
}
}
private static void restoreFontDrawState(int prevProgram, boolean textureEnabledBefore, boolean blendEnabledBefore, int boundTextureBefore, int lightmapBindingBefore) {
private static void restoreFontDrawState(int prevProgram, boolean textureEnabledBefore, boolean blendEnabledBefore,
int boundTextureBefore, int lightmapBindingBefore, boolean alphaTestEnabledBefore, BlendState blendBefore,
int shadeModelBefore) {
GLStateManager.glUseProgram(prevProgram);
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
@@ -792,9 +942,19 @@ public class BatchingFontRenderer {
if (textureEnabledBefore) {
GLStateManager.glEnable(GL11.GL_TEXTURE_2D);
}
if (!blendEnabledBefore) {
if (blendEnabledBefore) {
GLStateManager.enableBlend();
} else {
GLStateManager.disableBlend();
}
GLStateManager.tryBlendFuncSeparate(blendBefore.getSrcRgb(), blendBefore.getDstRgb(),
blendBefore.getSrcAlpha(), blendBefore.getDstAlpha());
if (alphaTestEnabledBefore) {
GLStateManager.enableAlphaTest();
} else {
GLStateManager.disableAlphaTest();
}
GLStateManager.glShadeModel(shadeModelBefore);
if (flushTextureChanged) {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, boundTextureBefore);
}
@@ -803,11 +963,156 @@ public class BatchingFontRenderer {
}
}
private void flushBatchThroughPipeline() {
final int cmdCount = batchCommands.size();
final int segmentCount = batchSegments.size();
if (segmentCount == 0 && (vertexDataPos == deferredVertexPos || cmdCount == deferredCmdWatermark)) {
truncateBatchToWatermark();
return;
}
final boolean isTextureEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
final boolean isBlendEnabledBefore = GLStateManager.isEffectiveBlendEnabled();
final boolean isAlphaTestEnabledBefore = GLStateManager.isEffectiveAlphaTestEnabled();
GLStateManager.getEffectiveBlendState(blendStateBefore);
final int boundTextureBefore = GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
final int prevBlockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
boolean textureChanged = false;
GLStateManager.enableTexture();
GLStateManager.enableAlphaTest();
GLStateManager.enableBlend();
GLStateManager.tryBlendFuncSeparate(blendSrcRGB, blendDstRGB, GL11.GL_ONE, GL11.GL_ZERO);
final Boolean prevTranslucency = GbufferPrograms.beginTranslucencyDeclaration(Boolean.TRUE);
GbufferPrograms.setBlockEntityDefaults();
final FontDrawCmd[] cmdsData = batchCommands.elements();
for (int i = 0; i < segmentCount; i++) {
final TextSegment segment = batchSegments.get(i);
Arrays.sort(cmdsData, segment.cmdStart, segment.cmdEnd, FontDrawCmd.DRAW_ORDER_COMPARATOR);
}
Arrays.sort(cmdsData, batchSealedEnd, cmdCount, FontDrawCmd.DRAW_ORDER_COMPARATOR);
// The unsealed tail still belongs to the modelview live at flush time; capture it before the segments move it.
resolveModelView(scratchModelView);
GLStateManager.glMatrixMode(GL11.GL_MODELVIEW);
GLStateManager.glPushMatrix();
try {
for (int i = 0; i < segmentCount; i++) {
final TextSegment segment = batchSegments.get(i);
if (segment.cmdStart == segment.cmdEnd) continue;
GLStateManager.setModelViewMatrix(segment.modelView);
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(segment.blockEntityId);
textureChanged |= emitRangeThroughPipeline(this, cmdsData, segment.cmdStart, segment.cmdEnd,
segment.packedLight, segment.normalX, segment.normalY, segment.normalZ);
}
if (batchSealedEnd < cmdCount) {
GLStateManager.setModelViewMatrix(scratchModelView);
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(prevBlockEntityId);
final Vector3f normal = ShaderManager.getCurrentNormal();
textureChanged |= emitRangeThroughPipeline(this, cmdsData, batchSealedEnd, cmdCount,
GLSMConfig.packedLastBrightness(), normal.x, normal.y, normal.z);
}
} finally {
GLStateManager.glPopMatrix();
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(prevBlockEntityId);
GbufferPrograms.endTranslucencyDeclaration(prevTranslucency);
if (isTextureEnabledBefore) {
GLStateManager.enableTexture();
} else {
GLStateManager.disableTexture();
}
if (isBlendEnabledBefore) {
GLStateManager.enableBlend();
} else {
GLStateManager.disableBlend();
}
GLStateManager.tryBlendFuncSeparate(blendStateBefore.getSrcRgb(), blendStateBefore.getDstRgb(),
blendStateBefore.getSrcAlpha(), blendStateBefore.getDstAlpha());
if (isAlphaTestEnabledBefore) {
GLStateManager.enableAlphaTest();
} else {
GLStateManager.disableAlphaTest();
}
if (textureChanged) {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, boundTextureBefore);
}
truncateBatchToWatermark();
}
}
/** Replays one sealed command range through the Tessellator so Iris sees it as ordinary geometry. */
private static boolean emitRangeThroughPipeline(BatchingFontRenderer owner, FontDrawCmd[] cmdsData, int from, int to,
int packedLight, float normalX, float normalY, float normalZ) {
final Tessellator tessellator = Tessellator.instance;
ResourceLocation lastTexture = DUMMY_RESOURCE_LOCATION;
boolean textureChanged = false;
boolean drawing = false;
try {
for (int i = from; i < to; i++) {
final FontDrawCmd cmd = cmdsData[i];
if (!Objects.equals(lastTexture, cmd.texture)) {
if (drawing) {
tessellator.draw();
drawing = false;
}
if (cmd.texture == null) {
GLStateManager.disableTexture();
} else {
GLStateManager.enableTexture();
((FontRendererAccessor) owner.underlying).angelica$bindTexture(cmd.texture);
textureChanged = true;
}
lastTexture = cmd.texture;
}
if (!drawing) {
tessellator.startDrawingQuads();
tessellator.setBrightness(packedLight);
tessellator.setNormal(normalX, normalY, normalZ);
drawing = true;
}
owner.emitCommand(tessellator, cmd);
}
} finally {
if (drawing) {
tessellator.draw();
}
}
return textureChanged;
}
private void emitCommand(Tessellator tessellator, FontDrawCmd cmd) {
final int firstVertex = cmd.startVtx / 6 * 4;
final int quadCount = cmd.idxCount / 6;
for (int q = 0; q < quadCount; q++) {
final long base = vertexDataAddress + (long) (firstVertex + q * 4) * VERTEX_SIZE;
emitVertex(tessellator, base);
emitVertex(tessellator, base + VERTEX_SIZE);
emitVertex(tessellator, base + 3L * VERTEX_SIZE);
emitVertex(tessellator, base + 2L * VERTEX_SIZE);
}
}
private void emitVertex(Tessellator tessellator, long ptr) {
tessellator.setColorRGBA(
memGetByte(ptr + 16) & 0xFF,
memGetByte(ptr + 17) & 0xFF,
memGetByte(ptr + 18) & 0xFF,
memGetByte(ptr + 19) & 0xFF);
tessellator.addVertexWithUV(memGetFloat(ptr), memGetFloat(ptr + 4), 0.0D,
memGetFloat(ptr + 8), memGetFloat(ptr + 12));
}
private static void restoreDepth(boolean testBefore, boolean maskBefore) {
if (testBefore != GLStateManager.getDepthTest().isEnabled()) {
if (testBefore) GLStateManager.enableDepthTest(); else GLStateManager.disableDepthTest();
}
if (maskBefore != GLStateManager.getDepthState().isEnabled()) {
if (maskBefore != GLStateManager.isEffectiveDepthMaskEnabled()) {
GLStateManager.glDepthMask(maskBefore);
}
}
@@ -865,7 +1170,7 @@ public class BatchingFontRenderer {
}
private static int hsvToRgb(float hue, float sat, float val) {
int h = (int)(hue / 60f) % 6;
int h = (int) (hue / 60f) % 6;
float f = hue / 60f - h;
float p = val * (1 - sat);
float q = val * (1 - f * sat);
@@ -879,11 +1184,12 @@ public class BatchingFontRenderer {
case 4: r=t; g=p; b=val; break;
default: r=val; g=p; b=q; break;
}
return ((int)(r*255) << 16) | ((int)(g*255) << 8) | (int)(b*255);
return ((int) (r * 255) << 16) | ((int) (g * 255) << 8) | (int) (b * 255);
}
private static final int RAINBOW_LUT_SIZE = 24;
private static final int[] RAINBOW_LUT = new int[RAINBOW_LUT_SIZE];
static {
for (int i = 0; i < RAINBOW_LUT_SIZE; i++) {
RAINBOW_LUT[i] = hsvToRgb(i * 15f, 1f, 1f);
@@ -918,7 +1224,7 @@ public class BatchingFontRenderer {
private static final float WAVE_FREQUENCY = 0.5f;
public float drawString(final float anchorX, final float anchorY, final int color, final boolean enableShadow,
final boolean unicodeFlag, final CharSequence string, int stringOffset, int stringLength) {
final boolean unicodeFlag, final CharSequence string, int stringOffset, int stringLength) {
// noinspection SizeReplaceableByIsEmpty
if (string == null || string.length() == 0) {
return anchorX + (enableShadow ? 1.0f : 0.0f);
@@ -1007,86 +1313,86 @@ public class BatchingFontRenderer {
charIdx += SECTION_X_PAYLOAD;
}
} else {
final boolean is09 = charInRange(fmtCode, '0', '9');
final boolean isAF = charInRange(fmtCode, 'a', 'f');
if (is09 || isAF) {
curRandom = false;
curBold = false;
curStrikethrough = false;
curUnderline = false;
curItalic = false;
curRainbow = false;
curGradient = false;
// wave/dinnerbone NOT reset — they're positional effects, independent of color
final int colorIdx = is09 ? (fmtCode - '0') : (fmtCode - 'a' + 10);
final int rgb = this.colorCode[colorIdx];
curColor = (curColor & 0xFF000000) | (rgb & 0x00FFFFFF);
final int shadowRgb = this.colorCode[colorIdx + 16];
curShadowColor = (curShadowColor & 0xFF000000) | (shadowRgb & 0x00FFFFFF);
} else if (fmtCode == 'k') {
curRandom = true;
} else if (fmtCode == 'l') {
curBold = true;
} else if (fmtCode == 'm') {
curStrikethrough = true;
strikethroughStartX = curX - 1.0f;
strikethroughEndX = strikethroughStartX;
} else if (fmtCode == 'n') {
curUnderline = true;
underlineStartX = curX - 1.0f;
underlineEndX = underlineStartX;
} else if (fmtCode == 'o') {
curItalic = true;
} else if (fmtCode == 'q' && AngelicaConfig.enableRainbow) {
curRainbow = true;
curGradient = false;
rainbowCharIndex = 0;
} else if (fmtCode == 'z' && AngelicaConfig.enableWaveText) {
curWave = !curWave;
} else if (fmtCode == 'v' && AngelicaConfig.enableDinnerboneText) {
curDinnerbone = !curDinnerbone;
} else if (fmtCode == 'u' && AngelicaConfig.enableDropShadow) {
int customRgb = (charIdx + SECTION_X_LENGTH < stringEnd)
? ColorCodeUtils.parseSectionXAt(string, charIdx + 1)
: -1;
if (customRgb != -1) {
curShadow = true;
curShadowCustomColor = true;
curShadowColorOverride = customRgb;
charIdx += SECTION_X_LENGTH;
} else {
curShadow = !curShadow;
if (!curShadow) curShadowCustomColor = false;
}
} else if (fmtCode == 'g' && AngelicaConfig.enableGradients && charIdx + GRADIENT_PAYLOAD < stringEnd) {
int color1 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1);
int color2 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1 + SECTION_X_LENGTH);
if (color1 != -1 && color2 != -1) {
curGradient = true;
final boolean is09 = charInRange(fmtCode, '0', '9');
final boolean isAF = charInRange(fmtCode, 'a', 'f');
if (is09 || isAF) {
curRandom = false;
curBold = false;
curStrikethrough = false;
curUnderline = false;
curItalic = false;
curRainbow = false;
gradientStartRgb = color1;
gradientEndRgb = color2;
gradientCharIndex = 0;
gradientTotalChars = countVisibleChars(string, charIdx + 1 + GRADIENT_PAYLOAD, stringEnd);
gradientStep = gradientTotalChars > 1 ? 1f / (gradientTotalChars - 1) : 0f;
charIdx += GRADIENT_PAYLOAD;
curGradient = false;
// wave/dinnerbone NOT reset — they're positional effects, independent of color
final int colorIdx = is09 ? (fmtCode - '0') : (fmtCode - 'a' + 10);
final int rgb = this.colorCode[colorIdx];
curColor = (curColor & 0xFF000000) | (rgb & 0x00FFFFFF);
final int shadowRgb = this.colorCode[colorIdx + 16];
curShadowColor = (curShadowColor & 0xFF000000) | (shadowRgb & 0x00FFFFFF);
} else if (fmtCode == 'k') {
curRandom = true;
} else if (fmtCode == 'l') {
curBold = true;
} else if (fmtCode == 'm') {
curStrikethrough = true;
strikethroughStartX = curX - 1.0f;
strikethroughEndX = strikethroughStartX;
} else if (fmtCode == 'n') {
curUnderline = true;
underlineStartX = curX - 1.0f;
underlineEndX = underlineStartX;
} else if (fmtCode == 'o') {
curItalic = true;
} else if (fmtCode == 'q' && AngelicaConfig.enableRainbow) {
curRainbow = true;
curGradient = false;
rainbowCharIndex = 0;
} else if (fmtCode == 'z' && AngelicaConfig.enableWaveText) {
curWave = !curWave;
} else if (fmtCode == 'v' && AngelicaConfig.enableDinnerboneText) {
curDinnerbone = !curDinnerbone;
} else if (fmtCode == 'u' && AngelicaConfig.enableDropShadow) {
int customRgb = (charIdx + SECTION_X_LENGTH < stringEnd)
? ColorCodeUtils.parseSectionXAt(string, charIdx + 1)
: -1;
if (customRgb != -1) {
curShadow = true;
curShadowCustomColor = true;
curShadowColorOverride = customRgb;
charIdx += SECTION_X_LENGTH;
} else {
curShadow = !curShadow;
if (!curShadow) curShadowCustomColor = false;
}
} else if (fmtCode == 'g' && AngelicaConfig.enableGradients && charIdx + GRADIENT_PAYLOAD < stringEnd) {
int color1 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1);
int color2 = ColorCodeUtils.parseSectionXAt(string, charIdx + 1 + SECTION_X_LENGTH);
if (color1 != -1 && color2 != -1) {
curGradient = true;
curRainbow = false;
gradientStartRgb = color1;
gradientEndRgb = color2;
gradientCharIndex = 0;
gradientTotalChars = countVisibleChars(string, charIdx + 1 + GRADIENT_PAYLOAD, stringEnd);
gradientStep = gradientTotalChars > 1 ? 1f / (gradientTotalChars - 1) : 0f;
charIdx += GRADIENT_PAYLOAD;
}
} else if (fmtCode == 'r') {
curRandom = false;
curBold = false;
curStrikethrough = false;
curUnderline = false;
curItalic = false;
curRainbow = false;
curWave = false;
curDinnerbone = false;
curGradient = false;
curShadow = false;
curShadowCustomColor = false;
curColor = color;
curShadowColor = shadowColor;
}
} else if (fmtCode == 'r') {
curRandom = false;
curBold = false;
curStrikethrough = false;
curUnderline = false;
curItalic = false;
curRainbow = false;
curWave = false;
curDinnerbone = false;
curGradient = false;
curShadow = false;
curShadowCustomColor = false;
curColor = color;
curShadowColor = shadowColor;
}
} // close else block for non-§x codes
continue;
@@ -1130,9 +1436,9 @@ public class BatchingFontRenderer {
}
if (curGradient && gradientTotalChars > 0) {
float t = Math.min(gradientCharIndex * gradientStep, 1f);
int gr = (int)((gradientStartRgb >> 16 & 0xFF) * (1-t) + (gradientEndRgb >> 16 & 0xFF) * t);
int gg = (int)((gradientStartRgb >> 8 & 0xFF) * (1-t) + (gradientEndRgb >> 8 & 0xFF) * t);
int gb = (int)((gradientStartRgb & 0xFF) * (1-t) + (gradientEndRgb & 0xFF) * t);
int gr = (int) ((gradientStartRgb >> 16 & 0xFF) * (1 - t) + (gradientEndRgb >> 16 & 0xFF) * t);
int gg = (int) ((gradientStartRgb >> 8 & 0xFF) * (1 - t) + (gradientEndRgb >> 8 & 0xFF) * t);
int gb = (int) ((gradientStartRgb & 0xFF) * (1 - t) + (gradientEndRgb & 0xFF) * t);
int rgbEffect = (gr << 16) | (gg << 8) | gb;
curColor = (curColor & 0xFF000000) | rgbEffect;
curShadowColor = (curShadowColor & 0xFF000000) | ((rgbEffect & 0xFCFCFC) >> 2);
@@ -1155,7 +1461,7 @@ public class BatchingFontRenderer {
// Wave: Y offset via sine wave
float renderY = heightNorth;
if (curWave) {
float time = HUDCaching.renderingCacheOverride ? 0f : (float)((System.nanoTime() & 0xFFFFFFFFFFFFL) * WAVE_TIME_SCALE);
float time = HUDCaching.renderingCacheOverride ? 0f : (float) ((System.nanoTime() & 0xFFFFFFFFFFFFL) * WAVE_TIME_SCALE);
renderY += (float) Math.sin(visibleCharIndex * WAVE_FREQUENCY + time) * AngelicaConfig.waveAmplitude;
}
@@ -15,6 +15,7 @@ import net.coderbot.batchedentityrendering.impl.BlendingStateHolder;
import net.coderbot.batchedentityrendering.impl.TransparencyType;
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
import net.coderbot.iris.layer.GbufferPrograms;
import net.coderbot.iris.layer.PassOverride;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
@@ -68,11 +69,9 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
return TRANSLUCENT;
}
private static MultiPhaseParameters.Builder tesrMaterialPhases(ResourceLocation texture, TesrMaterial material) {
private static MultiPhaseParameters.Builder tesrMaterialPhases(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits) {
final MultiPhaseParameters.Builder b = MultiPhaseParameters.builder();
if (texture != null) {
b.texture(new RenderPhase.Texture(texture, false, false));
}
b.texture(texture != null ? new RenderPhase.Texture(texture, false, false) : NO_TEXTURE);
if (material.isNoCull()) {
b.cull(DISABLE_CULLING);
}
@@ -85,19 +84,16 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
if (material.isDepthOnly()) {
b.writeMaskState(DEPTH_MASK);
}
if (material.cutoutAlpha() > 0) {
b.alpha(new RenderPhase.Alpha(material.cutoutAlpha()));
}
if (material.isDepthEqual()) {
b.depthTest(EQUAL_DEPTH_TEST);
}
b.alpha(material.cutoutAlpha() > 0 ? new RenderPhase.Alpha(material.cutoutAlpha()) : ZERO_ALPHA);
b.depthTest(material.isDepthEqual() ? EQUAL_DEPTH_TEST : LEQUAL_DEPTH_TEST);
switch (material.transparency()) {
case TRANSLUCENT -> b.transparency(TRANSLUCENT_TRANSPARENCY);
case ADDITIVE -> b.transparency(ADDITIVE_TRANSPARENCY);
case ADDITIVE_ALPHA -> b.transparency(LIGHTNING_TRANSPARENCY);
case GLINT -> b.transparency(GLINT_TRANSPARENCY);
case OPAQUE -> {}
case OPAQUE -> b.transparency(NO_TRANSPARENCY);
}
boolean shaderPhaseTaken = true;
switch (material.special()) {
case GLINT -> b.shader(SPECIAL_GLINT);
case BEACON_BEAM -> b.shader(SPECIAL_BEACON_BEAM);
@@ -105,9 +101,15 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
final TesrShader shader = material.shader();
if (shader != null) {
b.shader(new RenderPhase.Shader("angelica_tesr_shader_" + shader.name(), shader.bind(), shader.release()));
} else {
shaderPhaseTaken = false;
}
}
}
if (!shaderPhaseTaken && pass != PassOverride.NONE) {
b.shader(new RenderPhase.Shader("angelica_tesr_pass" + pass.nameSuffix(), pass::apply, pass::clear));
}
b.layering(polygonOffset(offsetFactor, offsetUnits));
return b;
}
@@ -119,19 +121,21 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
GbufferPrograms::teardownSpecialRenderCondition);
public static RenderLayer tesr(ResourceLocation texture, TesrMaterial material) {
final MultiPhaseParameters.Builder b = tesrMaterialPhases(texture, material).shadeModel(SMOOTH_SHADE_MODEL).lightmap(ENABLE_LIGHTMAP);
if (material.transparency() == TesrMaterial.Transparency.TRANSLUCENT) {
b.target(TRANSLUCENT_TARGET);
}
return tesr(texture, material, PassOverride.NONE, 0.0f, 0.0f);
}
public static RenderLayer tesr(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits) {
final MultiPhaseParameters.Builder b = tesrMaterialPhases(texture, material, pass, offsetFactor, offsetUnits).shadeModel(SMOOTH_SHADE_MODEL);
final boolean sortsTranslucent = material.transparency() != TesrMaterial.Transparency.OPAQUE;
return of("angelica_tesr_" + material.transparency().name().toLowerCase(Locale.ROOT),
return of("angelica_tesr_" + material.transparency().name().toLowerCase(Locale.ROOT) + pass.nameSuffix()
+ (offsetFactor == 0.0f && offsetUnits == 0.0f ? "" : "_offset" + offsetFactor + "_" + offsetUnits),
BatchVertexFormats.POSITION_COLOR_TEXTURE_LIGHTF_NORMAL, GL11.GL_QUADS, 65536, true, sortsTranslucent, b.build(false));
}
public static RenderLayer tesrNoPass(ResourceLocation texture, TesrMaterial material) {
return of("angelica_tesr_nopass_" + material.transparency().name().toLowerCase(Locale.ROOT),
BatchVertexFormats.POSITION_COLOR_TEXTURE_LIGHTF_NORMAL, GL11.GL_QUADS, 65536, true, false,
tesrMaterialPhases(texture, material).build(false));
tesrMaterialPhases(texture, material, PassOverride.NONE, 0.0f, 0.0f).build(false));
}
public static RenderLayer getOutline(ResourceLocation texture, RenderPhase.Cull cull) {
@@ -154,7 +158,7 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
@Override
public TransparencyType getTransparencyType() {
return phases.transparency.getTransparencyType();
return phases.transparency == null ? TransparencyType.OPAQUE : phases.transparency.getTransparencyType();
}
private MultiPhase(String name, VertexFormat vertexFormat, int drawMode, int expectedBufferSize, boolean hasCrumbling, boolean translucent, MultiPhaseParameters phases) {
@@ -170,7 +174,7 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
}
});
this.phases = phases;
this.affectedOutline = phases.outlineMode == RenderLayer.OutlineMode.AFFECTS_OUTLINE ? phases.texture.getId().map((arg2) -> {
this.affectedOutline = phases.outlineMode == RenderLayer.OutlineMode.AFFECTS_OUTLINE && phases.texture != null ? phases.texture.getId().map((arg2) -> {
return getOutline(arg2, phases.cull);
}) : Optional.empty();
this.hash = Objects.hash(new Object[]{super.hashCode(), phases});
@@ -187,7 +191,7 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
@Override
public ResourceLocation getTextureId() {
return phases.texture.getId().orElse(null);
return phases.texture == null ? null : phases.texture.getId().orElse(null);
}
@Override
@@ -223,7 +227,9 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
if (arg == arg2) {
return true;
} else {
return arg != null && arg2 != null ? Objects.equals(arg.phases, arg2.phases) : false;
return arg != null && arg2 != null && arg.name.equals(arg2.name)
&& arg.getVertexFormat() == arg2.getVertexFormat() && arg.getDrawMode() == arg2.getDrawMode()
&& Objects.equals(arg.phases, arg2.phases);
}
}
}
@@ -263,7 +269,13 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
this.writeMaskState = writeMaskState;
this.shader = shader;
this.outlineMode = outlineMode;
this.phases = ImmutableList.of(this.texture, this.transparency, this.diffuseLighting, this.shadeModel, this.alpha, this.depthTest, this.cull, this.lightmap, this.fog, this.layering, this.target, this.texturing, this.writeMaskState, this.shader);
final ImmutableList.Builder<RenderPhase> declared = ImmutableList.builder();
for (RenderPhase phase : new RenderPhase[] { this.texture, this.transparency, this.diffuseLighting,
this.shadeModel, this.alpha, this.depthTest, this.cull, this.lightmap, this.fog, this.layering,
this.target, this.texturing, this.writeMaskState, this.shader }) {
if (phase != null) declared.add(phase);
}
this.phases = declared.build();
}
@Override
@@ -308,22 +320,7 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
private RenderPhase.WriteMaskState writeMaskState;
private RenderPhase.Shader shader;
private Builder() {
this.texture = RenderPhase.NO_TEXTURE ;
this.transparency = RenderPhase.NO_TRANSPARENCY;
this.diffuseLighting = RenderPhase.DISABLE_DIFFUSE_LIGHTING;
this.shadeModel = RenderPhase.SHADE_MODEL;
this.alpha = RenderPhase.ZERO_ALPHA;
this.depthTest = RenderPhase.LEQUAL_DEPTH_TEST;
this.cull = RenderPhase.ENABLE_CULLING;
this.lightmap = RenderPhase.DISABLE_LIGHTMAP;
this.fog = RenderPhase.FOG;
this.layering = RenderPhase.NO_LAYERING;
this.target = RenderPhase.MAIN_TARGET;
this.texturing = RenderPhase.DEFAULT_TEXTURING;
this.writeMaskState = RenderPhase.ALL_MASK;
this.shader = RenderPhase.NO_SHADER;
}
private Builder() {}
public Builder texture(RenderPhase.Texture texture) {
this.texture = texture;
@@ -335,6 +332,10 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
return this;
}
public void layering(Layering layering) {
this.layering = layering;
}
public Builder shadeModel(RenderPhase.ShadeModel shadeModel) {
this.shadeModel = shadeModel;
return this;
@@ -9,7 +9,9 @@ import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Optional;
@@ -77,6 +79,7 @@ public abstract class RenderPhase {
protected static final WriteMaskState COLOR_MASK;
protected static final WriteMaskState DEPTH_MASK;
protected static final Layering NO_LAYERING;
protected static final Fog NO_FOG;
protected static final Fog FOG;
protected static final Fog BLACK_FOG;
@@ -85,6 +88,22 @@ public abstract class RenderPhase {
protected static final Target TRANSLUCENT_TARGET;
protected static final Shader NO_SHADER = new Shader("no_shader", () -> {}, () -> {});
private static final Map<Long, Layering> POLYGON_OFFSET_LAYERINGS = new ConcurrentHashMap<>();
public static Layering polygonOffset(float factor, float units) {
if (factor == 0.0f && units == 0.0f) {
return NO_LAYERING;
}
final long key = ((long) Float.floatToIntBits(factor) << 32) | (Float.floatToIntBits(units) & 0xFFFFFFFFL);
return POLYGON_OFFSET_LAYERINGS.computeIfAbsent(key, k -> new Layering("polygon_offset_" + factor + "_" + units, () -> {
GLStateManager.glEnable(GL11.GL_POLYGON_OFFSET_FILL);
GLStateManager.glPolygonOffset(factor, units);
}, () -> {
GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL);
GLStateManager.glPolygonOffset(0.0f, 0.0f);
}));
}
public RenderPhase(String name, Runnable beginAction, Runnable endAction) {
this.name = name;
this.beginAction = beginAction;
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.config;
import com.gtnewhorizon.gtnhlib.config.Config;
import com.gtnewhorizons.angelica.rendering.culling.GpuCulling;
import net.coderbot.iris.shadows.ShadowGraphGate;
@Config(modid = "angelica", filename = "angelica-modules")
public class AngelicaConfig {
@@ -347,6 +348,20 @@ public class AngelicaConfig {
@Config.DefaultBoolean(true)
public static boolean shadowSkipInMeshTileEntities;
@Config.Comment({
"How much a celestial body can move before the shadow map can update.",
"",
"Note this option is disabled when a shader pack utilizes voxelization features."
})
@Config.DefaultFloat(ShadowGraphGate.DEFAULT_ANGLE_DELTA_DEGREES)
@Config.RangeFloat(min = 0f, max = 0.1f)
public static float shadowGraphAngleDelta;
@Config.Comment("Adjusts the rate the shadow map rebuilds when when a celestial body is near the horizon. Rate is sinusoidal")
@Config.DefaultFloat(ShadowGraphGate.DEFAULT_HORIZON_SCALE)
@Config.RangeFloat(min = 0f, max = 1.0f)
public static float shadowGraphHorizonScale;
@Config.Comment("ASM transformer exclusion narrowing for mod compatibility. Disable per-mod if narrowing causes class loading issues.")
public static TransformerCompat transformerCompat = new TransformerCompat();
@@ -56,7 +56,9 @@ public class FontConfig {
@Config.RangeFloat(min = -2F, max = 2F)
public static float glyphSpacing;
@Config.Comment("Controls font antialiasing. 0 = none, 1 = 4x MSAA, 2 = 16x MSAA.")
@Config.Comment({
"Controls font antialiasing. 0 = none, 1 = 4x MSAA, 2 = 16x MSAA.",
"Turning off AA lets shaders pick up the text."})
@Config.DefaultInt(2)
@Config.RangeInt(min = 0, max = 2)
public static int fontAAMode;
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.debug.flyby;
import com.gtnewhorizons.angelica.config.SystemProperties;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import com.gtnewhorizons.angelica.rendering.FramePacer;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import net.minecraft.client.Minecraft;
@@ -57,6 +58,10 @@ public final class FlybyRunner {
private long plotTurning;
private int lastPhase = -1;
private long warmupSection;
private long runSection;
private long legSection;
private long[] frameTimesNs = NO_FRAMES;
private int frameCount;
private boolean framesTruncated;
@@ -180,9 +185,13 @@ public final class FlybyRunner {
this.buildPath();
this.freezeTimeAndWeather(mc);
this.state = this.warmupTicks > 0 ? State.WARMUP : State.RUNNING;
this.tick = 0;
if (this.state == State.RUNNING) this.beginMeasuring();
if (this.warmupTicks > 0) {
this.state = State.WARMUP;
this.warmupSection = Tracy.sectionEnter(Tracy.SECTION_BENCHMARK, "flyby warmup");
} else {
this.beginMeasuring();
}
LOGGER.info("Flyby {} starting at {} {} {} yaw {}", this.route.id(),
this.originX, this.originY, this.originZ, this.originYaw);
@@ -252,12 +261,18 @@ public final class FlybyRunner {
final int phase = (leg << 1) | turning;
if (phase != this.lastPhase) {
this.lastPhase = phase;
Tracy.message("flyby " + (turning != 0 ? "turn" : "leg") + " " + leg);
final String label = "flyby " + (turning != 0 ? "turn" : "leg") + " " + leg;
Tracy.message(label);
Tracy.sectionLeave(this.legSection);
this.legSection = Tracy.sectionEnter(Tracy.SECTION_BENCHMARK, label);
}
}
private void beginMeasuring() {
this.state = State.RUNNING;
Tracy.sectionLeave(this.warmupSection);
this.warmupSection = 0L;
if (Tracy.ENABLED) this.runSection = Tracy.sectionEnter(Tracy.SECTION_BENCHMARK, "flyby " + this.route.id());
this.plotLeg = Tracy.plotHandle("flyby.leg");
this.plotTurning = Tracy.plotHandle("flyby.turning");
this.lastPhase = -1;
@@ -265,11 +280,10 @@ public final class FlybyRunner {
this.frameCount = 0;
this.lastFrameNs = 0L;
this.runStartNs = System.nanoTime();
Tracy.message("flyby start route=" + this.route.id()
+ " length=" + this.runLength + this.route.lengthUnit()
+ " speed=" + this.route.speedOr(this.speed) + "b/t"
+ " ticks=" + this.runTicks
+ " sdlgpu=" + SystemProperties.USE_SDL_GPU);
FramePacer.beginStats();
if (Tracy.ENABLED) {
Tracy.message("flyby start route=" + this.route.id() + " length=" + this.runLength + this.route.lengthUnit() + " speed=" + this.route.speedOr(this.speed) + "b/t ticks=" + this.runTicks + " sdlgpu=" + SystemProperties.USE_SDL_GPU);
}
}
private void holdPosition(EntityClientPlayerMP player) {
@@ -364,10 +378,15 @@ public final class FlybyRunner {
this.state = State.SETTLE;
this.tick = 0;
final long elapsedNs = System.nanoTime() - this.runStartNs;
Tracy.message("flyby end route=" + this.route.id() + " frames=" + this.frameCount);
Tracy.sectionLeave(this.legSection);
this.legSection = 0L;
Tracy.sectionLeave(this.runSection);
this.runSection = 0L;
if (Tracy.ENABLED) Tracy.message("flyby end route=" + this.route.id() + " frames=" + this.frameCount);
final String summary = this.summarise(elapsedNs, player);
LOGGER.info(summary);
LOGGER.info(FramePacer.endStats());
if (mc.thePlayer != null) {
mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.AQUA + "[Angelica] " + EnumChatFormatting.WHITE + summary));
}
@@ -422,6 +441,13 @@ public final class FlybyRunner {
if (this.isActive()) {
LOGGER.info("Flyby cancelled");
this.state = State.DONE;
Tracy.sectionLeave(this.warmupSection);
this.warmupSection = 0L;
Tracy.sectionLeave(this.legSection);
this.legSection = 0L;
Tracy.sectionLeave(this.runSection);
this.runSection = 0L;
FramePacer.endStats();
}
}
@@ -10,6 +10,9 @@ import com.gtnewhorizons.angelica.glsm.hooks.ShaderTransformPostProcessor;
import com.gtnewhorizons.angelica.glsm.shader.ShaderType;
import org.taumc.glsl.grammar.GLSLParser;
import com.gtnewhorizons.angelica.glsm.hooks.ShaderWorkSubmitter;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaBooleanLayer;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.sdlgpu.SDLGPUGate;
import net.coderbot.iris.Iris;
import java.util.concurrent.CompletableFuture;
@@ -38,7 +41,20 @@ public class IrisGLSMBridge {
private static Runnable fogDensityListener = null;
private static Runnable colorModulatorListener = null;
private static boolean inputsDeferred = false;
private static boolean blendDeferred = false;
private static boolean programRestoreDeferred = false;
private static final Int2IntOpenHashMap programLastUpdatedFrame = new Int2IntOpenHashMap();
private static void refreshBlendCondition() {
if (Iris.getPipelineManager().getPipelineNullable() instanceof DeferredWorldRenderingPipeline drp) {
drp.onVanillaBlendChanged();
}
}
static {
programLastUpdatedFrame.defaultReturnValue(-1);
StateUpdateNotifiers.alphaFuncNotifier = listener -> alphaFuncListener = listener;
@@ -72,6 +88,44 @@ public class IrisGLSMBridge {
};
}
private static VanillaBooleanLayer gated(VanillaBooleanLayer layer) {
return new VanillaBooleanLayer() {
@Override
public boolean isOverrideHeld() {
return Iris.enabled && layer.isOverrideHeld();
}
@Override
public boolean getVanilla() {
return layer.getVanilla();
}
@Override
public void setVanilla(boolean enabled) {
layer.setVanilla(enabled);
}
};
}
private static <T> VanillaStateLayer<T> gated(VanillaStateLayer<T> layer) {
return new VanillaStateLayer<>() {
@Override
public boolean isOverrideHeld() {
return Iris.enabled && layer.isOverrideHeld();
}
@Override
public void readVanilla(T into) {
layer.readVanilla(into);
}
@Override
public void writeVanilla(T from) {
layer.writeVanilla(from);
}
};
}
public static void register() {
GLSMConfig.expandVertexFormats = Iris.enabled;
IrisSamplers.initRenderer();
@@ -88,6 +142,11 @@ public class IrisGLSMBridge {
return Iris.enabled && BlendModeStorage.isBlendLocked();
}
@Override
public boolean isOverrideHeld() {
return Iris.enabled && BlendModeStorage.isOverrideHeld();
}
@Override
public void deferBlendModeToggle(boolean enabled) {
BlendModeStorage.deferBlendModeToggle(enabled);
@@ -104,6 +163,13 @@ public class IrisGLSMBridge {
}
};
GLStateManager.getBlendMode().setVanillaLayer(gated(BlendModeStorage.ENABLE_LAYER));
GLStateManager.getBlendState().setVanillaLayer(gated(BlendModeStorage.FUNC_LAYER));
GLStateManager.getAlphaTest().setVanillaLayer(gated(AlphaTestStorage.ENABLE_LAYER));
GLStateManager.getAlphaState().setVanillaLayer(gated(AlphaTestStorage.FUNC_LAYER));
GLStateManager.getDepthState().setVanillaLayer(gated(DepthColorStorage.DEPTH_LAYER));
GLStateManager.getColorMask().setVanillaLayer(gated(DepthColorStorage.COLOR_LAYER));
GLSMHooks.alphaHandler = new DeferredAlphaHandler() {
@Override
public boolean isAlphaTestLocked() {
@@ -127,6 +193,11 @@ public class IrisGLSMBridge {
return Iris.enabled && DepthColorStorage.isDepthColorLocked();
}
@Override
public boolean isOverrideHeld() {
return Iris.enabled && DepthColorStorage.isOverrideHeld();
}
@Override
public void deferDepthEnable(boolean enabled) {
DepthColorStorage.deferDepthEnable(enabled);
@@ -190,9 +261,41 @@ public class IrisGLSMBridge {
StateTracker.INSTANCE.lightmapSampler = event.enabled;
updatePipeline = true;
}
if (updatePipeline) {
if (!updatePipeline) return;
if (GLStateManager.isForeignDraw()) {
inputsDeferred = true;
return;
}
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setInputs(StateTracker.INSTANCE.getInputs()));
});
GLSMHooks.VANILLA_BLEND_CHANGE.addListener(event -> {
if (!Iris.enabled) return;
if (GLStateManager.isForeignDraw()) {
blendDeferred = true;
return;
}
refreshBlendCondition();
});
GLSMHooks.FOREIGN_DRAW_END.addListener(event -> {
if (!Iris.enabled) return;
if (inputsDeferred) {
inputsDeferred = false;
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setInputs(StateTracker.INSTANCE.getInputs()));
}
if (blendDeferred) {
blendDeferred = false;
refreshBlendCondition();
}
if (programRestoreDeferred) {
programRestoreDeferred = false;
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
if (pipeline instanceof DeferredWorldRenderingPipeline drp && drp.shouldOverrideShaders()) {
drp.restorePassAfterModProgram(GLStateManager.getActiveProgram());
}
}
});
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
@@ -208,15 +311,29 @@ public class IrisGLSMBridge {
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
if (!(pipeline instanceof DeferredWorldRenderingPipeline drp)) return;
if (!drp.shouldOverrideShaders()) return;
if (drp.getActivePassProgramId() == -1) return;
DepthColorStorage.unlockDepthColor();
if (DepthColorStorage.isOwnedProgram(event.newProgram)) {
DepthColorStorage.unlockDepthColor();
} else {
if (event.newProgram != 0 && !DepthColorStorage.isOwnedProgram(event.newProgram)) {
drp.onModProgramOverride();
}
});
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
if (!Iris.enabled) return;
if (!event.postBind) return;
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
if (!(pipeline instanceof DeferredWorldRenderingPipeline drp)) return;
if (!drp.shouldOverrideShaders()) return;
if (GLStateManager.isForeignDraw()) {
programRestoreDeferred = true;
return;
}
drp.restorePassAfterModProgram(event.newProgram);
});
GLSMHooks.PROGRAM_CHANGE.addListener(event -> {
if (!Iris.enabled) return;
if (!event.postBind) return;
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.loading.fml.transformers;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
import com.gtnewhorizons.angelica.loading.shared.transformers.CeleritasBlockTransform;
import com.gtnewhorizons.angelica.loading.shared.transformers.TileEntityMarkerTransform;
import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
@@ -12,10 +13,12 @@ import org.objectweb.asm.tree.ClassNode;
public class CeleritasBlockTransformer implements IClassTransformer {
private final CeleritasBlockTransform inner;
private final TileEntityMarkerTransform tileEntities;
private final String[] exclusions;
public CeleritasBlockTransformer() {
this.inner = new CeleritasBlockTransform(AngelicaClientTweaker.isObfEnv());
this.tileEntities = new TileEntityMarkerTransform(AngelicaClientTweaker.isObfEnv());
this.exclusions = inner.getTransformerExclusions();
}
@@ -31,14 +34,19 @@ public class CeleritasBlockTransformer implements IClassTransformer {
}
final ClassReader cr = new ClassReader(basicClass);
inner.trackBlockSubclasses(cr.getClassName(), cr.getSuperName());
final String className = cr.getClassName();
inner.trackBlockSubclasses(className, cr.getSuperName());
tileEntities.track(className, cr.getSuperName());
if (!inner.shouldTransform(basicClass)) {
return basicClass;
final int markers = tileEntities.markersFor(className, basicClass);
final byte[] marked = markers == 0 ? basicClass : tileEntities.addMarkers(basicClass, markers);
if (!inner.shouldTransform(marked)) {
return marked;
}
final ClassNode cn = new ClassNode();
cr.accept(cn, 0);
(markers == 0 ? cr : new ClassReader(marked)).accept(cn, 0);
final boolean changed = inner.transformClassNode(transformedName, cn);
if (changed) {
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
@@ -47,7 +55,7 @@ public class CeleritasBlockTransformer implements IClassTransformer {
AngelicaClassDump.dumpClass(transformedName, basicClass, bytes, this);
return bytes;
}
return basicClass;
return marked;
}
}
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.loading.rfb.transformers;
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
import com.gtnewhorizons.angelica.loading.shared.transformers.CeleritasBlockTransform;
import com.gtnewhorizons.angelica.loading.shared.transformers.TileEntityMarkerTransform;
import com.gtnewhorizons.retrofuturabootstrap.api.ClassHeaderMetadata;
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
import com.gtnewhorizons.retrofuturabootstrap.api.ExtensibleClassLoader;
@@ -9,15 +10,18 @@ import com.gtnewhorizons.retrofuturabootstrap.api.RfbClassTransformer;
import org.intellij.lang.annotations.Pattern;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.tree.ClassNode;
import java.util.jar.Manifest;
public class RFBCeleritasBlockTransformer implements RfbClassTransformer {
private final CeleritasBlockTransform inner;
private final TileEntityMarkerTransform tileEntities;
public RFBCeleritasBlockTransformer(boolean isObf) {
inner = new CeleritasBlockTransform(isObf);
tileEntities = new TileEntityMarkerTransform(isObf);
}
@Pattern("[a-z0-9-]+")
@@ -54,15 +58,32 @@ public class RFBCeleritasBlockTransformer implements RfbClassTransformer {
return false;
}
inner.trackBlockSubclasses(metadata.binaryThisName(), metadata.binarySuperName());
return inner.shouldTransform(classNode.getOriginalBytes());
final String thisName = metadata.binaryThisName();
final String superName = metadata.binarySuperName();
final byte[] originalBytes = classNode.getOriginalBytes();
inner.trackBlockSubclasses(thisName, superName);
if (context == RfbClassTransformer.Context.LCL_WITH_TRANSFORMS) {
tileEntities.track(thisName, superName);
if (tileEntities.markersFor(thisName, originalBytes) != 0) return true;
}
return inner.shouldTransform(originalBytes);
}
@Override
public boolean transformClassIfNeeded(@NotNull ExtensibleClassLoader classLoader,
@NotNull RfbClassTransformer.Context context, @Nullable Manifest manifest,
@NotNull String className, @NotNull ClassNodeHandle classNode) {
final boolean changed = inner.transformClassNode(className, classNode.getNode());
final ClassNode node = classNode.getNode();
boolean changed = false;
if (context == RfbClassTransformer.Context.LCL_WITH_TRANSFORMS) {
final int markers = tileEntities.markersFor(node.name, classNode.getOriginalBytes());
if (markers != 0) {
tileEntities.addMarkers(node, markers);
changed = true;
}
}
changed |= inner.transformClassNode(className, node);
if (changed) {
classNode.computeMaxs();
AngelicaClassDump.dumpRFBClass(className, classNode, this);
@@ -0,0 +1,130 @@
package com.gtnewhorizons.angelica.loading.shared.transformers;
import com.gtnewhorizon.gtnhlib.asm.ClassConstantPoolParser;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Marks TileEntity subclasses that declare getDescriptionPacket, shouldRenderInPass or getDistanceFrom with a
* corresponding interface, so runtime code can test with instanceof instead of reflection.
*/
public final class TileEntityMarkerTransform {
public static final int MARK_DESCRIPTION_PACKET = 1 << 0;
public static final int MARK_SHOULD_RENDER_IN_PASS = 1 << 1;
public static final int MARK_GET_DISTANCE_FROM = 1 << 2;
private static final String SENDS_DESCRIPTION_PACKET = "com/gtnewhorizons/angelica/mixins/interfaces/SendsDescriptionPacket";
private static final String OVERRIDES_SHOULD_RENDER_IN_PASS = "com/gtnewhorizons/angelica/mixins/interfaces/OverridesShouldRenderInPass";
private static final String OVERRIDES_GET_DISTANCE_FROM = "com/gtnewhorizons/angelica/mixins/interfaces/OverridesGetDistanceFrom";
private static final String TILE_ENTITY = "net/minecraft/tileentity/TileEntity";
private static final int SCAN_FLAGS = ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;
private record Marker(int bit, String method, String desc, String iface) {}
private final Marker[] markers;
private final ClassConstantPoolParser prefilter;
private final Set<String> tileEntities = Collections.newSetFromMap(new ConcurrentHashMap<>());
public TileEntityMarkerTransform(boolean isObf) {
this.markers = new Marker[] {
new Marker(MARK_DESCRIPTION_PACKET, isObf ? "func_145844_m" : "getDescriptionPacket", "()Lnet/minecraft/network/Packet;", SENDS_DESCRIPTION_PACKET),
new Marker(MARK_SHOULD_RENDER_IN_PASS, "shouldRenderInPass", "(I)Z", OVERRIDES_SHOULD_RENDER_IN_PASS),
new Marker(MARK_GET_DISTANCE_FROM, isObf ? "func_145835_a" : "getDistanceFrom", "(DDD)D", OVERRIDES_GET_DISTANCE_FROM) };
final String[] methods = new String[markers.length];
for (int i = 0; i < markers.length; i++) methods[i] = markers[i].method();
this.prefilter = new ClassConstantPoolParser(methods);
}
private boolean isTileEntity(String className) {
return className != null && (className.startsWith(TILE_ENTITY) || tileEntities.contains(className));
}
public void track(String className, String superClassName) {
if (className != null && isTileEntity(superClassName)) tileEntities.add(className);
}
public int markersFor(String className, byte[] classBytes) {
if (TILE_ENTITY.equals(className) || !isTileEntity(className) || !prefilter.find(classBytes)) return 0;
final MarkerScanner scanner = new MarkerScanner();
new ClassReader(classBytes).accept(scanner, SCAN_FLAGS);
return scanner.declared & ~scanner.alreadyPresent;
}
public byte[] addMarkers(byte[] classBytes, int markers) {
final ClassReader cr = new ClassReader(classBytes);
final ClassWriter cw = new ClassWriter(cr, 0);
cr.accept(new ClassVisitor(Opcodes.ASM5, cw) {
@Override
public void visit(int version, int access, String name, String sig, String superName, String[] interfaces) {
final String[] added = interfacesFor(markers);
if (interfaces == null) {
super.visit(version, access, name, sig, superName, added);
return;
}
final String[] merged = Arrays.copyOf(interfaces, interfaces.length + added.length);
System.arraycopy(added, 0, merged, interfaces.length, added.length);
super.visit(version, access, name, sig, superName, merged);
}
}, 0);
return cw.toByteArray();
}
public void addMarkers(ClassNode cn, int markers) {
Collections.addAll(cn.interfaces, interfacesFor(markers));
}
private int bitOf(int access, String name, String desc) {
if ((access & Opcodes.ACC_PUBLIC) == 0 || (access & Opcodes.ACC_STATIC) != 0) return 0;
for (Marker m : markers) if (m.method().equals(name) && m.desc().equals(desc)) return m.bit();
return 0;
}
private int bitOf(String interfaceName) {
for (Marker m : markers) if (m.iface().equals(interfaceName)) return m.bit();
return 0;
}
private String[] interfacesFor(int wanted) {
final String[] names = new String[Integer.bitCount(wanted)];
int next = 0;
for (Marker m : markers) if ((wanted & m.bit()) != 0) names[next++] = m.iface();
return names;
}
private final class MarkerScanner extends ClassVisitor {
private int declared;
private int alreadyPresent;
MarkerScanner() {
super(Opcodes.ASM5);
}
@Override
public void visit(int version, int access, String name, String sig, String superName, String[] interfaces) {
if (interfaces == null) return;
for (String iface : interfaces) alreadyPresent |= bitOf(iface);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String sig, String[] exceptions) {
declared |= bitOf(access, name, desc);
return null;
}
}
}
@@ -387,6 +387,7 @@ public enum Mixins implements IMixins {
, "celeritas.terrain.MixinWorld_WorkerMutationGuard"
, "celeritas.terrain.MixinTileEntity_AwaitingDescriptor"
, "celeritas.terrain.MixinNetHandlerPlayClient_DescriptorRepair"
, "celeritas.terrain.MixinWorld_AwaitingDescriptor"
, "celeritas.terrain.MixinRenderRegion"
, "celeritas.terrain.MixinSectionRenderDataStorage"
, "celeritas.terrain.MixinDefaultChunkRenderer"
@@ -416,11 +417,13 @@ public enum Mixins implements IMixins {
.addClientMixins(
"shaders.MixinDroppedItemGlintEdges"
, "shaders.MixinHeldItemGlintEdges"
, "shaders.MixinEffectRenderer_Particles"
, "shaders.MixinEntityPickupFX"
, "shaders.MixinEntityRenderer"
, "shaders.MixinGuiIngameForge"
, "shaders.MixinFramebuffer"
, "shaders.MixinItem"
, "shaders.MixinItemRenderer_ItemId"
, "shaders.MixinLocale"
, "shaders.MixinRender"
, "shaders.MixinRenderBiped"
@@ -743,6 +746,24 @@ public enum Mixins implements IMixins {
.setApplyIf(() -> AngelicaConfig.enableIris)
.addClientMixins("client.etfuturum.MixinLayerBetterElytra")
),
ET_FUTURUM_BEACON_BEAM(new MixinBuilder("Iris Et Futurum Beacon Beam")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.ET_FUTURUM_REQUIEM)
.setApplyIf(() -> AngelicaConfig.enableIris)
.addClientMixins("client.etfuturum.MixinTileEntityNewBeaconRenderer")
),
OPENBLOCKS_TROPHY_ENTITY_GBUFFER(new MixinBuilder("Render OpenBlocks trophies with the entity gbuffer programs")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.OPENBLOCKS)
.setApplyIf(() -> AngelicaConfig.enableIris)
.addClientMixins("client.openblocks.MixinTileEntityTrophyRenderer")
),
DRAGONAPI_PARTICLE_SPLIT(new MixinBuilder("Split DragonAPI's replacement particle renderer between the particle gbuffer programs")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.DRAGON_API)
.setApplyIf(() -> AngelicaConfig.enableIris)
.addClientMixins("client.dragonapi.MixinThrottleableEffectRenderer")
),
MCPATCHER_FORGE(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures)
@@ -23,6 +23,7 @@ public enum TargetedMod implements ITargetMod {
NTM_SPACE("com.hbm.dim.SolarSystem"),
IC2("ic2.core.coremod.IC2core", "IC2"),
NATURA(null, "Natura"),
OPENBLOCKS(null, "OpenBlocks"),
SECURITYCRAFT(null, "securitycraft"),
STARMINER("jp.mc.ancientred.starminer.core.SMPlugin", "modJ_StarMiner"),
SUPERNOVA("com.mitchej123.supernova.core.SupernovaCore", "supernova"),
@@ -1,8 +1,12 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
public interface IAwaitingDescriptorTE {
public interface AwaitingDescriptorTE {
boolean angelica$isAwaitingDescriptor();
void angelica$setAwaitingDescriptor(boolean awaiting);
int angelica$getDescriptorWaitTicks();
void angelica$setDescriptorWaitTicks(int ticks);
}
@@ -6,6 +6,4 @@ public interface ITileEntityBoundingBoxCache {
AxisAlignedBB angelica$getCachedRenderBoundingBox();
byte angelica$boundsClass();
byte angelica$passClass();
}
@@ -0,0 +1,8 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
import net.minecraft.item.ItemStack;
public interface ItemRendererAccessor {
ItemStack angelica$getItemToRender();
}
@@ -0,0 +1,3 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
public interface OverridesGetDistanceFrom {}
@@ -0,0 +1,3 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
public interface OverridesShouldRenderInPass {}
@@ -0,0 +1,3 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
public interface SendsDescriptionPacket {}
@@ -0,0 +1,39 @@
package com.gtnewhorizons.angelica.profiling;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiIngameMenu;
import java.util.HashMap;
import java.util.Map;
public final class TracyUiSections {
private static final Map<Class<?>, String> LABELS = new HashMap<>();
private static Class<?> current = TracyUiSections.class;
private static long section;
private TracyUiSections() {}
public static void poll(GuiScreen screen) {
if (!Tracy.ENABLED) return;
final Class<?> screenClass = screen == null ? null : screen.getClass();
if (screenClass == current) return;
current = screenClass;
Tracy.sectionLeave(section);
section = Tracy.sectionEnter(Tracy.SECTION_UI, label(screenClass));
}
private static String label(Class<?> screenClass) {
if (screenClass == null) return "in world";
String label = LABELS.get(screenClass);
if (label == null) {
if (screenClass == GuiMainMenu.class) label = "main menu";
else if (screenClass == GuiIngameMenu.class) label = "pause";
else label = screenClass.getSimpleName();
LABELS.put(screenClass, label);
}
return label;
}
}
@@ -55,6 +55,7 @@ import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfig;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
import com.gtnewhorizons.angelica.iris.IrisGLSMBridge;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
@@ -216,9 +217,15 @@ public final class ClientProxy extends CommonProxy {
}
}
private long worldSection;
@SubscribeEvent
public void worldLoad(WorldEvent.Load event) {
if (!event.world.isRemote) return;
if (Tracy.ENABLED) {
Tracy.sectionLeave(this.worldSection);
this.worldSection = Tracy.sectionEnter(Tracy.SECTION_WORLD, "world " + event.world.provider.getDimensionName() + " (dim " + event.world.provider.dimensionId + ")");
}
if (GLStateManager.isRunningSplash()) {
GLStateManager.setRunningSplash(false);
LOGGER.info("World loaded - Enabling GLSM Cache");
@@ -238,6 +245,8 @@ public final class ClientProxy extends CommonProxy {
@SubscribeEvent
public void onWorldUnload(WorldEvent.Unload event) {
if (!event.world.isRemote) return;
Tracy.sectionLeave(this.worldSection);
this.worldSection = 0L;
DynamicLights.get().removeAllLightSources();
GpuCulling.onWorldUnload();
}
@@ -73,6 +73,9 @@ public final class SelectionBoxRenderer {
public static void draw(AxisAlignedBB aabb, int color) {
if (program == null || vao == null) return;
GLStateManager.enableDepthTest();
GLStateManager.glDepthFunc(GL11.GL_LEQUAL);
final float minX = (float) aabb.minX, minY = (float) aabb.minY, minZ = (float) aabb.minZ;
final float extX = (float) (aabb.maxX - aabb.minX);
final float extY = (float) (aabb.maxY - aabb.minY);
@@ -123,11 +123,11 @@ public class DeferredEntityOverlay {
GbufferPrograms.beginEntities();
try {
for (DeferredEntry entry : deferred) {
CapturedRenderingState.INSTANCE.setCurrentEntity(EntityIdHelper.getEntityId(entry.entity));
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(EntityIdHelper.getEntityId(entry.entity), 0);
renderOverlay(entry);
}
} finally {
CapturedRenderingState.INSTANCE.setCurrentEntity(-1);
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(-1, 0);
GbufferPrograms.endEntities();
}
recycle();
@@ -4,6 +4,7 @@ import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import com.gtnewhorizons.angelica.glsm.profiling.TracyBackend;
import java.util.concurrent.locks.LockSupport;
@@ -25,16 +26,21 @@ public final class FramePacer {
private static final Tracy.ZoneId Z_PACER_IDLE = Tracy.zoneId("pacerIdleWork", Tracy.COLOR_CLIENT);
private static final Tracy.ZoneId Z_PACER_SLEEP = Tracy.zoneId("pacerSleep", Tracy.COLOR_SWAP);
private static final long P_FRAME_GATE_US = Tracy.plotHandle("frameGateUs");
private static final long P_SLACK_US = Tracy.plotHandle("pacer.slackUs");
private static final long P_SPIN_US = Tracy.plotHandle("pacer.spinUs");
private static final long P_CEILING_HZ = Tracy.plotHandle("pacer.ceilingHz");
private static final long P_GATE_ACTIVE = Tracy.plotHandle("pacer.gateActive");
private static long deadline;
private static long lastFrameTime;
private static boolean warnedUncapped;
private static boolean pacedLastFrame;
private static boolean statsActive;
private static VSyncMode lastVSyncMode = VSyncMode.ON;
private static int lastCapHz;
private static int lastRefreshHz;
private static int lastCeilingHz;
private static IdleWork idleWork;
private static PacerSleeper sleeper = newSleeper();
@@ -47,7 +53,6 @@ public final class FramePacer {
private static int loggedRefreshHz;
private static int loggedCapHz;
private static boolean gateBlocking;
private static long lastSeenGateEnd;
private FramePacer() {}
@@ -60,6 +65,35 @@ public final class FramePacer {
idleWork = work;
}
public static boolean pacedLastFrame() {
return pacedLastFrame;
}
public static void beginStats() {
sleeper.resetStats();
statsActive = true;
}
public static String endStats() {
statsActive = false;
final String summary = sleeper.summary(configLine());
Tracy.message(summary);
return summary;
}
static boolean statsActive() {
return statsActive;
}
static String configLine(VSyncMode mode, int refreshHz, int capHz, int ceilingHz) {
return "cfg=[" + mode + " refresh=" + refreshHz + "Hz cap=" + capHz + " ceiling=" + ceilingHz + "Hz]";
}
private static String configLine() {
return configLine(lastVSyncMode, lastRefreshHz, lastCapHz,
pacingCeilingHz(lastVSyncMode, lastCapHz, lastRefreshHz, gateActive()));
}
public static int plausibleRefreshHz(int hz) {
return hz >= MIN_PLAUSIBLE_REFRESH_HZ && hz <= MAX_PLAUSIBLE_REFRESH_HZ ? hz : 0;
}
@@ -155,11 +189,6 @@ public final class FramePacer {
}
public static long pace(int ceilingHz, Runnable renderAheadWait) {
if (ceilingHz != lastCeilingHz) {
lastCeilingHz = ceilingHz;
deadline = 0L;
}
long now = System.nanoTime();
long phaseEnd;
@@ -167,6 +196,9 @@ public final class FramePacer {
final long thresholdNanos = gateBlockThresholdNanos(targetNanos);
long frameGateNanos = 0L;
pacedLastFrame = ceilingHz > 0;
Tracy.plotInt(P_CEILING_HZ, ceilingHz);
if (ceilingHz > 0) {
frameGateNanos = GLStateManager.lastFrameGateNanos();
final long gateEnd = GLStateManager.lastFrameGateEndNanos();
@@ -192,8 +224,11 @@ public final class FramePacer {
now = phaseEnd;
}
if (now < deadline) {
final long preSleep = now;
final long slackNanos = deadline - now;
Tracy.plotInt(P_SLACK_US, slackNanos / 1000);
final boolean slept = now < deadline;
if (slept) {
Tracy.beginZone(Z_PACER_SLEEP);
try {
now = sleeper.sleepUntil(deadline, now);
@@ -201,6 +236,10 @@ public final class FramePacer {
Tracy.endZone();
}
}
if (Tracy.ENABLED || statsActive) {
sleeper.noteFrame(slackNanos, slept ? now - deadline : 0L, slept);
Tracy.plotInt(P_SPIN_US, sleeper.lastSpinNanos / 1000);
}
} else {
deadline = 0L;
}
@@ -215,15 +254,18 @@ public final class FramePacer {
final long waitNanos = phaseEnd - now;
now = phaseEnd;
if (ceilingHz > 0 && waitNanos > thresholdNanos) deadline = now;
gateBlocking = observeGate(ceilingHz > 0 ? Math.max(frameGateNanos, waitNanos) : 0L, thresholdNanos);
final boolean gateBlocking = observeGate(ceilingHz > 0 ? Math.max(frameGateNanos, waitNanos) : 0L, thresholdNanos);
Tracy.plotInt(P_GATE_ACTIVE, gateBlocking ? 1 : 0);
GLStateManager.pumpDisplayMessages();
if (pacingStatusChanged(gateBlocking, lastVSyncMode, lastRefreshHz, lastCapHz)) {
final boolean blocking = settledBlocking;
final String line = pacingLine(lastVSyncMode, lastRefreshHz, lastCapHz, blocking);
if (vsyncNotHonoured(lastVSyncMode, blocking)) AngelicaMod.LOGGER.warn(line);
final boolean notHonoured = vsyncNotHonoured(lastVSyncMode, blocking);
if (notHonoured) AngelicaMod.LOGGER.warn(line);
else AngelicaMod.LOGGER.info(line);
Tracy.message(line, notHonoured ? TracyBackend.SEVERITY_WARNING : TracyBackend.SEVERITY_INFO);
}
final long period = lastFrameTime == 0L ? 0L : now - lastFrameTime;
@@ -5,31 +5,50 @@ import java.util.function.LongSupplier;
final class PacerSleeper {
private static final long SPIN_FLOOR_NANOS = 100_000L;
private static final long MIN_PARK_NANOS = 1_000_000L;
private static final long MAX_OVERSHOOT_NANOS = 4_000_000L;
private static final int SLOTS = 10;
static final int SLOTS = 10;
static final long MIN_SPIN_FLOOR_NANOS = 20_000L;
private static final long MAX_SPIN_FLOOR_NANOS = 500_000L;
private final LongSupplier clock;
private final LongConsumer parker;
private final boolean spinWait;
private final long[] overshoots = new long[SLOTS];
private long overshootSum;
private long spinFloorNanos = MIN_SPIN_FLOOR_NANOS;
private int offset;
long lastSpinNanos;
int lastParks;
private long frames;
private long slackSum;
private long parkTotal;
private long lateFrames;
private long lateMax;
PacerSleeper(LongSupplier clock, LongConsumer parker, boolean spinWait) {
this.clock = clock;
this.parker = parker;
this.spinWait = spinWait;
}
long spinFloorNanos() {
return spinFloorNanos;
}
long sleepUntil(long deadlineNanos, long now) {
resetLastFrame();
long spinFloor = spinFloorNanos;
while (true) {
final long remaining = deadlineNanos - now;
if (remaining <= SPIN_FLOOR_NANOS) break;
if (remaining <= spinFloor) break;
if (Thread.currentThread().isInterrupted()) return clock.getAsLong();
long request = remaining - avgOvershootNanos() - SPIN_FLOOR_NANOS;
long request = remaining - avgOvershootNanos() - spinFloor;
if (request <= 0L) {
if (remaining <= MIN_PARK_NANOS) break;
request = MIN_PARK_NANOS;
@@ -39,17 +58,51 @@ final class PacerSleeper {
parker.accept(request);
now = clock.getAsLong();
record(Math.min(Math.max(0L, now - before - request), MAX_OVERSHOOT_NANOS));
spinFloor = spinFloorNanos;
lastParks++;
}
if (spinWait) {
final long spinStart = now;
while (now < deadlineNanos) {
Thread.yield();
if (deadlineNanos - now > spinFloor) Thread.yield();
else Thread.onSpinWait();
now = clock.getAsLong();
}
lastSpinNanos = now - spinStart;
}
return now;
}
void noteFrame(long slackNanos, long wakeLateNanos, boolean slept) {
if (!slept) resetLastFrame();
frames++;
slackSum += slackNanos;
parkTotal += lastParks;
if (slackNanos < 0L && -slackNanos > lateMax) lateMax = -slackNanos;
if (slackNanos < 0L || wakeLateNanos > 0L) lateFrames++;
}
private void resetLastFrame() {
lastSpinNanos = 0L;
lastParks = 0;
}
void resetStats() {
frames = 0L;
slackSum = 0L;
parkTotal = 0L;
lateFrames = 0L;
lateMax = 0L;
}
String summary(String config) {
if (frames == 0L) return "pacer: " + config + " no paced frames";
return "pacer: " + config + " frames=" + frames + " parks=" + parkTotal + " slackUs avg=" + slackSum / frames / 1000L + " floorUs=" + spinFloorNanos / 1000L
+ " overshootUs est=" + avgOvershootNanos() / 1000L + " lateFrames=" + lateFrames + " lateMaxUs=" + lateMax / 1000L;
}
long avgOvershootNanos() {
return overshootSum / SLOTS;
}
@@ -58,5 +111,12 @@ final class PacerSleeper {
overshootSum += nanos - overshoots[offset];
overshoots[offset] = nanos;
offset = (offset + 1) % SLOTS;
long max = 0L;
for (final long o : overshoots) {
if (o > max) max = o;
}
final long jitter = max - avgOvershootNanos();
spinFloorNanos = Math.max(MIN_SPIN_FLOOR_NANOS, Math.min(MAX_SPIN_FLOOR_NANOS, jitter));
}
}
@@ -0,0 +1,63 @@
package com.gtnewhorizons.angelica.rendering;
import com.gtnewhorizons.angelica.client.rendering.DeferredDrawBatcher;
import com.gtnewhorizons.angelica.rendering.celeritas.SpriteExtension;
import net.coderbot.iris.layer.GbufferPrograms;
import net.minecraft.client.particle.EntityCloudFX;
import net.minecraft.client.particle.EntityFX;
import net.minecraft.client.particle.EntityFireworkOverlayFX;
import net.minecraft.client.particle.EntityFireworkSparkFX;
import net.minecraft.client.particle.EntityFireworkStarterFX;
import net.minecraft.client.particle.EntitySpellParticleFX;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import org.embeddedt.embeddium.impl.render.chunk.sprite.SpriteTransparencyLevel;
/**
* Splits a particle run between {@code gbuffers_particles} and {@code gbuffers_particles_translucent}.
*/
public final class ParticleRunSplitter {
private static boolean runTranslucent;
private ParticleRunSplitter() {}
public static void beginRun() {
runTranslucent = false;
GbufferPrograms.setTranslucencyDeclaration(Boolean.FALSE);
}
public static void splitIfNeeded(EntityFX particle, Tessellator tessellator, float partialTicks) {
final boolean translucent = isTranslucent(particle);
if (translucent == runTranslucent) {
return;
}
final boolean batcherWasActive = DeferredDrawBatcher.isActive();
if (batcherWasActive) {
DeferredDrawBatcher.exitAndFlush();
}
tessellator.draw();
runTranslucent = translucent;
GbufferPrograms.setTranslucencyDeclaration(translucent);
tessellator.startDrawingQuads();
tessellator.setBrightness(particle.getBrightnessForRender(partialTicks));
if (batcherWasActive) {
DeferredDrawBatcher.enter();
}
}
public static boolean isTranslucent(EntityFX particle) {
if (particle.particleIcon instanceof TextureAtlasSprite sprite) {
return ((SpriteExtension) sprite).celeritas$getTransparencyLevel() == SpriteTransparencyLevel.TRANSLUCENT;
}
return particle instanceof EntityFireworkSparkFX
|| particle instanceof EntityFireworkOverlayFX
|| particle instanceof EntityFireworkStarterFX
|| particle instanceof EntitySpellParticleFX
|| particle instanceof EntityCloudFX;
}
}
@@ -71,6 +71,7 @@ public final class PlayerReflectionCapture {
private static final Matrix4f pendingModelView = new Matrix4f();
private static int pendingSkin;
private static int pendingEntityId;
private static int pendingItemId;
private static int vbo;
private static int vao;
@@ -134,6 +135,7 @@ public final class PlayerReflectionCapture {
pendingModelView.set(GLStateManager.getModelViewMatrix());
pendingSkin = GLStateManager.getBoundTextureForServerState(0);
pendingEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedEntity();
pendingItemId = CapturedRenderingState.INSTANCE.getCurrentRenderedItem();
pending = true;
}
@@ -328,9 +330,10 @@ public final class PlayerReflectionCapture {
final boolean prevDepthWrite = GLStateManager.getDepthState().isEnabled();
final int prevEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedEntity();
final int prevItemId = CapturedRenderingState.INSTANCE.getCurrentRenderedItem();
savedModelView.set(GLStateManager.getModelViewMatrix());
CapturedRenderingState.INSTANCE.setCurrentEntity(pendingEntityId);
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(pendingEntityId, pendingItemId);
GLStateManager.setModelViewMatrix(pendingModelView);
if (maskable) {
GLStateManager.glColorMask(false, false, false, false);
@@ -344,7 +347,7 @@ public final class PlayerReflectionCapture {
GLStateManager.glColorMask(pr, pg, pb, pa);
}
GLStateManager.setModelViewMatrix(savedModelView);
CapturedRenderingState.INSTANCE.setCurrentEntity(prevEntityId);
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(prevEntityId, prevItemId);
GLStateManager.glBindVertexArray(0);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, prevTexture);
@@ -1,18 +1,20 @@
package com.gtnewhorizons.angelica.rendering;
/// Despite not in the `api` package, this interface is used by GT5uNH and GTNHLib, possibly others. Be careful what you
/// change!
@SuppressWarnings("unused")
public interface StateAwareTessellator {
/**
* True if the vertex originated from a RenderBlocks method call with the enableAO flag set.
*/
/// True if the vertex originated from a RenderBlocks method call with the enableAO flag set.
int RENDERED_WITH_VANILLA_AO = 0x1;
int NO_DIRECTIONAL_SHADING = 0x2;
void angelica$setAppliedAo(boolean flag);
void angelica$setNoDirectionalShading(boolean flag);
/**
* Sets whether we're doing terrain meshing as part of celeritas -- collects additional information.
*
* Enables per-vertex AO state collection into {@link #angelica$getVertexStates()}.
*/
/// Sets whether we're doing terrain meshing as part of celeritas -- collects additional information.
///
/// Enables per-vertex AO state collection into [#angelica$getVertexStates()].
void angelica$setCeleritasMeshing(boolean active);
int[] angelica$getVertexStates();
@@ -1,7 +1,6 @@
package com.gtnewhorizons.angelica.rendering;
import it.unimi.dsi.fastutil.objects.Reference2ByteOpenHashMap;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
@@ -17,25 +16,12 @@ public final class TileEntityRenderBoundsRegistry {
public static final byte INFINITE = 2;
public static final byte DYNAMIC = 3;
public static final byte PASS0_ONLY = 1;
public static final byte PASS_OVERRIDES = 2;
private static final byte NOT_OVERRIDDEN = 1;
private static final byte OVERRIDDEN = 2;
private static final Reference2ByteOpenHashMap<Class<? extends TileEntity>> classRegistry = new Reference2ByteOpenHashMap<>();
private static final Reference2ByteOpenHashMap<Class<? extends TileEntity>> passClassRegistry = new Reference2ByteOpenHashMap<>();
private static final Reference2ByteOpenHashMap<Class<? extends TileEntity>> distanceOverrideRegistry = new Reference2ByteOpenHashMap<>();
private static final Set<String> dynamicClassNames = ConcurrentHashMap.newKeySet();
private static final String[] SHOULD_RENDER_IN_PASS_NAMES = { "shouldRenderInPass" };
private static String[] distanceFromNames;
static {
classRegistry.defaultReturnValue(UNKNOWN);
passClassRegistry.defaultReturnValue(UNKNOWN);
distanceOverrideRegistry.defaultReturnValue(UNKNOWN);
}
private TileEntityRenderBoundsRegistry() {}
@@ -73,72 +59,4 @@ public final class TileEntityRenderBoundsRegistry {
}
return result;
}
public static boolean overridesMethod(Class<?> clazz, Class<?> base, String[] candidateNames, Class<?>... params) {
for (String name : candidateNames) {
try {
return clazz.getMethod(name, params).getDeclaringClass() != base;
} catch (NoSuchMethodException ignored) {
} catch (Throwable t) {
return true;
}
}
return true;
}
private static String[] distanceFromNames() {
String[] names = distanceFromNames;
if (names == null) {
String resolved;
try {
resolved = Boolean.TRUE.equals(Launch.blackboard.get("fml.deobfuscatedEnvironment")) ? "getDistanceFrom" : "func_145835_a";
} catch (Throwable t) {
resolved = "getDistanceFrom";
}
distanceFromNames = names = new String[] { resolved };
}
return names;
}
public static byte classifyPass(TileEntity te) {
final Class<? extends TileEntity> clazz = te.getClass();
synchronized (passClassRegistry) {
final byte cached = passClassRegistry.getByte(clazz);
if (cached != UNKNOWN) return cached;
}
final byte result = overridesMethod(clazz, TileEntity.class, SHOULD_RENDER_IN_PASS_NAMES, int.class)
? PASS_OVERRIDES : PASS0_ONLY;
synchronized (passClassRegistry) {
passClassRegistry.put(clazz, result);
}
return result;
}
public static boolean overridesGetDistanceFrom(Class<? extends TileEntity> clazz) {
synchronized (distanceOverrideRegistry) {
final byte cached = distanceOverrideRegistry.getByte(clazz);
if (cached != UNKNOWN) return cached == OVERRIDDEN;
}
final boolean overrides = overridesMethod(clazz, TileEntity.class, distanceFromNames(), double.class, double.class, double.class);
synchronized (distanceOverrideRegistry) {
distanceOverrideRegistry.put(clazz, overrides ? OVERRIDDEN : NOT_OVERRIDDEN);
}
return overrides;
}
public static void clear() {
synchronized (classRegistry) {
classRegistry.clear();
}
synchronized (passClassRegistry) {
passClassRegistry.clear();
}
synchronized (distanceOverrideRegistry) {
distanceOverrideRegistry.clear();
}
}
}
@@ -1,5 +1,7 @@
package com.gtnewhorizons.angelica.rendering.celeritas;
import static com.gtnewhorizons.angelica.rendering.StateAwareTessellator.NO_DIRECTIONAL_SHADING;
import com.gtnewhorizons.angelica.api.ExtQuadLightData;
import com.gtnewhorizons.angelica.api.TintComputer;
import com.gtnewhorizons.angelica.api.TintRegistry;
@@ -28,6 +30,7 @@ import org.embeddedt.embeddium.impl.model.light.data.QuadLightData;
import org.embeddedt.embeddium.impl.model.light.flat.FlatLightPipeline;
import org.embeddedt.embeddium.impl.model.light.smooth.SmoothLightPipeline;
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFacing;
import org.embeddedt.embeddium.impl.model.quad.properties.ModelQuadFlags;
import org.embeddedt.embeddium.impl.render.chunk.ChunkColorWriter;
import org.embeddedt.embeddium.impl.render.chunk.RenderPassConfiguration;
import org.embeddedt.embeddium.impl.render.chunk.compile.ChunkBuildBuffers;
@@ -43,6 +46,9 @@ import java.util.List;
public class AngelicaChunkBuildContext extends ChunkBuildContext {
public static final int NUM_PASSES = 2;
// Yoinked from Celeritas
private static final int[] REVERSED_QUAD_ORDER = { 0, 3, 2, 1 };
private final TextureMapExtension textureAtlas;
private final ChunkVertexEncoder.Vertex[] vertices = ChunkVertexEncoder.Vertex.uninitializedQuad();
@Getter
@@ -62,6 +68,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
private boolean lightPipelineReady = false;
private int originX, originY, originZ;
private float cachedSkylightSubtracted;
private final float[] fluidQuadPos = new float[12];
private ModelQuadFacing fluidQuadLightFace;
public AngelicaChunkBuildContext(RenderPassConfiguration<?> renderPassConfiguration, WorldClient world) {
super(renderPassConfiguration);
@@ -160,6 +168,7 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
boolean hasCachedMaterial = false;
TextureAtlasSprite lastSprite = null;
Material lastMaterial = null;
this.fluidQuadLightFace = null;
for (int quadIdx = 0; quadIdx < numQuads; quadIdx++) {
float uSum = 0, vSum = 0;
@@ -201,11 +210,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
// Otherwise, we need to use the block's lightmap values as they are as the ISBRH may have
// specified its own lightmaps for visual purposes (e.g. fullbright)
if (useAoCalculation && (quadState & StateAwareTessellator.RENDERED_WITH_VANILLA_AO) != 0) {
quadView.setup(trueNormal, blockX, blockY, blockZ);
final ModelQuadFacing lightFace = quadView.getLightFace();
final LightPipeline pipeline = blockAllowsSmoothLighting ? smoothLightPipeline : flatLightPipeline;
final ModelQuadFacing cullFace = quadView.getCullFace();
pipeline.calculate(quadView, worldX, worldY, worldZ, quadLightData, cullFace, lightFace, shouldApplyDiffuse, true);
calculateQuadLight(trueNormal, blockX, blockY, blockZ, worldX, worldY, worldZ, isFluid,
blockAllowsSmoothLighting, shouldApplyDiffuse && (quadState & NO_DIRECTIONAL_SHADING) == 0);
for (int vIdx = 0; vIdx < 4; vIdx++) {
final var vertex = vertices[vIdx];
@@ -227,11 +233,8 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
}
if (useAoCalculation && isFluid) {
quadView.setup(trueNormal, blockX, blockY, blockZ);
final ModelQuadFacing lightFace = quadView.getLightFace();
final LightPipeline pipeline = blockAllowsSmoothLighting ? smoothLightPipeline : flatLightPipeline;
final ModelQuadFacing cullFace = quadView.getCullFace();
pipeline.calculate(quadView, worldX, worldY, worldZ, quadLightData, cullFace, lightFace, shouldApplyDiffuse, true);
calculateQuadLight(trueNormal, blockX, blockY, blockZ, worldX, worldY, worldZ, true,
blockAllowsSmoothLighting, shouldApplyDiffuse);
for (int vIdx = 0; vIdx < 4; vIdx++) {
vertices[vIdx].light = quadLightData.lm[vIdx];
}
@@ -274,6 +277,42 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
}
}
private void calculateQuadLight(int trueNormal, int blockX, int blockY, int blockZ, int worldX, int worldY,
int worldZ, boolean isFluid, boolean blockAllowsSmoothLighting,
boolean shouldApplyDiffuse) {
final boolean isFlippedFluidCopy = isFluid && fluidQuadLightFace != null && isReversedFluidQuad();
quadView.setup(trueNormal, blockX, blockY, blockZ, isFluid ? ModelQuadFlags.IS_VANILLA_SHADED : 0,
isFlippedFluidCopy ? fluidQuadLightFace : null);
final LightPipeline pipeline = blockAllowsSmoothLighting ? smoothLightPipeline : flatLightPipeline;
pipeline.calculate(quadView, worldX, worldY, worldZ, quadLightData, quadView.getCullFace(),
quadView.getLightFace(), shouldApplyDiffuse, true);
if (isFluid && !isFlippedFluidCopy) {
fluidQuadLightFace = quadView.getLightFace();
for (int vIdx = 0; vIdx < 4; vIdx++) {
final var vertex = vertices[vIdx];
fluidQuadPos[vIdx * 3] = vertex.x;
fluidQuadPos[vIdx * 3 + 1] = vertex.y;
fluidQuadPos[vIdx * 3 + 2] = vertex.z;
}
} else if (!isFluid) {
fluidQuadLightFace = null;
}
}
private boolean isReversedFluidQuad() {
for (int vIdx = 0; vIdx < 4; vIdx++) {
final int src = REVERSED_QUAD_ORDER[vIdx] * 3;
final var vertex = vertices[vIdx];
if (vertex.x != fluidQuadPos[src] || vertex.y != fluidQuadPos[src + 1] || vertex.z != fluidQuadPos[src + 2]) {
return false;
}
}
return true;
}
private void applyBlockLightTint(int x, int y, int z, ChunkVertexEncoder.Vertex[] vertices) {
if (!hasColoredLight) return;
final ExtQuadLightData ext = (ExtQuadLightData) quadLightData;
@@ -4,6 +4,7 @@ import com.gtnewhorizon.gtnhlib.blockpos.BlockPos;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import com.gtnewhorizons.angelica.rendering.AngelicaRenderQueue;
import com.gtnewhorizons.angelica.rendering.StateAwareTessellator;
import com.gtnewhorizons.angelica.mixins.interfaces.OverridesGetDistanceFrom;
import com.gtnewhorizons.angelica.rendering.TileEntityRenderBoundsRegistry;
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProvider;
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
@@ -190,7 +191,7 @@ public abstract class AngelicaChunkBuilderMeshingTask extends ChunkBuilderTask<C
renderData.culledBlockEntities.add(tileEntity);
AngelicaBuiltRenderSectionData.packSectionLocalBounds(culledBounds, aabb, minX, minY, minZ);
double teDistSq;
if (TileEntityRenderBoundsRegistry.overridesGetDistanceFrom(tileEntity.getClass())) {
if (tileEntity instanceof OverridesGetDistanceFrom) {
teDistSq = Double.POSITIVE_INFINITY;
} else {
try {
@@ -45,6 +45,7 @@ import com.gtnewhorizons.angelica.dynamiclights.IDynamicLightWorldRenderer;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import com.gtnewhorizons.angelica.mixins.interfaces.ITileEntityBoundingBoxCache;
import com.gtnewhorizons.angelica.mixins.interfaces.OverridesShouldRenderInPass;
import com.gtnewhorizons.angelica.profiling.RenderClassTimings;
import com.gtnewhorizons.angelica.proxy.ClientProxy;
import com.gtnewhorizons.angelica.rendering.RenderingState;
@@ -405,7 +406,7 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
try {
for (int i = 0; i < frameTEs.size(); i++) {
final TileEntity te = frameTEs.get(i);
if (((ITileEntityBoundingBoxCache) te).angelica$passClass() == TileEntityRenderBoundsRegistry.PASS0_ONLY) {
if (!(te instanceof OverridesShouldRenderInPass)) {
dispatchTE(te, partialTicks);
count++;
continue;
@@ -13,18 +13,24 @@ public class VertexArrayQuadView implements ModelQuadView {
private int blockX, blockY, blockZ;
private ModelQuadFacing lightFace;
private int flags;
private int extraFlags;
public VertexArrayQuadView(ChunkVertexEncoder.Vertex[] vertices) {
this.vertices = vertices;
}
public void setup(int trueNormal, int blockX, int blockY, int blockZ) {
setup(trueNormal, blockX, blockY, blockZ, 0, null);
}
public void setup(int trueNormal, int blockX, int blockY, int blockZ, int extraFlags, ModelQuadFacing forcedLightFace) {
this.trueNormal = trueNormal;
this.blockX = blockX;
this.blockY = blockY;
this.blockZ = blockZ;
this.lightFace = null; // Reset cached lightFace
this.lightFace = forcedLightFace;
this.flags = 0;
this.extraFlags = extraFlags;
}
@Override
@@ -65,7 +71,7 @@ public class VertexArrayQuadView implements ModelQuadView {
@Override
public int getFlags() {
if (flags == 0) {
flags = ModelQuadFlags.getQuadFlags(this, getLightFace());
flags = ModelQuadFlags.getQuadFlags(this, getLightFace(), extraFlags);
}
return flags;
}
@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.rendering.celeritas.light;
import com.gtnewhorizons.angelica.rendering.celeritas.world.WorldSlice;
import lombok.Setter;
import net.minecraft.block.Block;
import net.minecraft.world.IBlockAccess;
@@ -9,6 +10,16 @@ import org.embeddedt.embeddium.impl.model.light.data.LightDataAccess;
public class LightDataCache extends LightDataAccess {
private IBlockAccess world;
/**
* Doing this to force packs like Steadfast's water to work properly.
*/
private static int skyLight(IBlockAccess world, Block block, int x, int y, int z, int neighborMaxed) {
if (world instanceof WorldSlice slice && block.getMaterial().isLiquid()) {
return slice.getRawSkyLight(x, y, z);
}
return neighborMaxed;
}
@Override
protected int compute(int x, int y, int z) {
final IBlockAccess world = this.world;
@@ -26,7 +37,7 @@ public class LightDataCache extends LightDataAccess {
} else {
final int packed = world.getLightBrightnessForSkyBlocks(x, y, z, 0);
bl = (packed >> 4) & 0xF;
sl = (packed >> 20) & 0xF;
sl = skyLight(world, block, x, y, z, (packed >> 20) & 0xF);
}
// AO value - blocks that emit light don't contribute to AO
@@ -276,6 +276,17 @@ public class WorldSlice implements IBlockAccessExtended, FLBlockAccess {
return skyBrightness << 20 | blockBrightness << 4;
}
public int getRawSkyLight(int x, int y, int z) {
final int minHeight = ModStatus.isCubicChunksLoaded ? CubicChunksAPI.getMinHeight(this.world) : 0;
final int maxHeight = ModStatus.isCubicChunksLoaded ? CubicChunksAPI.getMaxHeight(this.world) : 256;
if (y < minHeight || y >= maxHeight || x < -30_000_000 || z < -30_000_000 || x >= 30_000_000 || z >= 30_000_000) {
return 15;
}
return this.getLightLevel(EnumSkyBlock.Sky, x, y, z);
}
private int getSkyBlockTypeBrightness(EnumSkyBlock skyBlock, int x, int y, int z) {
if (this.getBlock(x, y, z).getUseNeighborBrightness()) {
int yp = this.getLightLevel(skyBlock, x, y + 1, z);
@@ -15,6 +15,7 @@ import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import com.gtnewhorizons.angelica.glsm.states.BlendState;
import com.gtnewhorizons.angelica.glsm.states.Color4;
import com.gtnewhorizons.angelica.glsm.states.PolygonState;
import com.gtnewhorizons.angelica.profiling.BailClassCounts;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
@@ -24,6 +25,7 @@ import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource;
import net.coderbot.batchedentityrendering.impl.TransparencyType;
import net.coderbot.iris.Iris;
import net.coderbot.iris.layer.GbufferPrograms;
import net.coderbot.iris.layer.PassOverride;
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
import net.coderbot.iris.uniforms.CapturedRenderingState;
import net.minecraft.client.model.ModelBox;
@@ -105,22 +107,34 @@ public final class ModelPartBatcher {
private static final class LayerKey {
ResourceLocation texture;
TesrMaterial material;
PassOverride pass;
float offsetFactor;
float offsetUnits;
LayerKey set(ResourceLocation texture, TesrMaterial material) {
LayerKey set(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits) {
this.texture = texture;
this.material = material;
this.pass = pass;
this.offsetFactor = offsetFactor;
this.offsetUnits = offsetUnits;
return this;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof LayerKey other)) return false;
return Objects.equals(texture, other.texture) && Objects.equals(material, other.material);
return Objects.equals(texture, other.texture) && Objects.equals(material, other.material)
&& Objects.equals(pass, other.pass)
&& Float.floatToIntBits(offsetFactor) == Float.floatToIntBits(other.offsetFactor)
&& Float.floatToIntBits(offsetUnits) == Float.floatToIntBits(other.offsetUnits);
}
@Override
public int hashCode() {
return Objects.hashCode(texture) * 31 + Objects.hashCode(material);
int h = (Objects.hashCode(texture) * 31 + Objects.hashCode(material)) * 31 + Objects.hashCode(pass);
h = h * 31 + Float.floatToIntBits(offsetFactor);
h = h * 31 + Float.floatToIntBits(offsetUnits);
return h;
}
}
@@ -128,6 +142,9 @@ public final class ModelPartBatcher {
private final LayerKey scratchKey = new LayerKey();
private ResourceLocation lastLayerTexture;
private TesrMaterial lastLayerMaterial;
private PassOverride lastLayerPass;
private float lastLayerOffsetFactor;
private float lastLayerOffsetUnits;
private RenderLayer lastLayer;
private static ResourceLocation lastBoundLocation;
@@ -177,7 +194,9 @@ public final class ModelPartBatcher {
if (instancedThisCycle) {
TesrBatchRenderer.INSTANCE.instancedRenderer.endFrame();
}
BatchingFontRenderer.flushDeferredText();
if (!TesrBatchRenderer.INSTANCE.hasPendingGeometry()) {
BatchingFontRenderer.flushDeferredText();
}
return;
}
final boolean inEntityLoop = mode == Mode.ENTITIES && Iris.enabled && GbufferPrograms.isEntityLoopActive();
@@ -193,6 +212,9 @@ public final class ModelPartBatcher {
if (instancedThisCycle) {
TesrBatchRenderer.INSTANCE.instancedRenderer.endFrame();
}
if (!TesrBatchRenderer.INSTANCE.hasPendingGeometry()) {
BatchingFontRenderer.flushDeferredText();
}
}
public boolean isActive() {
@@ -278,11 +300,19 @@ public final class ModelPartBatcher {
}
}
}
final RenderLayer layer = layerFor(texture, material);
final int entityId = mode == Mode.ENTITIES ? Math.max(0, CapturedRenderingState.INSTANCE.getCurrentRenderedEntity()) : CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
final PassOverride pass = shaderPipeline != null ? PassOverride.capture() : PassOverride.NONE;
final boolean offset = GLStateManager.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL);
final PolygonState polygon = GLStateManager.getPolygonState();
final float offsetFactor = offset ? polygon.getOffsetFactor() : 0.0f;
final float offsetUnits = offset ? polygon.getOffsetUnits() : 0.0f;
final RenderLayer layer = layerFor(texture, material, pass, offsetFactor, offsetUnits);
// Entities nested inside a TESR (mob spawner, OpenBlocks trophy) run in the block entity pass but carry an entity id
final int entityId = mode == Mode.ENTITIES || pass.isEntityPhase()
? Math.max(0, CapturedRenderingState.INSTANCE.getCurrentRenderedEntity())
: CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
final Color4 color = GLStateManager.getColor();
final int colorABGR = ColorABGR.pack(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
final int packedLight = ((int) GLSMConfig.lastBrightnessY << 16) | ((int) GLSMConfig.lastBrightnessX & 0xFFFF);
final int packedLight = GLSMConfig.packedLastBrightness();
final int mvGen = GLStateManager.getMvGeneration();
if (!mvCaptured || mvGen != lastMvGeneration) {
modelView.set(GLStateManager.getModelViewMatrix());
@@ -349,17 +379,21 @@ public final class ModelPartBatcher {
return entry;
}
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material) {
if (texture == lastLayerTexture && material == lastLayerMaterial) {
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits) {
if (texture == lastLayerTexture && material == lastLayerMaterial && pass.equals(lastLayerPass)
&& offsetFactor == lastLayerOffsetFactor && offsetUnits == lastLayerOffsetUnits) {
return lastLayer;
}
RenderLayer layer = layers.get(scratchKey.set(texture, material));
RenderLayer layer = layers.get(scratchKey.set(texture, material, pass, offsetFactor, offsetUnits));
if (layer == null) {
layer = RenderLayer.tesr(texture, material);
layers.put(new LayerKey().set(texture, material), layer);
layer = RenderLayer.tesr(texture, material, pass, offsetFactor, offsetUnits);
layers.put(new LayerKey().set(texture, material, pass, offsetFactor, offsetUnits), layer);
}
lastLayerTexture = texture;
lastLayerMaterial = material;
lastLayerPass = pass;
lastLayerOffsetFactor = offsetFactor;
lastLayerOffsetUnits = offsetUnits;
lastLayer = layer;
return layer;
}
@@ -12,6 +12,7 @@ import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import com.gtnewhorizons.angelica.glsm.states.Color4;
import com.gtnewhorizons.angelica.glsm.states.PolygonState;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource;
@@ -19,6 +20,7 @@ import net.coderbot.batchedentityrendering.impl.SegmentedBufferBuilder;
import net.coderbot.batchedentityrendering.impl.TransparencyType;
import net.coderbot.iris.Iris;
import net.coderbot.iris.layer.GbufferPrograms;
import net.coderbot.iris.layer.PassOverride;
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
import net.coderbot.iris.pipeline.FixedFunctionWorldRenderingPipeline;
import net.coderbot.iris.pipeline.WorldRenderingPhase;
@@ -129,13 +131,23 @@ public final class TesrBatchRenderer {
return activePass >= 0;
}
public boolean hasPendingGeometry() {
return activePass >= 0 || deferredFlushPending;
}
public void queue(TemplateBuffer template, ResourceLocation texture, TesrMaterial material) {
modelView.set(GLStateManager.getModelViewMatrix());
final int packedLight = currentPackedLight(material);
final int colorABGR = resolveColor(material);
if (activePass >= 0) {
final RenderLayer layer = layerFor(texture, material);
final int blockEntityId = CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
final boolean offset = GLStateManager.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL);
final PolygonState polygon = GLStateManager.getPolygonState();
final PassOverride pass = PassOverride.capture();
final RenderLayer layer = layerFor(texture, material, pass,
offset ? polygon.getOffsetFactor() : 0.0f, offset ? polygon.getOffsetUnits() : 0.0f);
final int blockEntityId = pass.isEntityPhase()
? Math.max(0, CapturedRenderingState.INSTANCE.getCurrentRenderedEntity())
: CapturedRenderingState.INSTANCE.getCurrentRenderedBlockEntity();
retained[activePass].queue(template, layer, material, modelView, packedLight, colorABGR, blockEntityId, captureTextureMatrix(), false);
} else {
drawImmediate(template, texture, material, packedLight, colorABGR);
@@ -159,8 +171,11 @@ public final class TesrBatchRenderer {
TesrMaterial.SpecialRender special;
TesrShader shader;
boolean noPass;
PassOverride pass;
float offsetFactor;
float offsetUnits;
LayerKey set(ResourceLocation texture, TesrMaterial.Transparency transparency, boolean noCull, boolean unlit, boolean noDepthWrite, boolean depthOnly, float cutoutAlpha, boolean depthEqual, TesrMaterial.SpecialRender special, TesrShader shader, boolean noPass) {
LayerKey set(ResourceLocation texture, TesrMaterial.Transparency transparency, boolean noCull, boolean unlit, boolean noDepthWrite, boolean depthOnly, float cutoutAlpha, boolean depthEqual, TesrMaterial.SpecialRender special, TesrShader shader, boolean noPass, PassOverride pass, float offsetFactor, float offsetUnits) {
this.texture = texture;
this.transparency = transparency;
this.noCull = noCull;
@@ -172,11 +187,14 @@ public final class TesrBatchRenderer {
this.special = special;
this.shader = shader;
this.noPass = noPass;
this.pass = pass;
this.offsetFactor = offsetFactor;
this.offsetUnits = offsetUnits;
return this;
}
LayerKey copy() {
return new LayerKey().set(texture, transparency, noCull, unlit, noDepthWrite, depthOnly, cutoutAlpha, depthEqual, special, shader, noPass);
return new LayerKey().set(texture, transparency, noCull, unlit, noDepthWrite, depthOnly, cutoutAlpha, depthEqual, special, shader, noPass, pass, offsetFactor, offsetUnits);
}
@Override
@@ -186,7 +204,10 @@ public final class TesrBatchRenderer {
&& noCull == other.noCull && unlit == other.unlit && noDepthWrite == other.noDepthWrite
&& depthOnly == other.depthOnly
&& Float.floatToIntBits(cutoutAlpha) == Float.floatToIntBits(other.cutoutAlpha)
&& depthEqual == other.depthEqual && special == other.special && shader == other.shader && noPass == other.noPass;
&& depthEqual == other.depthEqual && special == other.special && shader == other.shader && noPass == other.noPass
&& Objects.equals(pass, other.pass)
&& Float.floatToIntBits(offsetFactor) == Float.floatToIntBits(other.offsetFactor)
&& Float.floatToIntBits(offsetUnits) == Float.floatToIntBits(other.offsetUnits);
}
@Override
@@ -202,6 +223,9 @@ public final class TesrBatchRenderer {
h = h * 31 + Objects.hashCode(special);
h = h * 31 + System.identityHashCode(shader);
h = h * 31 + (noPass ? 1 : 0);
h = h * 31 + Objects.hashCode(pass);
h = h * 31 + Float.floatToIntBits(offsetFactor);
h = h * 31 + Float.floatToIntBits(offsetUnits);
return h;
}
}
@@ -210,18 +234,25 @@ public final class TesrBatchRenderer {
private final LayerKey scratchKey = new LayerKey();
private ResourceLocation lastLayerTexture;
private TesrMaterial lastLayerMaterial;
private PassOverride lastLayerPass;
private float lastLayerOffsetFactor;
private float lastLayerOffsetUnits;
private RenderLayer lastLayer;
private ResourceLocation lastImmediateTexture;
private TesrMaterial lastImmediateMaterial;
private RenderLayer lastImmediateLayer;
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material) {
if (texture == lastLayerTexture && material == lastLayerMaterial) {
private RenderLayer layerFor(ResourceLocation texture, TesrMaterial material, PassOverride pass, float offsetFactor, float offsetUnits) {
if (texture == lastLayerTexture && material == lastLayerMaterial && pass.equals(lastLayerPass)
&& offsetFactor == lastLayerOffsetFactor && offsetUnits == lastLayerOffsetUnits) {
return lastLayer;
}
final RenderLayer layer = layerLookup(texture, material, false);
final RenderLayer layer = layerLookup(texture, material, false, pass, offsetFactor, offsetUnits);
lastLayerTexture = texture;
lastLayerMaterial = material;
lastLayerPass = pass;
lastLayerOffsetFactor = offsetFactor;
lastLayerOffsetUnits = offsetUnits;
lastLayer = layer;
return layer;
}
@@ -230,18 +261,18 @@ public final class TesrBatchRenderer {
if (texture == lastImmediateTexture && material == lastImmediateMaterial) {
return lastImmediateLayer;
}
final RenderLayer layer = layerLookup(texture, material, true);
final RenderLayer layer = layerLookup(texture, material, true, PassOverride.NONE, 0.0f, 0.0f);
lastImmediateTexture = texture;
lastImmediateMaterial = material;
lastImmediateLayer = layer;
return layer;
}
private RenderLayer layerLookup(ResourceLocation texture, TesrMaterial material, boolean noPass) {
final LayerKey key = scratchKey.set(texture, material.transparency(), material.isNoCull(), material.isUnlit(), material.isNoDepthWrite(), material.isDepthOnly(), material.cutoutAlpha(), material.isDepthEqual(), material.special(), material.shader(), noPass);
private RenderLayer layerLookup(ResourceLocation texture, TesrMaterial material, boolean noPass, PassOverride pass, float offsetFactor, float offsetUnits) {
final LayerKey key = scratchKey.set(texture, material.transparency(), material.isNoCull(), material.isUnlit(), material.isNoDepthWrite(), material.isDepthOnly(), material.cutoutAlpha(), material.isDepthEqual(), material.special(), material.shader(), noPass, pass, offsetFactor, offsetUnits);
RenderLayer layer = layers.get(key);
if (layer == null) {
layer = noPass ? RenderLayer.tesrNoPass(texture, material) : RenderLayer.tesr(texture, material);
layer = noPass ? RenderLayer.tesrNoPass(texture, material) : RenderLayer.tesr(texture, material, pass, offsetFactor, offsetUnits);
layers.put(key.copy(), layer);
}
return layer;
@@ -249,9 +280,9 @@ public final class TesrBatchRenderer {
private static int currentPackedLight(TesrMaterial material) {
if (material.hasLightmap()) {
return ((int) material.lightmapY() << 16) | ((int) material.lightmapX() & 0xFFFF);
return GLSMConfig.packBrightness(material.lightmapX(), material.lightmapY());
}
return ((int) GLSMConfig.lastBrightnessY << 16) | ((int) GLSMConfig.lastBrightnessX & 0xFFFF);
return GLSMConfig.packedLastBrightness();
}
private static int resolveColor(TesrMaterial material) {
@@ -307,6 +338,7 @@ public final class TesrBatchRenderer {
try {
final EntityRenderer entityRenderer = Minecraft.getMinecraft().entityRenderer;
final boolean savedDepthMask = GLStateManager.getDepthState().isEnabled();
final boolean savedDepthTest = GLStateManager.getDepthTest().isEnabled();
final boolean savedBlend = GLStateManager.getBlendMode().isEnabled();
final int savedSrcRgb = GLStateManager.getBlendState().getSrcRgb();
final int savedDstRgb = GLStateManager.getBlendState().getDstRgb();
@@ -329,11 +361,12 @@ public final class TesrBatchRenderer {
bufferSource.endBatch(hook);
instancedRenderer.endFrame();
AngelicaBufferSource.rebindPass();
BatchingFontRenderer.flushDeferredText();
BatchingFontRenderer.flushDeferredText();
} finally {
if (wrap) GbufferPrograms.endBlockEntities();
entityRenderer.disableLightmap(0);
GLStateManager.glDepthMask(savedDepthMask);
if (savedDepthTest) GLStateManager.enableDepthTest(); else GLStateManager.disableDepthTest();
if (savedBlend) GLStateManager.enableBlend(); else GLStateManager.disableBlend();
GLStateManager.tryBlendFuncSeparate(savedSrcRgb, savedDstRgb, savedSrcAlpha, savedDstAlpha);
if (savedAlphaTest) GLStateManager.enableAlphaTest(); else GLStateManager.disableAlphaTest();
@@ -0,0 +1,53 @@
package com.gtnewhorizons.angelica.utils;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.tileentity.TileEntity;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.gtnewhorizons.angelica.mixins.interfaces.AwaitingDescriptorTE;
import com.gtnewhorizons.angelica.mixins.interfaces.SendsDescriptionPacket;
public final class AwaitingDescriptor {
private static final Logger LOGGER = LogManager.getLogger("Angelica");
private static final int WAIT_TICKS = 100;
private static final Set<Class<?>> NEVER_SENT = Collections.newSetFromMap(new ConcurrentHashMap<>());
private AwaitingDescriptor() {}
public static void begin(TileEntity te) {
if (!(te instanceof SendsDescriptionPacket) || NEVER_SENT.contains(te.getClass())) return;
final AwaitingDescriptorTE tile = (AwaitingDescriptorTE) te;
tile.angelica$setAwaitingDescriptor(true);
tile.angelica$setDescriptorWaitTicks(WAIT_TICKS);
}
public static void end(AwaitingDescriptorTE tile) {
tile.angelica$setAwaitingDescriptor(false);
tile.angelica$setDescriptorWaitTicks(0);
}
public static boolean defersUpdate(TileEntity te) {
final AwaitingDescriptorTE tile = (AwaitingDescriptorTE) te;
final int waiting = tile.angelica$getDescriptorWaitTicks();
if (waiting <= 0) return false;
tile.angelica$setDescriptorWaitTicks(waiting - 1);
if (waiting == 1) stopWaitingOn(te.getClass());
return true;
}
private static void stopWaitingOn(Class<?> cls) {
if (NEVER_SENT.add(cls)) {
LOGGER.warn("{} declares getDescriptionPacket() but never sent one", cls.getName());
}
}
}
@@ -98,6 +98,7 @@ public class TileLoader {
logger.severe("beforeChange was not called, invoking directly");
changeHandler.beforeChange();
}
long currentSize = Long.MIN_VALUE;
for (TileLoader loader : loaders) {
if (loader.isForThisMap(mapName)) {
if (loader.baseTextureMap == null) {
@@ -105,10 +106,13 @@ public class TileLoader {
loader.baseTexturesByName.putAll(map);
}
if (!loader.tilesToRegister.isEmpty()) {
if (currentSize == Long.MIN_VALUE) currentSize = getTextureSize(map.values());
loader.subLogger
.fine("adding icons to %s (%d remaining)", mapName, loader.tilesToRegister.size(), mapName);
while (!loader.tilesToRegister.isEmpty() && loader.registerOneIcon(textureMap, mapName, map)) {
// nothing
while (!loader.tilesToRegister.isEmpty()) {
long updatedSize = loader.registerOneIcon(textureMap, mapName, map, currentSize);
if (updatedSize < 0) break;
currentSize = updatedSize;
}
loader.subLogger.fine(
"done adding icons to %s (%d remaining)",
@@ -275,42 +279,44 @@ public class TileLoader {
return false;
}
protected boolean registerOneIcon(TextureMap textureMap, String mapName, Map<String, TextureAtlasSprite> map) {
protected long registerOneIcon(TextureMap textureMap, String mapName, Map<String, TextureAtlasSprite> map,
long currentSize) {
ResourceLocation resource = tilesToRegister.iterator()
.next();
String name = resource.toString();
if (registerDefaultIcon(name)) {
tilesToRegister.remove(resource);
return true;
return currentSize;
}
BufferedImage image = tileImages.get(resource);
if (image == null) {
subLogger.error("tile for %s unexpectedly missing", resource);
tilesToRegister.remove(resource);
return true;
return currentSize;
}
int width = image.getWidth();
int height = image.getHeight();
long currentSize = getTextureSize(map.values());
long newSize = 4 * width * width;
if (newSize + currentSize > MAX_TILESHEET_SIZE) {
float sizeMB = (float) currentSize / 1048576.0f;
if (currentSize <= 0) {
subLogger.error("%s too big for any tilesheet (%.1fMB), dropping", name, sizeMB);
tilesToRegister.remove(resource);
return true;
return currentSize;
} else {
subLogger.warning("%s nearly full (%.1fMB), will start a new tilesheet", mapName, sizeMB);
return false;
return -1;
}
}
TextureAtlasSprite previous = map.get(name);
TextureAtlasSprite icon = registerIconToMap(textureMap, resource, name);
map.put(name, icon);
currentSize += getTextureSize(icon) - getTextureSize(previous);
iconMap.put(name, icon);
String extra = (width == height ? "" : ", " + (height / width) + " frames");
subLogger.finer("%s -> %s icon %dx%d%s", name, mapName, width, width, extra);
tilesToRegister.remove(resource);
return true;
return currentSize;
}
protected TextureAtlasSprite registerIconToMap(TextureMap textureMap, ResourceLocation resource, String name) {
@@ -11,8 +11,6 @@ import net.minecraft.client.resources.I18n;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.util.MathHelper;
import org.lwjgl.opengl.Display;
import com.cardinalstar.cubicchunks.api.compat.CubicChunksVideoSettings;
import com.google.common.collect.ImmutableList;
import com.gtnewhorizons.angelica.compat.ModStatus;
@@ -21,7 +19,6 @@ import com.gtnewhorizons.angelica.rendering.celeritas.MultiDrawModeResolver;
import com.gtnewhorizons.angelica.rendering.culling.GpuCulling;
import com.gtnewhorizons.angelica.config.GpuCullingMode;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import cpw.mods.fml.common.Optional.Method;
import com.gtnewhorizons.angelica.glsm.streaming.StreamingUploader;
import jss.notfine.core.Settings;
@@ -48,6 +45,7 @@ import me.jellysquid.mods.sodium.client.gui.options.storage.MinecraftOptionsStor
import me.jellysquid.mods.sodium.client.gui.options.storage.SodiumOptionsStorage;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gui.option.IrisVideoSettings;
import net.coderbot.iris.pipeline.ShadowRenderer;
import org.embeddedt.embeddium.impl.render.chunk.occlusion.AsyncOcclusionMode;
public class SodiumGameOptionPages {
@@ -519,6 +517,36 @@ public class SodiumGameOptionPages {
.build());
if (Iris.enabled) {
final OptionImpl<AngelicaConfig, Integer> shadowGraphAngleDelta =
OptionImpl.createBuilder(int.class, angelicaOpts)
.setName(I18n.format("options.angelica.shadowGraphAngleDelta"))
.setTooltip(I18n.format("options.angelica.shadowGraphAngleDelta.tooltip"))
.setControl(o -> new SliderControl(o, 0, 100, 1, ControlValueFormatter.shadowGraphAngleDelta()))
.setImpact(OptionImpact.MEDIUM)
.setBinding((opts, value) -> AngelicaConfig.shadowGraphAngleDelta = value * 0.001f,
opts -> Math.round(AngelicaConfig.shadowGraphAngleDelta * 1000.0f))
.build();
shadowGraphAngleDelta.iris$dynamicallyEnable(ShadowRenderer::shadowGraphGateIsNotBypassed);
final OptionImpl<AngelicaConfig, Integer> shadowGraphHorizonScale =
OptionImpl.createBuilder(int.class, angelicaOpts)
.setName(I18n.format("options.angelica.shadowGraphHorizonScale"))
.setTooltip(I18n.format("options.angelica.shadowGraphHorizonScale.tooltip"))
.setControl(o -> new SliderControl(o, 0, 100, 1, ControlValueFormatter.shadowGraphHorizonScale()))
.setImpact(OptionImpact.LOW)
.setBinding((opts, value) -> AngelicaConfig.shadowGraphHorizonScale = value * 0.01f,
opts -> Math.round(AngelicaConfig.shadowGraphHorizonScale * 100.0f))
.build();
shadowGraphHorizonScale.iris$dynamicallyEnable(
() -> ShadowRenderer.shadowGraphGateIsNotBypassed() && shadowGraphAngleDelta.getValue() != 0);
groups.add(OptionGroup.createBuilder()
.add(shadowGraphAngleDelta)
.add(shadowGraphHorizonScale)
.build());
}
groups.add(OptionGroup.createBuilder()
.add(OptionImpl.createBuilder(boolean.class, angelicaOpts)
.setName(I18n.format("options.angelica.hudCaching"))
@@ -2,6 +2,8 @@ package me.jellysquid.mods.sodium.client.gui.options.control;
import net.minecraft.client.resources.I18n;
import java.util.Locale;
public interface ControlValueFormatter {
static ControlValueFormatter guiScale() {
return (v) -> (v == 0) ? I18n.format("options.guiScale.auto") : I18n.format(v + "x");
@@ -47,4 +49,14 @@ public interface ControlValueFormatter {
static ControlValueFormatter number() {
return String::valueOf;
}
static ControlValueFormatter shadowGraphAngleDelta() {
return (v) -> v == 0
? I18n.format("options.angelica.shadowGraphAngleDelta.always")
: String.format(Locale.ROOT, "%.3f°", v * 0.001f);
}
static ControlValueFormatter shadowGraphHorizonScale() {
return (v) -> v == 100 ? I18n.format("options.angelica.shadowGraphHorizonScale.off") : v + "%";
}
}
@@ -6,6 +6,8 @@ import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.coderbot.batchedentityrendering.impl.ordering.SimpleRenderOrderManager;
import net.coderbot.iris.Iris;
import net.coderbot.iris.layer.GbufferPrograms;
import net.coderbot.iris.pipeline.WorldRenderingPhase;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
import net.coderbot.iris.uniforms.CapturedRenderingState;
import org.lwjgl.opengl.GL11;
@@ -159,15 +161,17 @@ public class AngelicaBufferSource implements Groupable {
private void clearCurrentId() {
if (!anyIdSet) return;
if (idKind == GroupIdKind.ENTITY) {
CapturedRenderingState.INSTANCE.setCurrentEntity(-1);
CapturedRenderingState.INSTANCE.setCurrentEntityColor(0f, 0f, 0f, 0f);
} else {
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(0);
}
// A pass can set either kind: nested entities inside a TESR write the entity id during a block entity pass
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(-1, 0);
CapturedRenderingState.INSTANCE.setCurrentEntityColor(0f, 0f, 0f, 0f);
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(0);
anyIdSet = false;
}
private GroupIdKind effectiveIdKind() {
return GbufferPrograms.getCurrentPhase() == WorldRenderingPhase.ENTITIES ? GroupIdKind.ENTITY : idKind;
}
private void drawLayer(RenderLayer layer, LayerDrawHook hook) {
final List<BufferSegment> segments = typeToSegment.get(layer);
final boolean hasDynamic = segments != null && !segments.isEmpty();
@@ -184,7 +188,7 @@ public class AngelicaBufferSource implements Groupable {
GLStateManager.glPushMatrix();
GLStateManager.glLoadIdentity();
SegmentedBufferBuilder.LayerBuffer bound = null;
final boolean entityKind = idKind == GroupIdKind.ENTITY;
final boolean entityKind = effectiveIdKind() == GroupIdKind.ENTITY;
int currentId = Integer.MIN_VALUE;
int currentColor = 0;
boolean colorSet = false;
@@ -271,8 +275,8 @@ public class AngelicaBufferSource implements Groupable {
public void applyIdAndRebind(int id) {
anyIdSet = true;
if (idKind == GroupIdKind.ENTITY) {
CapturedRenderingState.INSTANCE.setCurrentEntity(id);
if (effectiveIdKind() == GroupIdKind.ENTITY) {
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(id, 0);
rebindPass();
} else {
setBlockEntityAndRebind(id);
@@ -281,8 +285,8 @@ public class AngelicaBufferSource implements Groupable {
public void applyIdNoRebind(int id) {
anyIdSet = true;
if (idKind == GroupIdKind.ENTITY) {
CapturedRenderingState.INSTANCE.setCurrentEntity(id);
if (effectiveIdKind() == GroupIdKind.ENTITY) {
CapturedRenderingState.INSTANCE.setCurrentEntityAndItem(id, 0);
} else {
CapturedRenderingState.INSTANCE.setCurrentBlockEntity(id);
}
@@ -54,6 +54,7 @@ import net.minecraft.launchwrapper.Launch;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.EnumChatFormatting;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.input.Keyboard;
import java.io.IOException;
@@ -919,6 +920,11 @@ public class Iris {
return pipelineManager;
}
@Nullable
public static PipelineManager getPipelineManagerNullable() {
return pipelineManager;
}
@NotNull
public static Optional<ShaderPack> getCurrentPack() {
return Optional.ofNullable(currentPack);
@@ -69,13 +69,17 @@ public class CeleritasTerrainPipeline {
IntFunction<ProgramImages> createTerrainImages,
IntFunction<ProgramImages> createShadowImages,
CustomUniforms customUniforms,
Optional<ProgramSource> terrainSource,
Optional<ProgramSource> terrainSolidSource,
Optional<ProgramSource> terrainCutoutSource,
Optional<ProgramSource> translucentSource,
Optional<ProgramSource> shadowSource,
Optional<ProgramSource> shadowSolidSource,
Optional<ProgramSource> shadowCutoutSource,
Optional<ProgramSource> shadowTranslucentSource,
CompletableFuture<Map<PatchShaderType, String>> terrainFuture,
CompletableFuture<Map<PatchShaderType, String>> terrainSolidFuture,
CompletableFuture<Map<PatchShaderType, String>> terrainCutoutFuture,
CompletableFuture<Map<PatchShaderType, String>> translucentFuture,
CompletableFuture<Map<PatchShaderType, String>> shadowFuture,
CompletableFuture<Map<PatchShaderType, String>> shadowSolidFuture,
CompletableFuture<Map<PatchShaderType, String>> shadowCutoutFuture,
CompletableFuture<Map<PatchShaderType, String>> shadowTranslucentFuture,
RenderTargets renderTargets,
ImmutableSet<Integer> flippedAfterPrepare,
@@ -90,11 +94,12 @@ public class CeleritasTerrainPipeline {
// Build program source map (local - only needed during construction)
final EnumMap<IrisTerrainPass, Optional<ProgramSource>> gbufferProgramSource = new EnumMap<>(IrisTerrainPass.class);
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_SOLID, terrainSource);
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_CUTOUT, terrainSource);
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_TRANSLUCENT, translucentSource.isPresent() ? translucentSource : terrainSource);
gbufferProgramSource.put(IrisTerrainPass.SHADOW, shadowSource);
gbufferProgramSource.put(IrisTerrainPass.SHADOW_TRANSLUCENT, shadowTranslucentSource.isPresent() ? shadowTranslucentSource : shadowSource);
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_SOLID, terrainSolidSource);
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_CUTOUT, terrainCutoutSource);
gbufferProgramSource.put(IrisTerrainPass.GBUFFER_TRANSLUCENT, translucentSource);
gbufferProgramSource.put(IrisTerrainPass.SHADOW, shadowSolidSource);
gbufferProgramSource.put(IrisTerrainPass.SHADOW_CUTOUT, shadowCutoutSource);
gbufferProgramSource.put(IrisTerrainPass.SHADOW_TRANSLUCENT, shadowTranslucentSource);
// Initialize PassInfo, framebuffers, blend modes, and alpha in single pass
for (IrisTerrainPass pass : IrisTerrainPass.VALUES) {
@@ -103,10 +108,12 @@ public class CeleritasTerrainPipeline {
// Set up framebuffer, blend mode, and buffer blend overrides
final ProgramId programId = switch (pass) {
case GBUFFER_SOLID -> ProgramId.TerrainSolid;
case GBUFFER_CUTOUT -> ProgramId.TerrainCutout;
case GBUFFER_TRANSLUCENT -> ProgramId.Water;
case SHADOW_TRANSLUCENT -> ProgramId.ShadowWater;
case SHADOW, SHADOW_CUTOUT -> ProgramId.Shadow;
default -> ProgramId.Terrain;
case SHADOW -> ProgramId.ShadowSolid;
case SHADOW_CUTOUT -> ProgramId.ShadowCutout;
};
if (pass.isShadow()) {
@@ -146,10 +153,12 @@ public class CeleritasTerrainPipeline {
}
// Process and apply shader sources
processShaderFuture(terrainFuture, terrainSource, passInfoMap.get(IrisTerrainPass.GBUFFER_SOLID), passInfoMap.get(IrisTerrainPass.GBUFFER_CUTOUT));
processShaderFuture(terrainSolidFuture, terrainSolidSource, passInfoMap.get(IrisTerrainPass.GBUFFER_SOLID));
processShaderFuture(terrainCutoutFuture, terrainCutoutSource, passInfoMap.get(IrisTerrainPass.GBUFFER_CUTOUT));
processShaderFuture(translucentFuture, translucentSource, passInfoMap.get(IrisTerrainPass.GBUFFER_TRANSLUCENT));
processShaderFuture(shadowFuture, shadowSource, passInfoMap.get(IrisTerrainPass.SHADOW), passInfoMap.get(IrisTerrainPass.SHADOW_CUTOUT));
processShaderFuture(shadowTranslucentFuture, shadowTranslucentSource.isPresent() ? shadowTranslucentSource : shadowSource, passInfoMap.get(IrisTerrainPass.SHADOW_TRANSLUCENT));
processShaderFuture(shadowSolidFuture, shadowSolidSource, passInfoMap.get(IrisTerrainPass.SHADOW));
processShaderFuture(shadowCutoutFuture, shadowCutoutSource, passInfoMap.get(IrisTerrainPass.SHADOW_CUTOUT));
processShaderFuture(shadowTranslucentFuture, shadowTranslucentSource, passInfoMap.get(IrisTerrainPass.SHADOW_TRANSLUCENT));
}
private void processShaderFuture(@Nullable CompletableFuture<Map<PatchShaderType, String>> future, Optional<ProgramSource> source, PassInfo... targets) {
@@ -1,22 +0,0 @@
package net.coderbot.iris.fantastic;
// TODO: RenderType
//public class IrisParticleRenderTypes {
// public static final ParticleRenderType OPAQUE_TERRAIN = new ParticleRenderType() {
// public void begin(BufferBuilder bufferBuilder, TextureManager textureManager) {
// GL11.glDisable(GL11.GL_BLEND);
// GL11.glDepthMask(true);
// GL11.glAlphaFunc(GL11.GL_GREATER, 0.1F);
// textureManager.bind(TextureAtlas.LOCATION_BLOCKS);
// bufferBuilder.begin(7, DefaultVertexFormat.PARTICLE);
// }
//
// public void end(Tesselator tesselator) {
// tesselator.end();
// }
//
// public String toString() {
// return "OPAQUE_TERRAIN_SHEET";
// }
// };
//}
@@ -12,6 +12,8 @@ public enum RenderCondition {
BEACON_BEAM,
ENTITIES,
ENTITIES_TRANSLUCENT,
PARTICLES,
PARTICLES_TRANSLUCENT,
GLINT,
ENTITY_EYES,
HAND_OPAQUE,
@@ -0,0 +1,24 @@
package net.coderbot.iris.gbuffer_overrides.matching;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.BlendState;
import org.lwjgl.opengl.GL11;
/**
* Decides whether a draw is blending.
*/
public final class TranslucentBlendMatcher {
/** Render thread only. */
private static final BlendState scratch = new BlendState();
private TranslucentBlendMatcher() {}
public static boolean matchesCurrentState() {
if (!GLStateManager.isEffectiveBlendEnabled()) {
return false;
}
GLStateManager.getEffectiveBlendState(scratch);
return scratch.getSrcRgb() == GL11.GL_SRC_ALPHA && scratch.getDstRgb() == GL11.GL_ONE_MINUS_SRC_ALPHA;
}
}
@@ -3,57 +3,114 @@ package net.coderbot.iris.gl.blending;
import com.gtnewhorizons.angelica.glsm.states.AlphaTestFunction;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaBooleanLayer;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
import lombok.Getter;
import org.lwjgl.opengl.GL11;
public class AlphaTestStorage {
private static boolean originalAlphaTestEnable;
private static AlphaTest originalAlphaTest;
@Getter
private static boolean vanillaEnabled;
private static int vanillaFunction = GL11.GL_ALWAYS;
private static float vanillaReference;
@Getter
private static boolean alphaTestLocked;
private static boolean overrideHeld;
public static void overrideAlphaTest(AlphaTest override) {
if (!alphaTestLocked) {
if (!overrideHeld) {
final AlphaState alphaState = GLStateManager.getAlphaState();
// Only save the previous state if the alpha test wasn't already locked
originalAlphaTestEnable = GLStateManager.getAlphaTest().isEnabled();
originalAlphaTest = new AlphaTest(AlphaTestFunction.fromGlId(alphaState.getFunction()).orElse(AlphaTestFunction.ALWAYS), alphaState.getReference());
}
// Only save the previous state if the alpha test wasn't already overridden
vanillaEnabled = GLStateManager.getAlphaTest().isEnabled();
vanillaFunction = alphaState.getFunction();
vanillaReference = alphaState.getReference();
}
alphaTestLocked = false;
overrideHeld = true;
if (override == null) {
GLStateManager.disableAlphaTest();
} else {
if (override == null) {
apply(false, GL11.GL_ALWAYS, 0.0f);
} else {
apply(true, override.getFunction().getGlId(), override.getReference());
}
}
private static void apply(boolean enabled, int function, float reference) {
alphaTestLocked = false;
try {
if (enabled) {
GLStateManager.enableAlphaTest();
GLStateManager.glAlphaFunc(function, reference);
} else {
GLStateManager.disableAlphaTest();
}
} finally {
alphaTestLocked = true;
}
}
public static void deferAlphaTestToggle(boolean enabled) {
vanillaEnabled = enabled;
}
public static void deferAlphaFunc(int function, float reference) {
vanillaFunction = function;
vanillaReference = reference;
}
public static void restoreAlphaTest() {
if (!overrideHeld) {
return;
}
alphaTestLocked = false;
if (vanillaEnabled) {
GLStateManager.enableAlphaTest();
GLStateManager.glAlphaFunc(override.getFunction().getGlId(), override.getReference());
}
alphaTestLocked = true;
}
public static void deferAlphaTestToggle(boolean enabled) {
originalAlphaTestEnable = enabled;
}
public static void deferAlphaFunc(int function, float reference) {
originalAlphaTest = new AlphaTest(AlphaTestFunction.fromGlId(function).get(), reference);
}
public static void restoreAlphaTest() {
if (!alphaTestLocked) {
return;
}
alphaTestLocked = false;
if (originalAlphaTestEnable) {
GLStateManager.enableAlphaTest();
} else {
} else {
GLStateManager.disableAlphaTest();
}
}
GLStateManager.glAlphaFunc(originalAlphaTest.getFunction().getGlId(), originalAlphaTest.getReference());
}
GLStateManager.glAlphaFunc(vanillaFunction, vanillaReference);
overrideHeld = false;
}
public static final VanillaBooleanLayer ENABLE_LAYER = new VanillaBooleanLayer() {
@Override
public boolean isOverrideHeld() {
return overrideHeld;
}
@Override
public boolean getVanilla() {
return vanillaEnabled;
}
@Override
public void setVanilla(boolean enabled) {
vanillaEnabled = enabled;
}
};
public static final VanillaStateLayer<AlphaState> FUNC_LAYER = new VanillaStateLayer<>() {
@Override
public boolean isOverrideHeld() {
return overrideHeld;
}
@Override
public void readVanilla(AlphaState into) {
into.setFunction(vanillaFunction);
into.setReference(vanillaReference);
}
@Override
public void writeVanilla(AlphaState from) {
vanillaFunction = from.getFunction();
vanillaReference = from.getReference();
}
};
}
@@ -2,126 +2,187 @@ package net.coderbot.iris.gl.blending;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaBooleanLayer;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import com.gtnewhorizons.angelica.glsm.states.BlendState;
import lombok.Getter;
public class BlendModeStorage {
private static final int MAX_DRAW_BUFFERS = 8;
private static final int MAX_DRAW_BUFFERS = 8;
private static boolean originalBlendEnable;
private static final BlendState originalBlend = new BlendState();
@Getter private static boolean blendLocked;
@Getter private static boolean hasDeferredChanges;
private static final int[] bufferOverrideIndices = new int[MAX_DRAW_BUFFERS];
private static final boolean[] bufferOverrideIsDisable = new boolean[MAX_DRAW_BUFFERS];
private static final BlendState[] bufferOverrideStates = new BlendState[MAX_DRAW_BUFFERS];
private static int bufferOverrideCount;
private static boolean vanillaBlendEnable;
private static final BlendState vanillaBlend = new BlendState();
static {
for (int i = 0; i < MAX_DRAW_BUFFERS; i++) {
bufferOverrideStates[i] = new BlendState();
}
}
@Getter
private static boolean blendLocked;
@Getter
private static boolean overrideHeld;
private static boolean hasGlobalOverride;
private static boolean hasDeferredChanges;
private static final int[] bufferOverrideIndices = new int[MAX_DRAW_BUFFERS];
private static final boolean[] bufferOverrideIsDisable = new boolean[MAX_DRAW_BUFFERS];
private static final BlendState[] bufferOverrideStates = new BlendState[MAX_DRAW_BUFFERS];
private static int bufferOverrideCount;
private static boolean bufferOverridesDirty;
static {
for (int i = 0; i < MAX_DRAW_BUFFERS; i++) {
bufferOverrideStates[i] = new BlendState();
}
}
public static void overrideBlend(BlendState override) {
if (!blendLocked) {
// Only save the previous state if the blend mode wasn't already locked
originalBlendEnable = GLStateManager.getBlendMode().isEnabled();
originalBlend.set(GLStateManager.getBlendState());
}
captureVanilla();
bufferOverrideCount = 0;
hasDeferredChanges = false;
blendLocked = false;
bufferOverrideCount = 0;
bufferOverridesDirty = false;
hasDeferredChanges = false;
overrideHeld = true;
hasGlobalOverride = true;
if (override == null) {
GLStateManager.disableBlend();
} else {
GLStateManager.enableBlend();
GLStateManager.tryBlendFuncSeparate(override.getSrcRgb(), override.getDstRgb(), override.getSrcAlpha(), override.getDstAlpha());
}
blendLocked = false;
try {
if (override == null) {
GLStateManager.disableBlend();
} else {
GLStateManager.enableBlend();
GLStateManager.tryBlendFuncSeparate(override.getSrcRgb(), override.getDstRgb(), override.getSrcAlpha(), override.getDstAlpha());
}
} finally {
blendLocked = true;
}
}
blendLocked = true;
}
public static void overrideBufferBlend(int index, BlendState override) {
captureVanilla();
public static void overrideBufferBlend(int index, BlendState override) {
if (!blendLocked) {
// Only save the previous state if the blend mode wasn't already locked
originalBlendEnable = GLStateManager.getBlendMode().isEnabled();
originalBlend.set(GLStateManager.getBlendState());
}
if (override == null) {
RenderSystem.disableBufferBlend(index);
} else {
RenderSystem.enableBufferBlend(index);
RenderSystem.blendFuncSeparatei(index, override.getSrcRgb(), override.getDstRgb(), override.getSrcAlpha(), override.getDstAlpha());
}
if (override == null) {
RenderSystem.disableBufferBlend(index);
} else {
RenderSystem.enableBufferBlend(index);
RenderSystem.blendFuncSeparatei(index, override.getSrcRgb(), override.getDstRgb(), override.getSrcAlpha(), override.getDstAlpha());
}
if (bufferOverrideCount < MAX_DRAW_BUFFERS) {
final int slot = bufferOverrideCount++;
bufferOverrideIndices[slot] = index;
bufferOverrideIsDisable[slot] = (override == null);
if (override != null) {
bufferOverrideStates[slot].setAll(override.getSrcRgb(), override.getDstRgb(), override.getSrcAlpha(), override.getDstAlpha());
}
}
overrideHeld = true;
blendLocked = true;
}
if (bufferOverrideCount < MAX_DRAW_BUFFERS) {
final int slot = bufferOverrideCount++;
bufferOverrideIndices[slot] = index;
bufferOverrideIsDisable[slot] = (override == null);
if (override != null) {
bufferOverrideStates[slot].setAll(override.getSrcRgb(), override.getDstRgb(), override.getSrcAlpha(), override.getDstAlpha());
}
}
blendLocked = true;
}
private static void captureVanilla() {
if (!overrideHeld) {
vanillaBlendEnable = GLStateManager.getBlendMode().isEnabled();
vanillaBlend.set(GLStateManager.getBlendState());
}
}
public static void deferBlendModeToggle(boolean enabled) {
originalBlendEnable = enabled;
hasDeferredChanges = true;
}
public static void deferBlendModeToggle(boolean enabled) {
vanillaBlendEnable = enabled;
hasDeferredChanges = true;
}
public static void deferBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
originalBlend.setAll(srcRgb, dstRgb, srcAlpha, dstAlpha);
hasDeferredChanges = true;
}
public static void deferBlendFunc(int srcRgb, int dstRgb, int srcAlpha, int dstAlpha) {
vanillaBlend.setAll(srcRgb, dstRgb, srcAlpha, dstAlpha);
hasDeferredChanges = true;
}
public static void flushDeferredBlend() {
if (!blendLocked || !hasDeferredChanges) return;
hasDeferredChanges = false;
blendLocked = false;
public static void flushDeferredBlend() {
if (!overrideHeld) return;
if (originalBlendEnable) {
GLStateManager.enableBlend();
} else {
GLStateManager.disableBlend();
}
GLStateManager.tryBlendFuncSeparate(
originalBlend.getSrcRgb(), originalBlend.getDstRgb(),
originalBlend.getSrcAlpha(), originalBlend.getDstAlpha());
if (hasDeferredChanges && !hasGlobalOverride) {
hasDeferredChanges = false;
applyVanilla();
bufferOverridesDirty = true;
}
blendLocked = true;
if (bufferOverridesDirty) {
bufferOverridesDirty = false;
reassertBufferOverrides();
}
}
for (int i = 0; i < bufferOverrideCount; i++) {
final int idx = bufferOverrideIndices[i];
if (bufferOverrideIsDisable[i]) {
RenderSystem.disableBufferBlend(idx);
} else {
final BlendState s = bufferOverrideStates[i];
RenderSystem.enableBufferBlend(idx);
RenderSystem.blendFuncSeparatei(idx, s.getSrcRgb(), s.getDstRgb(), s.getSrcAlpha(), s.getDstAlpha());
}
}
}
private static void reassertBufferOverrides() {
for (int i = 0; i < bufferOverrideCount; i++) {
final int idx = bufferOverrideIndices[i];
if (bufferOverrideIsDisable[i]) {
RenderSystem.disableBufferBlend(idx);
} else {
final BlendState s = bufferOverrideStates[i];
RenderSystem.enableBufferBlend(idx);
RenderSystem.blendFuncSeparatei(idx, s.getSrcRgb(), s.getDstRgb(), s.getSrcAlpha(), s.getDstAlpha());
}
}
}
public static void restoreBlend() {
if (!blendLocked) {
return;
}
public static void restoreBlend() {
if (!overrideHeld) {
return;
}
blendLocked = false;
hasDeferredChanges = false;
bufferOverrideCount = 0;
hasGlobalOverride = false;
hasDeferredChanges = false;
bufferOverrideCount = 0;
bufferOverridesDirty = false;
blendLocked = false;
if (originalBlendEnable) {
GLStateManager.enableBlend();
} else {
GLStateManager.disableBlend();
}
applyVanilla();
overrideHeld = false;
}
GLStateManager.tryBlendFuncSeparate(originalBlend.getSrcRgb(), originalBlend.getDstRgb(), originalBlend.getSrcAlpha(), originalBlend.getDstAlpha());
}
private static void applyVanilla() {
final boolean wasLocked = blendLocked;
blendLocked = false;
try {
if (vanillaBlendEnable) {
GLStateManager.enableBlend();
} else {
GLStateManager.disableBlend();
}
GLStateManager.tryBlendFuncSeparate(vanillaBlend.getSrcRgb(), vanillaBlend.getDstRgb(), vanillaBlend.getSrcAlpha(), vanillaBlend.getDstAlpha());
} finally {
blendLocked = wasLocked;
}
}
public static final VanillaBooleanLayer ENABLE_LAYER = new VanillaBooleanLayer() {
@Override
public boolean isOverrideHeld() {
return overrideHeld;
}
@Override
public boolean getVanilla() {
return vanillaBlendEnable;
}
@Override
public void setVanilla(boolean enabled) {
deferBlendModeToggle(enabled);
}
};
public static final VanillaStateLayer<BlendState> FUNC_LAYER = new VanillaStateLayer<>() {
@Override
public boolean isOverrideHeld() {
return overrideHeld;
}
@Override
public void readVanilla(BlendState into) {
into.setAll(vanillaBlend.getSrcRgb(), vanillaBlend.getDstRgb(), vanillaBlend.getSrcAlpha(), vanillaBlend.getDstAlpha());
}
@Override
public void writeVanilla(BlendState from) {
deferBlendFunc(from.getSrcRgb(), from.getDstRgb(), from.getSrcAlpha(), from.getDstAlpha());
}
};
}
@@ -3,64 +3,105 @@ package net.coderbot.iris.gl.blending;
import com.gtnewhorizons.angelica.glsm.states.DepthState;
import com.gtnewhorizons.angelica.glsm.states.ColorMask;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.VanillaStateLayer;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import lombok.Getter;
public class DepthColorStorage {
private static boolean originalDepthEnable;
private static net.coderbot.iris.gl.blending.ColorMask originalColor;
@Getter
private static boolean vanillaDepthEnable;
private static final ColorMask vanillaColor = new ColorMask();
@Getter
private static boolean depthColorLocked;
private static final IntOpenHashSet ownedPrograms = new IntOpenHashSet();
@Getter
private static boolean overrideHeld;
public static void registerOwnedProgram(int programId) {
ownedPrograms.add(programId);
}
private static final IntOpenHashSet ownedPrograms = new IntOpenHashSet();
public static void unregisterOwnedProgram(int programId) {
ownedPrograms.remove(programId);
}
public static void registerOwnedProgram(int programId) {
ownedPrograms.add(programId);
}
public static boolean isOwnedProgram(int programId) {
return ownedPrograms.contains(programId);
}
public static void unregisterOwnedProgram(int programId) {
ownedPrograms.remove(programId);
}
public static boolean isOwnedProgram(int programId) {
return ownedPrograms.contains(programId);
}
public static void disableDepthColor() {
if (!depthColorLocked) {
// Only save the previous state if the depth and color mask wasn't already locked
final ColorMask colorMask = GLStateManager.getColorMask();
final DepthState depthState = GLStateManager.getDepthState();
if (!overrideHeld) {
final ColorMask colorMask = GLStateManager.getColorMask();
final DepthState depthState = GLStateManager.getDepthState();
originalDepthEnable = depthState.isEnabled();
originalColor = new net.coderbot.iris.gl.blending.ColorMask(colorMask.red, colorMask.green, colorMask.blue, colorMask.alpha);
}
vanillaDepthEnable = depthState.isEnabled();
vanillaColor.set(colorMask);
}
depthColorLocked = false;
overrideHeld = true;
GLStateManager.glDepthMask(false);
GLStateManager.glColorMask(false, false, false, false);
depthColorLocked = false;
try {
GLStateManager.glDepthMask(false);
GLStateManager.glColorMask(false, false, false, false);
} finally {
depthColorLocked = true;
}
}
depthColorLocked = true;
}
public static void deferDepthEnable(boolean enabled) {
vanillaDepthEnable = enabled;
}
public static void deferDepthEnable(boolean enabled) {
originalDepthEnable = enabled;
}
public static void deferColorMask(boolean red, boolean green, boolean blue, boolean alpha) {
vanillaColor.setAll(red, green, blue, alpha);
}
public static void deferColorMask(boolean red, boolean green, boolean blue, boolean alpha) {
originalColor = new net.coderbot.iris.gl.blending.ColorMask(red, green, blue, alpha);
}
public static void unlockDepthColor() {
if (!overrideHeld) {
return;
}
public static void unlockDepthColor() {
if (!depthColorLocked) {
return;
}
depthColorLocked = false;
depthColorLocked = false;
GLStateManager.glDepthMask(vanillaDepthEnable);
GLStateManager.glColorMask(vanillaColor.red, vanillaColor.green, vanillaColor.blue, vanillaColor.alpha);
overrideHeld = false;
}
GLStateManager.glDepthMask(originalDepthEnable);
public static final VanillaStateLayer<DepthState> DEPTH_LAYER = new VanillaStateLayer<>() {
@Override
public boolean isOverrideHeld() {
return overrideHeld;
}
GLStateManager.glColorMask(originalColor.isRedMasked(), originalColor.isGreenMasked(), originalColor.isBlueMasked(), originalColor.isAlphaMasked());
}
@Override
public void readVanilla(DepthState into) {
into.setEnabled(vanillaDepthEnable);
}
@Override
public void writeVanilla(DepthState from) {
vanillaDepthEnable = from.isEnabled();
}
};
public static final VanillaStateLayer<ColorMask> COLOR_LAYER = new VanillaStateLayer<>() {
@Override
public boolean isOverrideHeld() {
return overrideHeld;
}
@Override
public void readVanilla(ColorMask into) {
into.set(vanillaColor);
}
@Override
public void writeVanilla(ColorMask from) {
vanillaColor.set(from);
}
};
}
@@ -36,7 +36,6 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
@Getter
private ShaderPackEntry applied = null;
@Setter
@Getter
private ShaderPackEntry selected = null;
@@ -203,6 +202,11 @@ public class ShaderPackSelectionList extends IrisGuiSlot {
}
}
public void setSelected(ShaderPackEntry selected) {
this.selected = selected;
this.screen.refreshScreenSwitchButton();
}
public void select(String name) {
for (BaseEntry entry : this.entries) {
if (entry instanceof ShaderPackEntry shaderPackEntry && name.equals(shaderPackEntry.getPackName())) {
@@ -39,8 +39,10 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
@@ -85,6 +87,8 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
private boolean guiHidden = false;
private boolean dirty = false;
private final Map<String, Boolean> packOptionAvailability = new HashMap<>();
public ShaderPackScreen(GuiScreen parent) {
this.title = I18n.format("options.iris.shaderPackSelection.title");
@@ -250,19 +254,7 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
I18n.format("options.iris.openShaderPackFolder"), button -> this.openShaderPackFolder()));
this.screenSwitchButton = new IrisButton(topCenter + 78, this.height - 51, 152, 20,
I18n.format("options.iris.shaderPackList"), button -> {
this.optionMenuOpen = !this.optionMenuOpen;
// UX: Apply changes before switching screens to avoid unintuitive behavior
//
// Not doing this leads to unintuitive behavior, since selecting a pack in the
// list (but not applying) would open the settings for the previous pack, rather
// than opening the settings for the selected (but not applied) pack.
this.applyChanges();
initGui();
}
);
I18n.format("options.iris.shaderPackList"), button -> this.toggleOptionMenu());
this.buttonList.add(this.screenSwitchButton);
refreshScreenSwitchButton();
@@ -299,6 +291,17 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
}
@Override
public void updateScreen() {
super.updateScreen();
if (this.notificationDialogTimer > 0) {
this.notificationDialogTimer--;
} else if (this.notificationDialog != null) {
this.notificationDialog = null;
}
}
/**
* Called when the mouse is clicked.
*/
@@ -346,10 +349,52 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
}
}
/**
* Switches between the pack list and the pack settings, applying the pending pack first.
* <p>
* UX: Apply changes before switching screens to avoid unintuitive behavior. Not doing this leads to
* unintuitive behavior, since selecting a pack in the list (but not applying) would open the settings
* for the previous pack, rather than opening the settings for the selected (but not applied) pack.
*/
private void toggleOptionMenu() {
final boolean opening = !this.optionMenuOpen;
this.optionMenuOpen = opening;
this.applyChanges();
// The pack is only parsed by applyChanges, so this is the first point where an option-less
// pack can be recognized. Stay on the list instead of opening an empty settings screen.
if (opening && !currentPackHasOptions()) {
this.optionMenuOpen = false;
displayNotification(I18n.format("options.iris.shaderPackOptions.noOptions", Iris.getCurrentPackName()));
}
initGui();
}
private boolean canOpenOptionMenu() {
if (!this.shaderPackList.getTopButtonRow().shadersEnabled) {
return false;
}
final ShaderPackEntry highlighted = this.shaderPackList.getSelected();
final String name = highlighted != null ? highlighted.getPackName() : Iris.getCurrentPackName();
return name == null || this.packOptionAvailability.getOrDefault(name, Boolean.TRUE);
}
private boolean currentPackHasOptions() {
return Iris.getCurrentPack().map(p -> p.getMenuContainer().hasMenuElements()).orElse(false);
}
public void refreshForChangedPack() {
if (Iris.getCurrentPack().isPresent()) {
final ShaderPack currentPack = Iris.getCurrentPack().get();
if (Iris.getCurrentPackName() != null) {
this.packOptionAvailability.put(Iris.getCurrentPackName(), currentPack.getMenuContainer().hasMenuElements());
}
this.navigation = new NavigationController();
if (this.shaderOptionList != null) {
@@ -364,11 +409,10 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
}
public void refreshScreenSwitchButton() {
if (this.screenSwitchButton != null) {
if (this.screenSwitchButton != null && this.shaderPackList != null) {
this.screenSwitchButton.displayString = optionMenuOpen ? I18n.format("options.iris.shaderPackList") : I18n.format("options.iris.shaderPackSettings");
// Disable the settings switch when shaders are off or the selected pack exposes no options
this.screenSwitchButton.enabled = optionMenuOpen || (shaderPackList.getTopButtonRow().shadersEnabled
&& Iris.getCurrentPack().map(p -> !p.getMenuContainer().mainScreen.elements.isEmpty()).orElse(true));
// Disable the settings switch when shaders are off or the highlighted pack exposes no options
this.screenSwitchButton.enabled = optionMenuOpen || canOpenOptionMenu();
}
}
@@ -435,12 +479,8 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
return;
}
if (keyCode == Keyboard.KEY_TAB) {
final boolean canOpenOptions = shaderPackList.getTopButtonRow().shadersEnabled
&& Iris.getCurrentPack().map(p -> !p.getMenuContainer().mainScreen.elements.isEmpty()).orElse(false);
if (this.optionMenuOpen || canOpenOptions) {
this.optionMenuOpen = !this.optionMenuOpen;
this.applyChanges();
this.initGui();
if (this.optionMenuOpen || canOpenOptionMenu()) {
toggleOptionMenu();
}
return;
}
@@ -1,24 +1,32 @@
package net.coderbot.iris.layer;
import lombok.Getter;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.states.AlphaState;
import com.gtnewhorizons.angelica.glsm.states.BlendState;
import net.coderbot.iris.gl.shader.ProgramCreator;
import net.coderbot.iris.gl.state.StateUpdateNotifiers;
import net.coderbot.iris.pipeline.WorldRenderingPhase;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import java.util.Arrays;
public class GbufferPrograms {
private static boolean entities;
private static boolean blockEntities;
private static boolean particles;
private static boolean outline;
private static boolean entityLoop;
private static Runnable phaseChangeListener;
static {
StateUpdateNotifiers.phaseChangeNotifier = listener -> phaseChangeListener = listener;
}
private static void checkReentrancy() {
if (entities || blockEntities || outline) {
throw new IllegalStateException("GbufferPrograms in weird state, tried to call begin function when entities = "
@@ -61,8 +69,18 @@ public class GbufferPrograms {
throw new IllegalStateException("GbufferPrograms in weird state, tried to call endEntities when entities = false");
}
setPhase(WorldRenderingPhase.NONE);
entities = false;
endPhase();
}
private static void endPhase() {
if (particles) {
setPhase(WorldRenderingPhase.PARTICLES);
} else if (entityLoop) {
setPhase(WorldRenderingPhase.ENTITIES);
} else {
setPhase(WorldRenderingPhase.NONE);
}
}
public static void beginOutline() {
@@ -76,10 +94,41 @@ public class GbufferPrograms {
throw new IllegalStateException("GbufferPrograms in weird state, tried to call endOutline when outline = false");
}
setPhase(WorldRenderingPhase.NONE);
outline = false;
endPhase();
}
public static int beginParticles() {
if (particleDepth == particlePhaseSaves.length) {
particlePhaseSaves = Arrays.copyOf(particlePhaseSaves, particleDepth * 2);
particleTranslucencySaves = Arrays.copyOf(particleTranslucencySaves, particleDepth * 2);
}
particlePhaseSaves[particleDepth] = getCurrentPhase();
particleTranslucencySaves[particleDepth] = beginTranslucencyDeclaration(Boolean.FALSE);
setPhase(WorldRenderingPhase.PARTICLES);
particles = true;
return particleDepth++;
}
public static void endParticles(int depth) {
particleDepth = depth;
endTranslucencyDeclaration(particleTranslucencySaves[depth]);
particleTranslucencySaves[depth] = null;
setPhase(particlePhaseSaves[depth]);
particlePhaseSaves[depth] = null;
particles = particleDepth > 0;
}
private static WorldRenderingPhase[] particlePhaseSaves = new WorldRenderingPhase[4];
private static Boolean[] particleTranslucencySaves = new Boolean[4];
private static int particleDepth;
public static void beginBlockEntities() {
checkReentrancy();
setPhase(WorldRenderingPhase.BLOCK_ENTITIES);
@@ -92,8 +141,101 @@ public class GbufferPrograms {
throw new IllegalStateException("GbufferPrograms in weird state, tried to call endBlockEntities when blockEntities = false");
}
setPhase(WorldRenderingPhase.NONE);
blockEntities = false;
endPhase();
}
public static void setCutoutDefaults() {
GLStateManager.enableAlphaTest();
GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.1F);
final int previousUnit = GLStateManager.getActiveTextureUnitForServerState();
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
GLStateManager.enableTexture();
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + previousUnit);
}
public static long pushCutoutDefaults() {
final long saved = packCutoutState();
setCutoutDefaults();
return saved;
}
public static void popCutoutDefaults(long saved) {
if ((saved & ALPHA_ENABLED_BIT) != 0) {
GLStateManager.enableAlphaTest();
} else {
GLStateManager.disableAlphaTest();
}
GLStateManager.glAlphaFunc((int) ((saved >>> 32) & 0xFFFF), Float.intBitsToFloat((int) saved));
if ((saved & LIGHTMAP_ENABLED_BIT) == 0) {
final int previousUnit = GLStateManager.getActiveTextureUnitForServerState();
GLStateManager.glActiveTexture(GL13.GL_TEXTURE1);
GLStateManager.disableTexture();
GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + previousUnit);
}
}
public static int pushBlendState() {
if (blendDepth == blendSaves.length) {
blendSaves = Arrays.copyOf(blendSaves, blendDepth * 2);
blendEnabledSaves = Arrays.copyOf(blendEnabledSaves, blendDepth * 2);
}
BlendState saved = blendSaves[blendDepth];
if (saved == null) {
saved = new BlendState();
blendSaves[blendDepth] = saved;
}
GLStateManager.getEffectiveBlendState(saved);
blendEnabledSaves[blendDepth] = GLStateManager.isEffectiveBlendEnabled();
return blendDepth++;
}
public static void popBlendState(int depth) {
blendDepth = depth;
final boolean enabled = blendEnabledSaves[depth];
if (enabled != GLStateManager.isEffectiveBlendEnabled()) {
if (enabled) {
GLStateManager.enableBlend();
} else {
GLStateManager.disableBlend();
}
}
final BlendState saved = blendSaves[depth];
GLStateManager.getEffectiveBlendState(blendScratch);
if (blendScratch.getSrcRgb() != saved.getSrcRgb() || blendScratch.getDstRgb() != saved.getDstRgb()
|| blendScratch.getSrcAlpha() != saved.getSrcAlpha() || blendScratch.getDstAlpha() != saved.getDstAlpha()) {
GLStateManager.tryBlendFuncSeparate(saved.getSrcRgb(), saved.getDstRgb(), saved.getSrcAlpha(), saved.getDstAlpha());
}
}
public static void popBlendStateTop() {
if (blendDepth > 0) {
popBlendState(blendDepth - 1);
}
}
private static BlendState[] blendSaves = new BlendState[4];
private static boolean[] blendEnabledSaves = new boolean[4];
private static int blendDepth;
private static final BlendState blendScratch = new BlendState();
private static final long ALPHA_ENABLED_BIT = 1L << 48;
private static final long LIGHTMAP_ENABLED_BIT = 1L << 49;
private static final AlphaState alphaScratch = new AlphaState();
private static long packCutoutState() {
GLStateManager.getEffectiveAlphaState(alphaScratch);
return (Float.floatToRawIntBits(alphaScratch.getReference()) & 0xFFFFFFFFL)
| ((long) (alphaScratch.getFunction() & 0xFFFF) << 32)
| (GLStateManager.isEffectiveAlphaTestEnabled() ? ALPHA_ENABLED_BIT : 0L)
| (GLStateManager.getTextures().getTextureUnitStates(1).isEnabled() ? LIGHTMAP_ENABLED_BIT : 0L);
}
public static void setBlockEntityDefaults() {
@@ -121,7 +263,23 @@ public class GbufferPrograms {
}
}
public static boolean beginNestedEntityPhase() {
if (getCurrentPhase() != WorldRenderingPhase.BLOCK_ENTITIES) {
return false;
}
setOverridePhase(WorldRenderingPhase.ENTITIES);
return true;
}
public static void endNestedEntityPhase(boolean pushed) {
if (pushed) {
setOverridePhase(null);
}
}
public static void setOverridePhase(WorldRenderingPhase phase) {
overridePhase = phase;
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
if (pipeline != null) {
@@ -129,20 +287,62 @@ public class GbufferPrograms {
}
}
@Getter
private static WorldRenderingPhase overridePhase;
public static Boolean beginTranslucencyDeclaration(Boolean translucent) {
final Boolean previous = declaredTranslucent;
declaredTranslucent = translucent;
applyTranslucencyDeclaration();
return previous;
}
public static void endTranslucencyDeclaration(Boolean previous) {
declaredTranslucent = previous;
applyTranslucencyDeclaration();
}
public static void setTranslucencyDeclaration(Boolean translucent) {
declaredTranslucent = translucent;
applyTranslucencyDeclaration();
}
private static Boolean declaredTranslucent;
private static void applyTranslucencyDeclaration() {
final WorldRenderingPipeline pipeline = Iris.getPipelineManager().getPipelineNullable();
if (pipeline != null) {
pipeline.setDeclaredTranslucency(declaredTranslucent);
}
}
private static SpecialCondition currentSpecial;
public static SpecialCondition getSpecialCondition() {
return currentSpecial;
}
public static Boolean getDeclaredTranslucency() {
return declaredTranslucent;
}
public static void setupSpecialRenderCondition(SpecialCondition override) {
currentSpecial = override;
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setSpecialCondition(override));
}
public static void teardownSpecialRenderCondition() {
currentSpecial = null;
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setSpecialCondition(null));
}
public static void runPhaseChangeNotifier() {
if (phaseChangeListener != null) {
phaseChangeListener.run();
}
}
public static void init() {
// Empty initializer to run static
}
@@ -0,0 +1,48 @@
package net.coderbot.iris.layer;
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
import net.coderbot.iris.pipeline.WorldRenderingPhase;
import java.util.Locale;
public record PassOverride(SpecialCondition special, Boolean translucent, WorldRenderingPhase phase) {
public static final PassOverride NONE = new PassOverride(null, null, null);
public PassOverride(SpecialCondition special, Boolean translucent) {
this(special, translucent, null);
}
public static PassOverride capture() {
final SpecialCondition special = GbufferPrograms.getSpecialCondition();
final Boolean translucent = GbufferPrograms.getDeclaredTranslucency();
final WorldRenderingPhase phase = GbufferPrograms.getOverridePhase();
return special == null && translucent == null && phase == null
? NONE : new PassOverride(special, translucent, phase);
}
public void apply() {
GbufferPrograms.setupSpecialRenderCondition(special);
GbufferPrograms.setTranslucencyDeclaration(translucent);
GbufferPrograms.setOverridePhase(phase);
}
public void clear() {
GbufferPrograms.setupSpecialRenderCondition(null);
GbufferPrograms.setTranslucencyDeclaration(null);
GbufferPrograms.setOverridePhase(null);
}
public boolean isEntityPhase() {
return phase == WorldRenderingPhase.ENTITIES;
}
public String nameSuffix() {
if (special == null && translucent == null && phase == null) {
return "";
}
return "_" + (special == null ? "plain" : special.name().toLowerCase(Locale.ROOT))
+ (translucent == null ? "" : (translucent ? "_translucent" : "_opaque"))
+ (phase == null ? "" : "_" + phase.name().toLowerCase(Locale.ROOT));
}
}
@@ -26,6 +26,7 @@ import net.coderbot.iris.features.FeatureFlags;
import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability;
import net.coderbot.iris.gbuffer_overrides.matching.ProgramTable;
import net.coderbot.iris.gbuffer_overrides.matching.RenderCondition;
import net.coderbot.iris.gbuffer_overrides.matching.TranslucentBlendMatcher;
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
import net.coderbot.iris.gbuffer_overrides.state.RenderTargetStateListener;
import net.coderbot.iris.gl.blending.AlphaTestOverride;
@@ -127,9 +128,6 @@ import java.util.function.Supplier;
* Encapsulates the compiled shader program objects for the currently loaded shaderpack.
*/
public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, RenderTargetStateListener {
private final static int SRC_ALPHA = 770;
private final static int ONE_MINUS_SRC_ALPHA = 771;
private final static int ONE = 1;
private final RenderTargets renderTargets;
@Nullable
@@ -260,14 +258,31 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
final Map<Pair<String, InputAvailability>, CompletableFuture<Map<PatchShaderType, String>>> instancedTransformFutures = submitAttributeTransforms(resolver, INSTANCED_PROGRAM_IDS, true);
final Optional<ProgramSource> terrainSource = first(programs.getGbuffersTerrain(), programs.getGbuffersTexturedLit(), programs.getGbuffersTextured(), programs.getGbuffersBasic());
final Optional<ProgramSource> terrainSolidOverride = programs.getGbuffersTerrainSolid();
final Optional<ProgramSource> terrainCutoutOverride = programs.getGbuffersTerrainCutout();
final Optional<ProgramSource> terrainSolidSource = first(terrainSolidOverride, terrainSource);
final Optional<ProgramSource> terrainCutoutSource = first(terrainCutoutOverride, terrainSource);
final Optional<ProgramSource> translucentSource = first(programs.getGbuffersWater(), terrainSource);
final Optional<ProgramSource> shadowSource = programs.getShadow();
final Optional<ProgramSource> shadowSolidOverride = programs.getShadowSolid();
final Optional<ProgramSource> shadowCutoutOverride = programs.getShadowCutout();
final Optional<ProgramSource> shadowSolidSource = first(shadowSolidOverride, shadowSource);
final Optional<ProgramSource> shadowCutoutSource = first(shadowCutoutOverride, shadowSource);
final Optional<ProgramSource> shadowTranslucentSource = first(programs.getShadowWater(), shadowSource);
// Celeritas terrain transform futures
final CompletableFuture<Map<PatchShaderType, String>> celeritasTerrainFuture = terrainSource.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(null);
final CompletableFuture<Map<PatchShaderType, String>> celeritasTerrainSolidFuture = terrainSolidOverride
.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(celeritasTerrainFuture);
final CompletableFuture<Map<PatchShaderType, String>> celeritasTerrainCutoutFuture = terrainCutoutOverride
.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(celeritasTerrainFuture);
final CompletableFuture<Map<PatchShaderType, String>> celeritasTranslucentFuture = translucentSource.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(null);
final CompletableFuture<Map<PatchShaderType, String>> celeritasShadowFuture = shadowSource.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(null);
final CompletableFuture<Map<PatchShaderType, String>> celeritasShadowSolidFuture = shadowSolidOverride
.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(celeritasShadowFuture);
final CompletableFuture<Map<PatchShaderType, String>> celeritasShadowCutoutFuture = shadowCutoutOverride
.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(celeritasShadowFuture);
final CompletableFuture<Map<PatchShaderType, String>> celeritasShadowTranslucentFuture = shadowTranslucentSource.map(DeferredWorldRenderingPipeline::submitCeleritasTerrainTransform).orElse(null);
final Int2ObjectArrayMap<CompletableFuture<String>> setupComputeFutures = submitSetupComputeTransforms(programs.getSetup(), programs.getPackDirectives().getTextureMap());
@@ -461,6 +476,8 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
ProgramId.BeaconBeam, ProgramId.BeaconBeam, ProgramId.BeaconBeam,
ProgramId.Entities, ProgramId.Entities, ProgramId.Entities,
ProgramId.EntitiesTrans, ProgramId.EntitiesTrans, ProgramId.EntitiesTrans,
ProgramId.Particles, ProgramId.Particles, ProgramId.Particles,
ProgramId.ParticlesTrans, ProgramId.ParticlesTrans, ProgramId.ParticlesTrans,
null, ProgramId.ArmorGlint, ProgramId.ArmorGlint,
null, ProgramId.SpiderEyes, ProgramId.SpiderEyes,
ProgramId.Hand, ProgramId.Hand, ProgramId.Hand,
@@ -697,11 +714,14 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
this.celeritasTerrainPipeline = new CeleritasTerrainPipeline(createTerrainSamplers,
shadowRenderer == null ? null : createShadowTerrainSamplers, createTerrainImages,
shadowRenderer == null ? null : createShadowTerrainImages, this.customUniforms,
terrainSource,
terrainSolidSource,
terrainCutoutSource,
translucentSource,
shadowSource,
shadowSolidSource,
shadowCutoutSource,
shadowTranslucentSource,
celeritasTerrainFuture, celeritasTranslucentFuture, celeritasShadowFuture, celeritasShadowTranslucentFuture,
celeritasTerrainSolidFuture, celeritasTerrainCutoutFuture, celeritasTranslucentFuture,
celeritasShadowSolidFuture, celeritasShadowCutoutFuture, celeritasShadowTranslucentFuture,
renderTargets, flippedAfterPrepare, flippedAfterTranslucent,
celeritasShadowFb);
@@ -843,28 +863,18 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
}
switch (phase) {
case NONE, OUTLINE, DEBUG, PARTICLES:
case NONE, OUTLINE, DEBUG:
return RenderCondition.DEFAULT;
case PARTICLES:
return isTranslucentDraw() ? RenderCondition.PARTICLES_TRANSLUCENT : RenderCondition.PARTICLES;
case SKY, SUNSET, CUSTOM_SKY, SUN, MOON, STARS, VOID:
return RenderCondition.SKY;
case TERRAIN_SOLID, TERRAIN_CUTOUT, TERRAIN_CUTOUT_MIPPED:
return RenderCondition.TERRAIN_OPAQUE;
case ENTITIES:
if (GLStateManager.getBlendState().getSrcRgb() == SRC_ALPHA &&
GLStateManager.getBlendState().getSrcAlpha() == ONE_MINUS_SRC_ALPHA &&
GLStateManager.getBlendState().getDstRgb() == ONE &&
GLStateManager.getBlendState().getDstAlpha() == ONE_MINUS_SRC_ALPHA)
{
return RenderCondition.ENTITIES_TRANSLUCENT;
} else {
return RenderCondition.ENTITIES;
}
return isTranslucentDraw() ? RenderCondition.ENTITIES_TRANSLUCENT : RenderCondition.ENTITIES;
case BLOCK_ENTITIES:
if (GLStateManager.getBlendState().getSrcRgb() == SRC_ALPHA &&
GLStateManager.getBlendState().getDstRgb() == ONE_MINUS_SRC_ALPHA) {
return RenderCondition.BLOCK_ENTITIES_TRANSLUCENT;
}
return RenderCondition.BLOCK_ENTITIES;
return isTranslucentDraw() ? RenderCondition.BLOCK_ENTITIES_TRANSLUCENT : RenderCondition.BLOCK_ENTITIES;
case DESTROY:
return RenderCondition.DESTROY;
case HAND_SOLID:
@@ -884,6 +894,10 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
}
}
private boolean isTranslucentDraw() {
return declaredTranslucent != null ? declaredTranslucent : TranslucentBlendMatcher.matchesCurrentState();
}
public boolean shouldOverrideShaders() {
return isRenderingLevelGeometry() && isMainBound;
}
@@ -906,10 +920,60 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
* Called when a mod overrides the GL program away from the active Iris pass
*/
public void onModProgramOverride() {
if (current != null) {
modProgramOverrode = true;
programBeforeModOverride = getActivePassProgramId();
}
current = null;
currentCondition = null;
}
public void restorePassAfterModProgram(int newProgram) {
if (refreshingPass || drivingProgram || !modProgramOverrode) {
return;
}
if (newProgram != 0 && newProgram != programBeforeModOverride) {
return;
}
refreshingPass = true;
try {
current = null;
matchPass();
} finally {
modProgramOverrode = false;
programBeforeModOverride = -1;
refreshingPass = false;
}
}
private boolean refreshingPass;
private boolean modProgramOverrode;
private int programBeforeModOverride = -1;
public void onVanillaBlendChanged() {
if (matchingBlend || drivingProgram || refreshingPass) {
return;
}
if (declaredTranslucent != null) {
return;
}
final WorldRenderingPhase phase = getPhase();
if (phase != WorldRenderingPhase.ENTITIES && phase != WorldRenderingPhase.BLOCK_ENTITIES) {
return;
}
matchingBlend = true;
try {
matchPass();
} finally {
matchingBlend = false;
}
}
private boolean matchingBlend;
private void matchPass() {
if (!shouldOverrideShaders()) {
return;
@@ -946,13 +1010,20 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
current = pass;
if (pass != null) {
pass.use();
} else {
Program.unbind();
drivingProgram = true;
try {
if (pass != null) {
pass.use();
} else {
Program.unbind();
}
} finally {
drivingProgram = false;
}
}
private boolean drivingProgram;
@Override
public void rebindCurrentPass() {
final Pass pass = this.current;
@@ -1631,6 +1702,9 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
@Nullable
public ComputeProgram getShadowVoxelizationCompute() { return shadowVoxelizationCompute; }
@Nullable
public ShadowRenderer getShadowRenderer() { return shadowRenderer; }
private GlUniformMatrix4f voxelizationModelView;
private GlUniformMatrix3f voxelizationNormalMatrix;
private final Matrix3f voxelizationNormalScratch = new Matrix3f();
@@ -1772,6 +1846,14 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
@Override
public void beginHand() {
isRenderingFullScreenPass = true;
beginPass(null);
currentCondition = null;
profiler.startSection("iris_center_depth");
centerDepthSampler.sampleCenterDepth();
profiler.endSection();
isRenderingFullScreenPass = false;
if (!usesSampler(IrisSamplers.USAGE_DEPTHTEX2)) {
return;
}
@@ -2005,10 +2087,6 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
isRenderingFullScreenPass = true;
profiler.startSection("iris_center_depth");
centerDepthSampler.sampleCenterDepth();
profiler.endSection();
profiler.startSection("iris_composites");
GLDebug.pushGroup("composite");
try {
@@ -2110,6 +2188,15 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
matchPass();
}
@Override
public void setDeclaredTranslucency(@Nullable Boolean translucent) {
this.declaredTranslucent = translucent;
matchPass();
}
@Nullable
private Boolean declaredTranslucent;
@Override
public RenderTargetStateListener getRenderTargetStateListener() {
return this;
@@ -19,6 +19,7 @@ import net.coderbot.iris.uniforms.FrameUpdateNotifier;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.client.shader.Framebuffer;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.OptionalInt;
@@ -83,6 +84,11 @@ public class FixedFunctionWorldRenderingPipeline implements WorldRenderingPipeli
}
@Override
public void setDeclaredTranslucency(@Nullable Boolean translucent) {
}
@Override
public RenderTargetStateListener getRenderTargetStateListener() {
return RenderTargetStateListener.NOP;
@@ -1,10 +1,12 @@
package net.coderbot.iris.pipeline;
import com.gtnewhorizons.angelica.compat.ModStatus;
import com.gtnewhorizons.angelica.compat.mojang.Camera;
import com.gtnewhorizons.angelica.compat.mojang.GameModeUtil;
import com.gtnewhorizons.angelica.compat.mojang.InteractionHand;
import com.gtnewhorizons.angelica.event.RenderHandEvent;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.mixins.interfaces.ItemRendererAccessor;
import com.gtnewhorizons.angelica.rendering.RenderingState;
import com.gtnewhorizons.angelica.rendering.celeritas.BlockRenderLayer;
import lombok.Getter;
@@ -83,96 +85,133 @@ public class HandRenderer {
!RenderHandEvent.post();
}
public boolean isHandTranslucent(InteractionHand hand) {
ItemStack heldItem = hand.getItemInHand(Minecraft.getMinecraft().thePlayer);
public boolean isItemTranslucent(ItemStack heldItem) {
if (heldItem == null) return false;
final Item item = heldItem.getItem();
if (item instanceof ItemBlock itemBlock) {
final Block block = itemBlock.field_150939_a;
if (block == null) return false;
final Map<Block, BlockRenderLayer> blockTypeIds = BlockRenderingSettings.INSTANCE.getBlockTypeIds();
return blockTypeIds != null && blockTypeIds.get(itemBlock.field_150939_a) == BlockRenderLayer.TRANSLUCENT;
final BlockRenderLayer override = blockTypeIds != null ? blockTypeIds.get(block) : null;
final BlockRenderLayer layer = override != null ? override : BlockRenderLayer.fromVanillaPass(block.getRenderBlockPass());
return layer == BlockRenderLayer.TRANSLUCENT;
}
return false;
}
public boolean isHandTranslucent(InteractionHand hand) {
return isItemTranslucent(hand.getItemInHand(Minecraft.getMinecraft().thePlayer));
}
private ItemStack mainHandItem() {
return ((ItemRendererAccessor) Minecraft.getMinecraft().entityRenderer.itemRenderer).angelica$getItemToRender();
}
public boolean isAnyHandTranslucent() {
return isHandTranslucent(InteractionHand.MAIN_HAND) || isHandTranslucent(InteractionHand.OFF_HAND);
return isItemTranslucent(mainHandItem())
|| (ModStatus.isBackhandLoaded && isHandTranslucent(InteractionHand.OFF_HAND));
}
public boolean isAnyHandSolid() {
return !(isItemTranslucent(mainHandItem())
&& (!ModStatus.isBackhandLoaded || isHandTranslucent(InteractionHand.OFF_HAND)));
}
public void renderSolid(float tickDelta, Camera camera, RenderGlobal gameRenderer, WorldRenderingPipeline pipeline) {
if (!canRender(camera, gameRenderer) || !IrisApi.getInstance().isShaderPackInUse()) {
if (!IrisApi.getInstance().isShaderPackInUse() || !canRender(camera, gameRenderer) || !isAnyHandSolid()) {
return;
}
Minecraft mc = Minecraft.getMinecraft();
ACTIVE = true;
final int blendSave = GbufferPrograms.pushBlendState();
final long cutoutSave = GbufferPrograms.pushCutoutDefaults();
final boolean depthMaskBefore = GLStateManager.isEffectiveDepthMaskEnabled();
GLStateManager.disableBlend();
GLStateManager.defaultBlendFunc();
pipeline.setPhase(WorldRenderingPhase.HAND_SOLID);
GLStateManager.glPushMatrix();
GLStateManager.glDepthMask(true); // actually write to the depth buffer, it's normally disabled at this point
mc.mcProfiler.startSection("iris_hand");
try {
setupGlState(gameRenderer, camera, tickDelta);
setupGlState(gameRenderer, camera, tickDelta);
GbufferPrograms.setBlockEntityDefaults();
GbufferPrograms.setBlockEntityDefaults();
renderingSolid = true;
renderingSolid = true;
mc.entityRenderer.enableLightmap(tickDelta);
mc.entityRenderer.itemRenderer.renderItemInFirstPerson(tickDelta);
mc.entityRenderer.disableLightmap(tickDelta);
} finally {
renderingSolid = false;
mc.entityRenderer.enableLightmap(tickDelta);
mc.entityRenderer.itemRenderer.renderItemInFirstPerson(tickDelta);
mc.entityRenderer.disableLightmap(tickDelta);
mc.mcProfiler.endSection();
GLStateManager.defaultBlendFunc();
GLStateManager.glDepthMask(false);
GLStateManager.glPopMatrix();
GLStateManager.glPopMatrix();
resetProjectionMatrix();
mc.mcProfiler.endSection();
GLStateManager.glDepthMask(depthMaskBefore);
GbufferPrograms.popCutoutDefaults(cutoutSave);
GbufferPrograms.popBlendState(blendSave);
resetProjectionMatrix();
pipeline.setPhase(WorldRenderingPhase.NONE);
renderingSolid = false;
pipeline.setPhase(WorldRenderingPhase.NONE);
ACTIVE = false;
ACTIVE = false;
}
}
// TODO: RenderType
public void renderTranslucent(float tickDelta, Camera camera, RenderGlobal gameRenderer, WorldRenderingPipeline pipeline) {
if (!canRender(camera, gameRenderer) || !isAnyHandTranslucent() || !IrisApi.getInstance().isShaderPackInUse()) {
if (!IrisApi.getInstance().isShaderPackInUse() || !canRender(camera, gameRenderer) || !isAnyHandTranslucent()) {
return;
}
Minecraft mc = Minecraft.getMinecraft();
ACTIVE = true;
final int blendSave = GbufferPrograms.pushBlendState();
final long cutoutSave = GbufferPrograms.pushCutoutDefaults();
final boolean depthMaskBefore = GLStateManager.isEffectiveDepthMaskEnabled();
GLStateManager.enableBlend();
GLStateManager.defaultBlendFunc();
pipeline.setPhase(WorldRenderingPhase.HAND_TRANSLUCENT);
GLStateManager.glPushMatrix();
GLStateManager.glDepthMask(false);
mc.mcProfiler.startSection("iris_hand_translucent");
try {
setupGlState(gameRenderer, camera, tickDelta);
setupGlState(gameRenderer, camera, tickDelta);
GbufferPrograms.setBlockEntityDefaults();
GbufferPrograms.setBlockEntityDefaults();
mc.entityRenderer.enableLightmap(tickDelta);
mc.entityRenderer.itemRenderer.renderItemInFirstPerson(tickDelta);
mc.entityRenderer.disableLightmap(tickDelta);
} finally {
mc.mcProfiler.endSection();
mc.entityRenderer.enableLightmap(tickDelta);
mc.entityRenderer.itemRenderer.renderItemInFirstPerson(tickDelta);
mc.entityRenderer.disableLightmap(tickDelta);
GLStateManager.glPopMatrix();
resetProjectionMatrix();
GLStateManager.glPopMatrix();
GLStateManager.glDepthMask(depthMaskBefore);
GbufferPrograms.popCutoutDefaults(cutoutSave);
GbufferPrograms.popBlendState(blendSave);
resetProjectionMatrix();
pipeline.setPhase(WorldRenderingPhase.NONE);
Minecraft.getMinecraft().mcProfiler.endSection();
pipeline.setPhase(WorldRenderingPhase.NONE);
ACTIVE = false;
ACTIVE = false;
}
}
private void resetProjectionMatrix() {
@@ -1,6 +1,7 @@
package net.coderbot.iris.pipeline;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.profiling.Tracy;
import lombok.Getter;
import net.coderbot.iris.Iris;
import net.coderbot.iris.uniforms.SystemTimeUniforms;
@@ -19,6 +20,7 @@ public class PipelineManager {
private final Map<String, WorldRenderingPipeline> pipelinesPerDimension = new HashMap<>();
private WorldRenderingPipeline pipeline = new FixedFunctionWorldRenderingPipeline();
private String lastPreparedDimension = null;
private long shaderSection;
@Getter
private int versionCounterForSodiumShaderReload = 0;
@@ -41,6 +43,9 @@ public class PipelineManager {
Iris.logger.info("Creating pipeline for dimension '{}'", currentDimension);
pipeline = pipelineFactory.apply(currentDimension);
pipelinesPerDimension.put(currentDimension, pipeline);
if (Tracy.ENABLED) {
shaderSection = Tracy.sectionEnter(Tracy.SECTION_SHADERS, "shaders " + Iris.getCurrentPackName() + " / " + currentDimension);
}
} else {
pipeline = pipelinesPerDimension.get(currentDimension);
}
@@ -70,6 +75,8 @@ public class PipelineManager {
* @see <a href="https://github.com/IrisShaders/Iris/issues/1330">this GitHub issue</a>
*/
public void destroyPipeline() {
Tracy.sectionLeave(shaderSection);
shaderSection = 0L;
pipelinesPerDimension.forEach((dimensionName, pipeline) -> {
Iris.logger.info("Destroying pipeline for dimension '{}'", dimensionName);
resetTextureState();
@@ -17,6 +17,7 @@ import com.gtnewhorizons.angelica.rendering.RenderingState;
import com.gtnewhorizons.angelica.rendering.celeritas.AngelicaRenderSectionManager;
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import lombok.Setter;
import net.coderbot.iris.Iris;
import net.coderbot.iris.compat.dh.DHCompat;
import net.coderbot.iris.gui.option.IrisVideoSettings;
@@ -106,7 +107,8 @@ public class ShadowRenderer {
private final ShadowCullState packCullingState;
private final ShadowCompositeRenderer compositeRenderer;
private boolean packHasVoxelization;
private boolean playerReflectionCaptureEnabled = true;
@Setter
private boolean playerReflectionCaptureEnabled = true;
private final boolean shouldRenderTerrain;
private final boolean shouldRenderTranslucent;
private final boolean shouldRenderEntities;
@@ -148,10 +150,19 @@ public class ShadowRenderer {
private boolean preSubmitActive;
private float preSubmittedShadowAngle = Float.NaN;
private float shadowAngleDelta() {
final Vector4f light = celestialUniforms.getShadowLightPositionInWorldSpace();
final float x = light.x(), y = light.y(), z = light.z();
final float length = (float) Math.sqrt(x * x + y * y + z * z);
return ShadowGraphGate.deltaForElevation(length > 0.0f ? y / length : 1.0f,
ShadowGraphGate.degreesToRotation(AngelicaConfig.shadowGraphAngleDelta),
AngelicaConfig.shadowGraphHorizonScale);
}
public void preSubmitGraphUpdate(int frame, boolean spectator) {
final AngelicaRenderSectionManager rsm = CeleritasWorldRenderer.getInstance().getRenderSectionManager();
final float currentShadowAngle = getShadowAngle();
if (ShadowGraphGate.shouldMarkDirty(lastGraphShadowAngle, currentShadowAngle)) {
if (ShadowGraphGate.shouldMarkDirty(lastGraphShadowAngle, currentShadowAngle, shadowAngleDelta())) {
rsm.markShadowGraphDirty();
lastGraphShadowAngle = currentShadowAngle;
}
@@ -235,13 +246,21 @@ public class ShadowRenderer {
public void setUsesImages(boolean usesImages) {
this.packHasVoxelization = packHasVoxelization || usesImages;
this.shadowWritesImages = usesImages;
}
public void setPlayerReflectionCaptureEnabled(boolean enabled) {
this.playerReflectionCaptureEnabled = enabled;
private boolean shadowWritesImages;
public boolean shadowTerrainAlwaysRelays() {
return !shouldRenderTerrain || voxelizationActive() || shadowWritesImages;
}
public static MatrixStack createShadowModelView(float sunPathRotation, float intervalSize) {
public static boolean shadowGraphGateIsNotBypassed() {
return !(Iris.getPipelineManager().getPipelineNullable() instanceof DeferredWorldRenderingPipeline deferred)
|| deferred.getShadowRenderer() == null || !deferred.getShadowRenderer().shadowTerrainAlwaysRelays();
}
public static MatrixStack createShadowModelView(float sunPathRotation, float intervalSize) {
// Use entity position for shadow matrix
final Vector3d entityPos = Camera.INSTANCE.getEntityPos();
@@ -833,7 +852,7 @@ public class ShadowRenderer {
globalTileEntities.clear();
final float currentShadowAngle = getShadowAngle();
if (ShadowGraphGate.shouldMarkDirty(lastGraphShadowAngle, currentShadowAngle)) {
if (ShadowGraphGate.shouldMarkDirty(lastGraphShadowAngle, currentShadowAngle, shadowAngleDelta())) {
CeleritasWorldRenderer.getInstance().getRenderSectionManager().markShadowGraphDirty();
lastGraphShadowAngle = currentShadowAngle;
}
@@ -843,8 +862,7 @@ public class ShadowRenderer {
final boolean consumePreSubmit = preSubmitActive;
preSubmitActive = false;
final boolean relayTerrain = !deferActive
|| voxelizationActive()
final boolean relayTerrain = shadowTerrainAlwaysRelays()
|| !targets.isTerrainSnapshotValid()
|| consumePreSubmit
|| CeleritasWorldRenderer.getInstance().getRenderSectionManager().isShadowGraphDirty()
@@ -14,6 +14,7 @@ import net.coderbot.iris.shaderpack.CloudSetting;
import net.coderbot.iris.shaderpack.texture.TextureStage;
import net.coderbot.iris.uniforms.FrameUpdateNotifier;
import net.minecraft.client.renderer.EntityRenderer;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.OptionalInt;
@@ -31,6 +32,7 @@ public interface WorldRenderingPipeline {
void setPhase(WorldRenderingPhase phase);
void setInputs(InputAvailability availability);
void setSpecialCondition(SpecialCondition special);
void setDeclaredTranslucency(@Nullable Boolean translucent);
RenderTargetStateListener getRenderTargetStateListener();
int getCurrentNormalTexture();
@@ -17,6 +17,8 @@ public class ProgramSet {
private final PackDirectives packDirectives;
private final ProgramSource shadow;
private final ProgramSource shadowSolid;
private final ProgramSource shadowCutout;
private final ProgramSource shadowWater;
private final ComputeSource[] shadowCompute;
@@ -34,6 +36,8 @@ public class ProgramSet {
private final ProgramSource gbuffersTextured;
private final ProgramSource gbuffersTexturedLit;
private final ProgramSource gbuffersTerrain;
private final ProgramSource gbuffersTerrainSolid;
private final ProgramSource gbuffersTerrainCutout;
private ProgramSource gbuffersDamagedBlock;
private final ProgramSource gbuffersSkyBasic;
private final ProgramSource gbuffersSkyTextured;
@@ -43,6 +47,8 @@ public class ProgramSet {
private final ProgramSource gbuffersEntitiesTrans;
private final ProgramSource gbuffersEntitiesGlowing;
private final ProgramSource gbuffersLightning;
private final ProgramSource gbuffersParticles;
private final ProgramSource gbuffersParticlesTrans;
private final ProgramSource gbuffersGlint;
private final ProgramSource gbuffersEntityEyes;
private final ProgramSource gbuffersBlock;
@@ -83,6 +89,10 @@ public class ProgramSet {
// - https://github.com/IrisShaders/Iris/issues/987
this.shadow = readProgramSource(directory, sourceProvider, "shadow", this, shaderProperties,
BlendModeOverride.OFF);
this.shadowSolid = readProgramSource(directory, sourceProvider, "shadow_solid", this, shaderProperties,
BlendModeOverride.OFF);
this.shadowCutout = readProgramSource(directory, sourceProvider, "shadow_cutout", this, shaderProperties,
BlendModeOverride.OFF);
this.shadowWater = readProgramSource(directory, sourceProvider, "shadow_water", this, shaderProperties,
BlendModeOverride.OFF);
this.shadowCompute = readComputeArray(directory, sourceProvider, "shadow");
@@ -114,6 +124,8 @@ public class ProgramSet {
this.gbuffersTextured = readProgramSource(directory, sourceProvider, "gbuffers_textured", this, shaderProperties);
this.gbuffersTexturedLit = readProgramSource(directory, sourceProvider, "gbuffers_textured_lit", this, shaderProperties);
this.gbuffersTerrain = readProgramSource(directory, sourceProvider, "gbuffers_terrain", this, shaderProperties);
this.gbuffersTerrainSolid = readProgramSource(directory, sourceProvider, "gbuffers_terrain_solid", this, shaderProperties);
this.gbuffersTerrainCutout = readProgramSource(directory, sourceProvider, "gbuffers_terrain_cutout", this, shaderProperties);
this.gbuffersDamagedBlock = readProgramSource(directory, sourceProvider, "gbuffers_damagedblock", this, shaderProperties);
this.gbuffersSkyBasic = readProgramSource(directory, sourceProvider, "gbuffers_skybasic", this, shaderProperties);
this.gbuffersSkyTextured = readProgramSource(directory, sourceProvider, "gbuffers_skytextured", this, shaderProperties);
@@ -123,6 +135,8 @@ public class ProgramSet {
this.gbuffersEntitiesTrans = readProgramSource(directory, sourceProvider, "gbuffers_entities_translucent", this, shaderProperties);
this.gbuffersEntitiesGlowing = readProgramSource(directory, sourceProvider, "gbuffers_entities_glowing", this, shaderProperties);
this.gbuffersLightning = readProgramSource(directory, sourceProvider, "gbuffers_lightning", this, shaderProperties);
this.gbuffersParticles = readProgramSource(directory, sourceProvider, "gbuffers_particles", this, shaderProperties);
this.gbuffersParticlesTrans = readProgramSource(directory, sourceProvider, "gbuffers_particles_translucent", this, shaderProperties);
this.gbuffersGlint = readProgramSource(directory, sourceProvider, "gbuffers_armor_glint", this, shaderProperties);
this.gbuffersEntityEyes = readProgramSource(directory, sourceProvider, "gbuffers_spidereyes", this, shaderProperties);
this.gbuffersBlock = readProgramSource(directory, sourceProvider, "gbuffers_block", this, shaderProperties);
@@ -224,6 +238,8 @@ public class ProgramSet {
List<ComputeSource> computes = new ArrayList<>();
programs.add(shadow);
programs.add(shadowSolid);
programs.add(shadowCutout);
programs.add(shadowWater);
programs.addAll(Arrays.asList(shadowcomp));
programs.addAll(Arrays.asList(begin));
@@ -231,8 +247,9 @@ public class ProgramSet {
programs.addAll (Arrays.asList(
gbuffersBasic, gbuffersBeaconBeam, gbuffersTextured, gbuffersTexturedLit, gbuffersTerrain,
gbuffersTerrainSolid, gbuffersTerrainCutout,
gbuffersDamagedBlock, gbuffersSkyBasic, gbuffersSkyTextured, gbuffersClouds, gbuffersWeather,
gbuffersEntities, gbuffersEntitiesTrans, gbuffersEntitiesGlowing, gbuffersLightning, gbuffersGlint, gbuffersEntityEyes, gbuffersBlock, gbuffersBlockTrans,
gbuffersEntities, gbuffersEntitiesTrans, gbuffersEntitiesGlowing, gbuffersLightning, gbuffersParticles, gbuffersParticlesTrans, gbuffersGlint, gbuffersEntityEyes, gbuffersBlock, gbuffersBlockTrans,
gbuffersHand, dhShadow, dhTerrain, dhGeneric, dhWater
));
@@ -309,6 +326,14 @@ public class ProgramSet {
return shadow.requireValid();
}
public Optional<ProgramSource> getShadowSolid() {
return shadowSolid.requireValid();
}
public Optional<ProgramSource> getShadowCutout() {
return shadowCutout.requireValid();
}
public Optional<ProgramSource> getShadowWater() {
return shadowWater.requireValid();
}
@@ -353,6 +378,14 @@ public class ProgramSet {
return gbuffersTerrain.requireValid();
}
public Optional<ProgramSource> getGbuffersTerrainSolid() {
return gbuffersTerrainSolid.requireValid();
}
public Optional<ProgramSource> getGbuffersTerrainCutout() {
return gbuffersTerrainCutout.requireValid();
}
public Optional<ProgramSource> getGbuffersDamagedBlock() {
return gbuffersDamagedBlock.requireValid();
}
@@ -389,6 +422,14 @@ public class ProgramSet {
return gbuffersLightning.requireValid();
}
public Optional<ProgramSource> getGbuffersParticles() {
return gbuffersParticles.requireValid();
}
public Optional<ProgramSource> getGbuffersParticlesTrans() {
return gbuffersParticlesTrans.requireValid();
}
public Optional<ProgramSource> getGbuffersGlint() {
return gbuffersGlint.requireValid();
}
@@ -428,6 +469,8 @@ public class ProgramSet {
public Optional<ProgramSource> get(ProgramId programId) {
return switch (programId) {
case Shadow -> getShadow();
case ShadowSolid -> getShadowSolid();
case ShadowCutout -> getShadowCutout();
case ShadowWater -> first(getShadowWater(), getShadow());
case Basic -> getGbuffersBasic();
case Line -> gbuffersLine.requireValid();
@@ -437,6 +480,8 @@ public class ProgramSet {
case SkyTextured -> getGbuffersSkyTextured();
case Clouds -> getGbuffersClouds();
case Terrain -> getGbuffersTerrain();
case TerrainSolid -> getGbuffersTerrainSolid();
case TerrainCutout -> getGbuffersTerrainCutout();
case DamagedBlock -> getGbuffersDamagedBlock();
case Block -> getGbuffersBlock();
case BlockTrans -> getGbuffersBlockTrans();
@@ -445,6 +490,8 @@ public class ProgramSet {
case EntitiesTrans -> getGbuffersEntitiesTrans();
case EntitiesGlowing -> getGbuffersEntitiesGlowing();
case Lightning -> getGbuffersLightning();
case Particles -> getGbuffersParticles();
case ParticlesTrans -> getGbuffersParticlesTrans();
case ArmorGlint -> getGbuffersGlint();
case SpiderEyes -> getGbuffersEntityEyes();
case Hand -> getGbuffersHand();
@@ -27,7 +27,9 @@ public enum ProgramId {
Terrain(ProgramGroup.Gbuffers, "terrain", TexturedLit),
TerrainSolid(ProgramGroup.Gbuffers, "terrain_solid", Terrain),
TerrainCutoutMip(ProgramGroup.Gbuffers, "terrain_cutout_mip", Terrain),
// terrain_cutout_mip program was removed in modern versions of Minecraft. As the goal is to be a faithful backport of Iris
// we likely should just group cutout and cutout mipped into the same program.
// CUTOUT_MIPPED essentially became CUTOUT and now everything uses mipmaps in modern. We don't as of writing.
TerrainCutout(ProgramGroup.Gbuffers, "terrain_cutout", Terrain),
DamagedBlock(ProgramGroup.Gbuffers, "damagedblock", Terrain),
@@ -40,6 +42,8 @@ public enum ProgramId {
EntitiesTrans(ProgramGroup.Gbuffers, "entities_translucent", Entities),
EntitiesGlowing(ProgramGroup.Gbuffers, "entities_glowing", Entities),
Lightning(ProgramGroup.Gbuffers, "lightning", Entities),
Particles(ProgramGroup.Gbuffers, "particles", TexturedLit),
ParticlesTrans(ProgramGroup.Gbuffers, "particles_translucent", Particles),
ArmorGlint(ProgramGroup.Gbuffers, "armor_glint", Textured),
SpiderEyes(ProgramGroup.Gbuffers, "spidereyes", Textured,
new BlendModeOverride(new BlendState(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE)),
@@ -82,6 +82,10 @@ public class OptionMenuContainer {
// Screens will call this when they contain a "*" element, so that the list of
// unused options can be added after all other screens have been resolved
public boolean hasMenuElements() {
return !this.originalMainElements.isEmpty();
}
public void queueForUnusedOptionDump(int index, List<OptionMenuElement> elementList) {
this.unusedOptionDumpQueue.put(elementList, index);
}
Loaded 100 of 146 files, more files were not shown because too many files have changed in this diff. Show more