Merge remote-tracking branch 'upstream/1.20/dev' into feat/multi-loader-1.21

This commit is contained in:
IThundxr 2024-11-16 13:44:29 -05:00
commit eb1c56e9ac
Failed to generate hash of commit
13 changed files with 452 additions and 131 deletions

View file

@ -0,0 +1,5 @@
package dev.engine_room.flywheel.backend;
public class BackendDebugFlags {
public static boolean LIGHT_STORAGE_VIEW = false;
}

View file

@ -0,0 +1,9 @@
package dev.engine_room.flywheel.backend;
import org.jetbrains.annotations.Nullable;
import net.minecraft.world.level.chunk.DataLayer;
public interface SkyLightSectionStorageExtension {
@Nullable DataLayer flywheel$skyDataLayer(long section);
}

View file

@ -23,6 +23,11 @@ public final class LightLut {
.set(z, index + 1); .set(z, index + 1);
} }
public void prune() {
// Maybe this could be done better incrementally?
indices.prune((middle) -> middle.prune(IntLayer::prune));
}
public void remove(long section) { public void remove(long section) {
final var x = SectionPos.x(section); final var x = SectionPos.x(section);
final var y = SectionPos.y(section); final var y = SectionPos.y(section);
@ -49,6 +54,11 @@ public final class LightLut {
return out; return out;
} }
@FunctionalInterface
public interface Prune<T> {
boolean prune(T t);
}
public static final class Layer<T> { public static final class Layer<T> {
private boolean hasBase = false; private boolean hasBase = false;
private int base = 0; private int base = 0;
@ -135,6 +145,69 @@ public final class LightLut {
return (T) out; return (T) out;
} }
/**
* @return {@code true} if the layer is now empty.
*/
public boolean prune(Prune<T> inner) {
if (!hasBase) {
return true;
}
// Prune the next layer before checking for leading/trailing zeros.
for (var i = 0; i < nextLayer.length; i++) {
var o = nextLayer[i];
if (o != null && inner.prune((T) o)) {
nextLayer[i] = null;
}
}
var leadingZeros = getLeadingZeros();
if (leadingZeros == nextLayer.length) {
return true;
}
var trailingZeros = getTrailingZeros();
if (leadingZeros == 0 && trailingZeros == 0) {
return false;
}
final var newIndices = new Object[nextLayer.length - leadingZeros - trailingZeros];
System.arraycopy(nextLayer, leadingZeros, newIndices, 0, newIndices.length);
nextLayer = newIndices;
base += leadingZeros;
return false;
}
private int getLeadingZeros() {
int out = 0;
for (Object index : nextLayer) {
if (index == null) {
out++;
} else {
break;
}
}
return out;
}
private int getTrailingZeros() {
int out = 0;
for (int i = nextLayer.length - 1; i >= 0; i--) {
if (nextLayer[i] == null) {
out++;
} else {
break;
}
}
return out;
}
private void resize(int length) { private void resize(int length) {
final var newIndices = new Object[length]; final var newIndices = new Object[length];
System.arraycopy(nextLayer, 0, newIndices, 0, nextLayer.length); System.arraycopy(nextLayer, 0, newIndices, 0, nextLayer.length);
@ -214,6 +287,61 @@ public final class LightLut {
indices[offset] = index; indices[offset] = index;
} }
/**
* @return {@code true} if the layer is now empty.
*/
public boolean prune() {
if (!hasBase) {
return true;
}
var leadingZeros = getLeadingZeros();
if (leadingZeros == indices.length) {
return true;
}
var trailingZeros = getTrailingZeros();
if (leadingZeros == 0 && trailingZeros == 0) {
return false;
}
final var newIndices = new int[indices.length - leadingZeros - trailingZeros];
System.arraycopy(indices, leadingZeros, newIndices, 0, newIndices.length);
indices = newIndices;
base += leadingZeros;
return false;
}
private int getTrailingZeros() {
int out = 0;
for (int i = indices.length - 1; i >= 0; i--) {
if (indices[i] == 0) {
out++;
} else {
break;
}
}
return out;
}
private int getLeadingZeros() {
int out = 0;
for (int index : indices) {
if (index == 0) {
out++;
} else {
break;
}
}
return out;
}
public void clear(int i) { public void clear(int i) {
if (!hasBase) { if (!hasBase) {
return; return;

View file

@ -10,8 +10,11 @@ import dev.engine_room.flywheel.api.visual.Effect;
import dev.engine_room.flywheel.api.visual.EffectVisual; import dev.engine_room.flywheel.api.visual.EffectVisual;
import dev.engine_room.flywheel.api.visualization.VisualizationContext; import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.api.visualization.VisualizationManager; import dev.engine_room.flywheel.api.visualization.VisualizationManager;
import dev.engine_room.flywheel.backend.BackendDebugFlags;
import dev.engine_room.flywheel.backend.SkyLightSectionStorageExtension;
import dev.engine_room.flywheel.backend.engine.indirect.StagingBuffer; import dev.engine_room.flywheel.backend.engine.indirect.StagingBuffer;
import dev.engine_room.flywheel.backend.gl.buffer.GlBuffer; import dev.engine_room.flywheel.backend.gl.buffer.GlBuffer;
import dev.engine_room.flywheel.backend.mixin.light.LightEngineAccessor;
import dev.engine_room.flywheel.lib.instance.InstanceTypes; import dev.engine_room.flywheel.lib.instance.InstanceTypes;
import dev.engine_room.flywheel.lib.instance.TransformedInstance; import dev.engine_room.flywheel.lib.instance.TransformedInstance;
import dev.engine_room.flywheel.lib.math.MoreMath; import dev.engine_room.flywheel.lib.math.MoreMath;
@ -27,9 +30,10 @@ import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.LightTexture;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
import net.minecraft.core.Vec3i;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.lighting.LayerLightEventListener; import net.minecraft.world.level.chunk.DataLayer;
/** /**
* A managed arena of light sections for uploading to the GPU. * A managed arena of light sections for uploading to the GPU.
@ -46,8 +50,6 @@ import net.minecraft.world.level.lighting.LayerLightEventListener;
* <p>Thus, each section occupies 5832 bytes. * <p>Thus, each section occupies 5832 bytes.
*/ */
public class LightStorage implements Effect { public class LightStorage implements Effect {
public static boolean DEBUG = false;
public static final int BLOCKS_PER_SECTION = 18 * 18 * 18; public static final int BLOCKS_PER_SECTION = 18 * 18 * 18;
public static final int LIGHT_SIZE_BYTES = BLOCKS_PER_SECTION; public static final int LIGHT_SIZE_BYTES = BLOCKS_PER_SECTION;
public static final int SOLID_SIZE_BYTES = MoreMath.ceilingDiv(BLOCKS_PER_SECTION, Integer.SIZE) * Integer.BYTES; public static final int SOLID_SIZE_BYTES = MoreMath.ceilingDiv(BLOCKS_PER_SECTION, Integer.SIZE) * Integer.BYTES;
@ -55,6 +57,9 @@ public class LightStorage implements Effect {
private static final int DEFAULT_ARENA_CAPACITY_SECTIONS = 64; private static final int DEFAULT_ARENA_CAPACITY_SECTIONS = 64;
private static final int INVALID_SECTION = -1; private static final int INVALID_SECTION = -1;
private static final ConstantDataLayer EMPTY_BLOCK_DATA = new ConstantDataLayer(0);
private static final ConstantDataLayer EMPTY_SKY_DATA = new ConstantDataLayer(15);
private final LevelAccessor level; private final LevelAccessor level;
private final LightLut lut; private final LightLut lut;
private final CpuArena arena; private final CpuArena arena;
@ -102,12 +107,12 @@ public class LightStorage implements Effect {
public <C> Plan<C> createFramePlan() { public <C> Plan<C> createFramePlan() {
return SimplePlan.of(() -> { return SimplePlan.of(() -> {
if (DEBUG != isDebugOn) { if (BackendDebugFlags.LIGHT_STORAGE_VIEW != isDebugOn) {
var visualizationManager = VisualizationManager.get(level); var visualizationManager = VisualizationManager.get(level);
// Really should be non-null, but just in case. // Really should be non-null, but just in case.
if (visualizationManager != null) { if (visualizationManager != null) {
if (DEBUG) { if (BackendDebugFlags.LIGHT_STORAGE_VIEW) {
visualizationManager.effects() visualizationManager.effects()
.queueAdd(this); .queueAdd(this);
} else { } else {
@ -115,7 +120,7 @@ public class LightStorage implements Effect {
.queueRemove(this); .queueRemove(this);
} }
} }
isDebugOn = DEBUG; isDebugOn = BackendDebugFlags.LIGHT_STORAGE_VIEW;
} }
if (updatedSections.isEmpty() && requestedSections == null) { if (updatedSections.isEmpty() && requestedSections == null) {
@ -165,6 +170,8 @@ public class LightStorage implements Effect {
return; return;
} }
boolean anyRemoved = false;
var entries = section2ArenaIndex.long2IntEntrySet(); var entries = section2ArenaIndex.long2IntEntrySet();
var it = entries.iterator(); var it = entries.iterator();
while (it.hasNext()) { while (it.hasNext()) {
@ -175,8 +182,14 @@ public class LightStorage implements Effect {
arena.free(entry.getIntValue()); arena.free(entry.getIntValue());
endTrackingSection(section); endTrackingSection(section);
it.remove(); it.remove();
anyRemoved = true;
} }
} }
if (anyRemoved) {
lut.prune();
needsLutRebuild = true;
}
} }
private void beginTrackingSection(long section, int index) { private void beginTrackingSection(long section, int index) {
@ -194,11 +207,6 @@ public class LightStorage implements Effect {
} }
public void collectSection(long section) { public void collectSection(long section) {
var lightEngine = level.getLightEngine();
var blockLight = lightEngine.getLayerListener(LightLayer.BLOCK);
var skyLight = lightEngine.getLayerListener(LightLayer.SKY);
int index = indexForSection(section); int index = indexForSection(section);
changed.set(index); changed.set(index);
@ -210,21 +218,21 @@ public class LightStorage implements Effect {
collectSolidData(ptr, section); collectSolidData(ptr, section);
collectCenter(blockLight, skyLight, ptr, section); collectCenter(ptr, section);
for (SectionEdge i : SectionEdge.values()) { for (SectionEdge i : SectionEdge.values()) {
collectYZPlane(blockLight, skyLight, ptr, SectionPos.offset(section, i.sectionOffset, 0, 0), i); collectYZPlane(ptr, SectionPos.offset(section, i.sectionOffset, 0, 0), i);
collectXZPlane(blockLight, skyLight, ptr, SectionPos.offset(section, 0, i.sectionOffset, 0), i); collectXZPlane(ptr, SectionPos.offset(section, 0, i.sectionOffset, 0), i);
collectXYPlane(blockLight, skyLight, ptr, SectionPos.offset(section, 0, 0, i.sectionOffset), i); collectXYPlane(ptr, SectionPos.offset(section, 0, 0, i.sectionOffset), i);
for (SectionEdge j : SectionEdge.values()) { for (SectionEdge j : SectionEdge.values()) {
collectXStrip(blockLight, skyLight, ptr, SectionPos.offset(section, 0, i.sectionOffset, j.sectionOffset), i, j); collectXStrip(ptr, SectionPos.offset(section, 0, i.sectionOffset, j.sectionOffset), i, j);
collectYStrip(blockLight, skyLight, ptr, SectionPos.offset(section, i.sectionOffset, 0, j.sectionOffset), i, j); collectYStrip(ptr, SectionPos.offset(section, i.sectionOffset, 0, j.sectionOffset), i, j);
collectZStrip(blockLight, skyLight, ptr, SectionPos.offset(section, i.sectionOffset, j.sectionOffset, 0), i, j); collectZStrip(ptr, SectionPos.offset(section, i.sectionOffset, j.sectionOffset, 0), i, j);
} }
} }
collectCorners(blockLight, skyLight, ptr, section); collectCorners(ptr, section);
} }
private void collectSolidData(long ptr, long section) { private void collectSolidData(long ptr, long section) {
@ -259,64 +267,59 @@ public class LightStorage implements Effect {
} }
} }
private void writeSolid(long ptr, int index, boolean blockValid) { private DataLayer getSkyData(long section) {
if (!blockValid) { var sky = level.getLightEngine()
return; .getLayerListener(LightLayer.SKY);
} var skyStorage = (SkyLightSectionStorageExtension) ((LightEngineAccessor<?, ?>) sky).flywheel$storage();
int intIndex = index / Integer.SIZE;
int bitIndex = index % Integer.SIZE;
long offset = intIndex * Integer.BYTES; var out = skyStorage.flywheel$skyDataLayer(section);
int bitField = MemoryUtil.memGetInt(ptr + offset); if (out == null) {
bitField |= 1 << bitIndex; return EMPTY_SKY_DATA;
MemoryUtil.memPutInt(ptr + offset, bitField);
} }
private void collectXStrip(LayerLightEventListener blockLight, LayerLightEventListener skyLight, long ptr, long section, SectionEdge y, SectionEdge z) { return out;
var pos = SectionPos.of(section);
var blockData = blockLight.getDataLayerData(pos);
var skyData = skyLight.getDataLayerData(pos);
if (blockData == null || skyData == null) {
return;
} }
private DataLayer getBlockData(long section) {
var out = ((LightEngineAccessor<?, ?>) level.getLightEngine()
.getLayerListener(LightLayer.BLOCK)).flywheel$storage()
.getDataLayerData(section);
if (out == null) {
return EMPTY_BLOCK_DATA;
}
return out;
}
private void collectXStrip(long ptr, long section, SectionEdge y, SectionEdge z) {
var blockData = getBlockData(section);
var skyData = getSkyData(section);
for (int x = 0; x < 16; x++) { for (int x = 0; x < 16; x++) {
write(ptr, x, y.relative, z.relative, blockData.get(x, y.pos, z.pos), skyData.get(x, y.pos, z.pos)); write(ptr, x, y.relative, z.relative, blockData.get(x, y.pos, z.pos), skyData.get(x, y.pos, z.pos));
} }
} }
private void collectYStrip(LayerLightEventListener blockLight, LayerLightEventListener skyLight, long ptr, long section, SectionEdge x, SectionEdge z) { private void collectYStrip(long ptr, long section, SectionEdge x, SectionEdge z) {
var pos = SectionPos.of(section); var blockData = getBlockData(section);
var blockData = blockLight.getDataLayerData(pos); var skyData = getSkyData(section);
var skyData = skyLight.getDataLayerData(pos);
if (blockData == null || skyData == null) {
return;
}
for (int y = 0; y < 16; y++) { for (int y = 0; y < 16; y++) {
write(ptr, x.relative, y, z.relative, blockData.get(x.pos, y, z.pos), skyData.get(x.pos, y, z.pos)); write(ptr, x.relative, y, z.relative, blockData.get(x.pos, y, z.pos), skyData.get(x.pos, y, z.pos));
} }
} }
private void collectZStrip(LayerLightEventListener blockLight, LayerLightEventListener skyLight, long ptr, long section, SectionEdge x, SectionEdge y) { private void collectZStrip(long ptr, long section, SectionEdge x, SectionEdge y) {
var pos = SectionPos.of(section); var blockData = getBlockData(section);
var blockData = blockLight.getDataLayerData(pos); var skyData = getSkyData(section);
var skyData = skyLight.getDataLayerData(pos);
if (blockData == null || skyData == null) {
return;
}
for (int z = 0; z < 16; z++) { for (int z = 0; z < 16; z++) {
write(ptr, x.relative, y.relative, z, blockData.get(x.pos, y.pos, z), skyData.get(x.pos, y.pos, z)); write(ptr, x.relative, y.relative, z, blockData.get(x.pos, y.pos, z), skyData.get(x.pos, y.pos, z));
} }
} }
private void collectYZPlane(LayerLightEventListener blockLight, LayerLightEventListener skyLight, long ptr, long section, SectionEdge x) { private void collectYZPlane(long ptr, long section, SectionEdge x) {
var pos = SectionPos.of(section); var blockData = getBlockData(section);
var blockData = blockLight.getDataLayerData(pos); var skyData = getSkyData(section);
var skyData = skyLight.getDataLayerData(pos);
if (blockData == null || skyData == null) {
return;
}
for (int y = 0; y < 16; y++) { for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) { for (int z = 0; z < 16; z++) {
write(ptr, x.relative, y, z, blockData.get(x.pos, y, z), skyData.get(x.pos, y, z)); write(ptr, x.relative, y, z, blockData.get(x.pos, y, z), skyData.get(x.pos, y, z));
@ -324,13 +327,9 @@ public class LightStorage implements Effect {
} }
} }
private void collectXZPlane(LayerLightEventListener blockLight, LayerLightEventListener skyLight, long ptr, long section, SectionEdge y) { private void collectXZPlane(long ptr, long section, SectionEdge y) {
var pos = SectionPos.of(section); var blockData = getBlockData(section);
var blockData = blockLight.getDataLayerData(pos); var skyData = getSkyData(section);
var skyData = skyLight.getDataLayerData(pos);
if (blockData == null || skyData == null) {
return;
}
for (int z = 0; z < 16; z++) { for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) { for (int x = 0; x < 16; x++) {
write(ptr, x, y.relative, z, blockData.get(x, y.pos, z), skyData.get(x, y.pos, z)); write(ptr, x, y.relative, z, blockData.get(x, y.pos, z), skyData.get(x, y.pos, z));
@ -338,13 +337,9 @@ public class LightStorage implements Effect {
} }
} }
private void collectXYPlane(LayerLightEventListener blockLight, LayerLightEventListener skyLight, long ptr, long section, SectionEdge z) { private void collectXYPlane(long ptr, long section, SectionEdge z) {
var pos = SectionPos.of(section); var blockData = getBlockData(section);
var blockData = blockLight.getDataLayerData(pos); var skyData = getSkyData(section);
var skyData = skyLight.getDataLayerData(pos);
if (blockData == null || skyData == null) {
return;
}
for (int y = 0; y < 16; y++) { for (int y = 0; y < 16; y++) {
for (int x = 0; x < 16; x++) { for (int x = 0; x < 16; x++) {
write(ptr, x, y, z.relative, blockData.get(x, y, z.pos), skyData.get(x, y, z.pos)); write(ptr, x, y, z.relative, blockData.get(x, y, z.pos), skyData.get(x, y, z.pos));
@ -352,13 +347,9 @@ public class LightStorage implements Effect {
} }
} }
private void collectCenter(LayerLightEventListener blockLight, LayerLightEventListener skyLight, long ptr, long section) { private void collectCenter(long ptr, long section) {
var pos = SectionPos.of(section); var blockData = getBlockData(section);
var blockData = blockLight.getDataLayerData(pos); var skyData = getSkyData(section);
var skyData = skyLight.getDataLayerData(pos);
if (blockData == null || skyData == null) {
return;
}
for (int y = 0; y < 16; y++) { for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) { for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) { for (int x = 0; x < 16; x++) {
@ -368,7 +359,12 @@ public class LightStorage implements Effect {
} }
} }
private void collectCorners(LayerLightEventListener blockLight, LayerLightEventListener skyLight, long ptr, long section) { private void collectCorners(long ptr, long section) {
var lightEngine = level.getLightEngine();
var blockLight = lightEngine.getLayerListener(LightLayer.BLOCK);
var skyLight = lightEngine.getLayerListener(LightLayer.SKY);
var blockPos = new BlockPos.MutableBlockPos(); var blockPos = new BlockPos.MutableBlockPos();
int xMin = SectionPos.sectionToBlockCoord(SectionPos.x(section)); int xMin = SectionPos.sectionToBlockCoord(SectionPos.x(section));
int yMin = SectionPos.sectionToBlockCoord(SectionPos.y(section)); int yMin = SectionPos.sectionToBlockCoord(SectionPos.y(section));
@ -485,8 +481,10 @@ public class LightStorage implements Effect {
public class DebugVisual implements EffectVisual<LightStorage>, SimpleDynamicVisual { public class DebugVisual implements EffectVisual<LightStorage>, SimpleDynamicVisual {
private final InstanceRecycler<TransformedInstance> boxes; private final InstanceRecycler<TransformedInstance> boxes;
private final Vec3i renderOrigin;
public DebugVisual(VisualizationContext ctx, float partialTick) { public DebugVisual(VisualizationContext ctx, float partialTick) {
renderOrigin = ctx.renderOrigin();
boxes = new InstanceRecycler<>(() -> ctx.instancerProvider() boxes = new InstanceRecycler<>(() -> ctx.instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, HitboxComponent.BOX_MODEL) .instancer(InstanceTypes.TRANSFORMED, HitboxComponent.BOX_MODEL)
.createInstance()); .createInstance());
@ -505,15 +503,15 @@ public class LightStorage implements Effect {
private void setupSectionBoxes() { private void setupSectionBoxes() {
section2ArenaIndex.keySet() section2ArenaIndex.keySet()
.forEach(l -> { .forEach(l -> {
var x = SectionPos.x(l); var x = SectionPos.x(l) * 16 - renderOrigin.getX();
var y = SectionPos.y(l); var y = SectionPos.y(l) * 16 - renderOrigin.getY();
var z = SectionPos.z(l); var z = SectionPos.z(l) * 16 - renderOrigin.getZ();
var instance = boxes.get(); var instance = boxes.get();
instance.setIdentityTransform() instance.setIdentityTransform()
.scale(16)
.translate(x, y, z) .translate(x, y, z)
.scale(16)
.color(255, 255, 0) .color(255, 255, 0)
.light(LightTexture.FULL_BRIGHT) .light(LightTexture.FULL_BRIGHT)
.setChanged(); .setChanged();
@ -526,6 +524,14 @@ public class LightStorage implements Effect {
var base1 = first.base(); var base1 = first.base();
var size1 = first.size(); var size1 = first.size();
float debug1 = base1 * 16 - renderOrigin.getY();
float min2 = Float.POSITIVE_INFINITY;
float max2 = Float.NEGATIVE_INFINITY;
float min3 = Float.POSITIVE_INFINITY;
float max3 = Float.NEGATIVE_INFINITY;
for (int y = 0; y < size1; y++) { for (int y = 0; y < size1; y++) {
var second = first.getRaw(y); var second = first.getRaw(y);
@ -536,6 +542,16 @@ public class LightStorage implements Effect {
var base2 = second.base(); var base2 = second.base();
var size2 = second.size(); var size2 = second.size();
float y2 = (base1 + y) * 16 - renderOrigin.getY() + 7.5f;
min2 = Math.min(min2, base2);
max2 = Math.max(max2, base2 + size2);
float minLocal3 = Float.POSITIVE_INFINITY;
float maxLocal3 = Float.NEGATIVE_INFINITY;
float debug2 = base2 * 16 - renderOrigin.getX();
for (int x = 0; x < size2; x++) { for (int x = 0; x < size2; x++) {
var third = second.getRaw(x); var third = second.getRaw(x);
@ -546,55 +562,43 @@ public class LightStorage implements Effect {
var base3 = third.base(); var base3 = third.base();
var size3 = third.size(); var size3 = third.size();
float x2 = (base2 + x) * 16 - renderOrigin.getX() + 7.5f;
min3 = Math.min(min3, base3);
max3 = Math.max(max3, base3 + size3);
minLocal3 = Math.min(minLocal3, base3);
maxLocal3 = Math.max(maxLocal3, base3 + size3);
float debug3 = base3 * 16 - renderOrigin.getZ();
for (int z = 0; z < size3; z++) { for (int z = 0; z < size3; z++) {
float x1 = base2 * 16;
float y1 = base1 * 16;
float z1 = base3 * 16;
float x2 = (base2 + x) * 16 + 7.5f;
float y2 = (base1 + y) * 16 + 7.5f;
float z2 = (base3 + z) * 16 + 7.5f;
boxes.get() boxes.get()
.setIdentityTransform() .setIdentityTransform()
.translate(x1, y2, z2) .translate(x2, y2, debug3)
.scale(size2 * 16, 1, 1)
.color(255, 0, 0)
.light(LightTexture.FULL_BRIGHT)
.setChanged();
boxes.get()
.setIdentityTransform()
.translate(x2, y1, z2)
.scale(1, size1 * 16, 1)
.color(0, 255, 0)
.light(LightTexture.FULL_BRIGHT)
.setChanged();
boxes.get()
.setIdentityTransform()
.translate(x2, y2, z1)
.scale(1, 1, size3 * 16) .scale(1, 1, size3 * 16)
.color(0, 0, 255) .color(0, 0, 255)
.light(LightTexture.FULL_BRIGHT) .light(LightTexture.FULL_BRIGHT)
.setChanged(); .setChanged();
}
}
if (third.getRaw(z) == 0) {
float x3 = (base2 + x) * 16 + 6f;
float y3 = (base1 + y) * 16 + 6f;
float z3 = (base3 + z) * 16 + 6f;
// Freely representable section that is not filled.
boxes.get() boxes.get()
.setIdentityTransform() .setIdentityTransform()
.translate(x3, y3, z3) .translate(debug2, y2, minLocal3 * 16 - renderOrigin.getZ())
.scale(4) .scale(size2 * 16, 1, (maxLocal3 - minLocal3) * 16)
.color(0, 255, 255) .color(255, 0, 0)
.light(LightTexture.FULL_BRIGHT) .light(LightTexture.FULL_BRIGHT)
.setChanged(); .setChanged();
} }
}
} boxes.get()
} .setIdentityTransform()
.translate(min2 * 16 - renderOrigin.getX(), debug1, min3 * 16 - renderOrigin.getZ())
.scale((max2 - min2) * 16, size1 * 16, (max3 - min3) * 16)
.color(0, 255, 0)
.light(LightTexture.FULL_BRIGHT)
.setChanged();
} }
@Override @Override
@ -607,4 +611,17 @@ public class LightStorage implements Effect {
boxes.delete(); boxes.delete();
} }
} }
private static class ConstantDataLayer extends DataLayer {
private final int value;
private ConstantDataLayer(int value) {
this.value = value;
}
@Override
public int get(int x, int y, int z) {
return value;
}
}
} }

View file

@ -0,0 +1,14 @@
package dev.engine_room.flywheel.backend.mixin.light;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.world.level.lighting.DataLayerStorageMap;
import net.minecraft.world.level.lighting.LayerLightSectionStorage;
import net.minecraft.world.level.lighting.LightEngine;
@Mixin(LightEngine.class)
public interface LightEngineAccessor<M extends DataLayerStorageMap<M>, S extends LayerLightSectionStorage<M>> {
@Accessor("storage")
S flywheel$storage();
}

View file

@ -0,0 +1,15 @@
package dev.engine_room.flywheel.backend.mixin.light;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
@Mixin(targets = "net.minecraft.world.level.lighting.SkyLightSectionStorage.SkyDataLayerStorageMap")
public interface SkyDataLayerStorageMapAccessor {
@Accessor("currentLowestY")
int flywheel$currentLowestY();
@Accessor("topSections")
Long2IntOpenHashMap flywheel$topSections();
}

View file

@ -0,0 +1,46 @@
package dev.engine_room.flywheel.backend.mixin.light;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import dev.engine_room.flywheel.backend.SkyLightSectionStorageExtension;
import net.minecraft.core.Direction;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.chunk.DataLayer;
import net.minecraft.world.level.lighting.LayerLightSectionStorage;
import net.minecraft.world.level.lighting.SkyLightSectionStorage;
@Mixin(SkyLightSectionStorage.class)
public abstract class SkyLightSectionStorageMixin extends LayerLightSectionStorage implements SkyLightSectionStorageExtension {
protected SkyLightSectionStorageMixin() {
super(null, null, null);
}
@Override
@Nullable
public DataLayer flywheel$skyDataLayer(long section) {
// Logic copied from SkyLightSectionStorage#getLightValue, but here we directly return the DataLayer
long l = section;
int i = SectionPos.y(l);
SkyDataLayerStorageMapAccessor skyDataLayerStorageMap = (SkyDataLayerStorageMapAccessor) this.visibleSectionData;
int j = skyDataLayerStorageMap.flywheel$topSections()
.get(SectionPos.getZeroNode(l));
if (j != skyDataLayerStorageMap.flywheel$currentLowestY() && i < j) {
DataLayer dataLayer = this.getDataLayerData(l);
if (dataLayer == null) {
for (; dataLayer == null; dataLayer = this.getDataLayerData(l)) {
if (++i >= j) {
return null;
}
l = SectionPos.offset(l, Direction.UP);
}
}
return dataLayer;
} else {
return null;
}
}
}

View file

@ -9,7 +9,10 @@
"GlStateManagerMixin", "GlStateManagerMixin",
"LevelRendererAccessor", "LevelRendererAccessor",
"OptionsMixin", "OptionsMixin",
"RenderSystemMixin" "RenderSystemMixin",
"light.LightEngineAccessor",
"light.SkyDataLayerStorageMapAccessor",
"light.SkyLightSectionStorageMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1

View file

@ -0,0 +1,44 @@
package dev.engine_room.flywheel.lib.task;
import dev.engine_room.flywheel.api.task.Plan;
import dev.engine_room.flywheel.api.task.TaskExecutor;
import dev.engine_room.flywheel.lib.task.functional.BooleanSupplierWithContext;
/**
* Executes one plan or another, depending on a dynamically evaluated condition.
*
* @param condition The condition to branch on.
* @param onTrue The plan to execute if the condition is true.
* @param <C> The type of the context object.
*/
public record ConditionalPlan<C>(BooleanSupplierWithContext<C> condition,
Plan<C> onTrue) implements SimplyComposedPlan<C> {
public static <C> Builder<C> on(BooleanSupplierWithContext<C> condition) {
return new Builder<>(condition);
}
public static <C> Builder<C> on(BooleanSupplierWithContext.Ignored<C> condition) {
return new Builder<>(condition);
}
@Override
public void execute(TaskExecutor taskExecutor, C context, Runnable onCompletion) {
if (condition.getAsBoolean(context)) {
onTrue.execute(taskExecutor, context, onCompletion);
} else {
onCompletion.run();
}
}
public static final class Builder<C> {
private final BooleanSupplierWithContext<C> condition;
public Builder(BooleanSupplierWithContext<C> condition) {
this.condition = condition;
}
public ConditionalPlan<C> then(Plan<C> onTrue) {
return new ConditionalPlan<>(condition, onTrue);
}
}
}

View file

@ -0,0 +1,8 @@
package dev.engine_room.flywheel.impl;
public class ImplDebugFlags {
/**
* Debug flag to globally turn beginFrame/tick off.
*/
public static boolean PAUSE_UPDATES = false;
}

View file

@ -15,6 +15,8 @@ import dev.engine_room.flywheel.api.visual.ShaderLightVisual;
import dev.engine_room.flywheel.api.visual.TickableVisual; import dev.engine_room.flywheel.api.visual.TickableVisual;
import dev.engine_room.flywheel.api.visual.Visual; import dev.engine_room.flywheel.api.visual.Visual;
import dev.engine_room.flywheel.api.visualization.VisualizationContext; import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.impl.ImplDebugFlags;
import dev.engine_room.flywheel.lib.task.ConditionalPlan;
import dev.engine_room.flywheel.lib.task.ForEachPlan; import dev.engine_room.flywheel.lib.task.ForEachPlan;
import dev.engine_room.flywheel.lib.task.NestedPlan; import dev.engine_room.flywheel.lib.task.NestedPlan;
import dev.engine_room.flywheel.lib.task.PlanMap; import dev.engine_room.flywheel.lib.task.PlanMap;
@ -42,11 +44,16 @@ public abstract class Storage<T> {
} }
public Plan<DynamicVisual.Context> framePlan() { public Plan<DynamicVisual.Context> framePlan() {
return NestedPlan.of(dynamicVisuals, lightUpdatedVisuals.plan(), ForEachPlan.of(() -> simpleDynamicVisuals, SimpleDynamicVisual::beginFrame)); var update = ConditionalPlan.<DynamicVisual.Context>on(() -> !ImplDebugFlags.PAUSE_UPDATES)
.then(NestedPlan.of(dynamicVisuals, ForEachPlan.of(() -> simpleDynamicVisuals, SimpleDynamicVisual::beginFrame)));
// Do light updates regardless.
return NestedPlan.of(lightUpdatedVisuals.plan(), update);
} }
public Plan<TickableVisual.Context> tickPlan() { public Plan<TickableVisual.Context> tickPlan() {
return NestedPlan.of(tickableVisuals, ForEachPlan.of(() -> simpleTickableVisuals, SimpleTickableVisual::tick)); return ConditionalPlan.<TickableVisual.Context>on(() -> !ImplDebugFlags.PAUSE_UPDATES)
.then(NestedPlan.of(tickableVisuals, ForEachPlan.of(() -> simpleTickableVisuals, SimpleTickableVisual::tick)));
} }
public LightUpdatedVisualStorage lightUpdatedVisuals() { public LightUpdatedVisualStorage lightUpdatedVisuals() {

View file

@ -8,9 +8,9 @@ import com.mojang.brigadier.context.CommandContext;
import dev.engine_room.flywheel.api.backend.Backend; import dev.engine_room.flywheel.api.backend.Backend;
import dev.engine_room.flywheel.api.backend.BackendManager; import dev.engine_room.flywheel.api.backend.BackendManager;
import dev.engine_room.flywheel.backend.BackendDebugFlags;
import dev.engine_room.flywheel.backend.compile.LightSmoothness; import dev.engine_room.flywheel.backend.compile.LightSmoothness;
import dev.engine_room.flywheel.backend.compile.PipelineCompiler; import dev.engine_room.flywheel.backend.compile.PipelineCompiler;
import dev.engine_room.flywheel.backend.engine.LightStorage;
import dev.engine_room.flywheel.backend.engine.uniform.DebugMode; import dev.engine_room.flywheel.backend.engine.uniform.DebugMode;
import dev.engine_room.flywheel.backend.engine.uniform.FrameUniforms; import dev.engine_room.flywheel.backend.engine.uniform.FrameUniforms;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
@ -179,12 +179,24 @@ public final class FlwCommands {
debug.then(ClientCommandManager.literal("lightSections") debug.then(ClientCommandManager.literal("lightSections")
.then(ClientCommandManager.literal("on") .then(ClientCommandManager.literal("on")
.executes(context -> { .executes(context -> {
LightStorage.DEBUG = true; BackendDebugFlags.LIGHT_STORAGE_VIEW = true;
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
})) }))
.then(ClientCommandManager.literal("off") .then(ClientCommandManager.literal("off")
.executes(context -> { .executes(context -> {
LightStorage.DEBUG = false; BackendDebugFlags.LIGHT_STORAGE_VIEW = false;
return Command.SINGLE_SUCCESS;
})));
debug.then(ClientCommandManager.literal("pauseUpdates")
.then(ClientCommandManager.literal("on")
.executes(context -> {
ImplDebugFlags.PAUSE_UPDATES = true;
return Command.SINGLE_SUCCESS;
}))
.then(ClientCommandManager.literal("off")
.executes(context -> {
ImplDebugFlags.PAUSE_UPDATES = false;
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
}))); })));

View file

@ -6,9 +6,9 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import dev.engine_room.flywheel.api.backend.Backend; import dev.engine_room.flywheel.api.backend.Backend;
import dev.engine_room.flywheel.api.backend.BackendManager; import dev.engine_room.flywheel.api.backend.BackendManager;
import dev.engine_room.flywheel.backend.BackendDebugFlags;
import dev.engine_room.flywheel.backend.compile.LightSmoothness; import dev.engine_room.flywheel.backend.compile.LightSmoothness;
import dev.engine_room.flywheel.backend.compile.PipelineCompiler; import dev.engine_room.flywheel.backend.compile.PipelineCompiler;
import dev.engine_room.flywheel.backend.engine.LightStorage;
import dev.engine_room.flywheel.backend.engine.uniform.DebugMode; import dev.engine_room.flywheel.backend.engine.uniform.DebugMode;
import dev.engine_room.flywheel.backend.engine.uniform.FrameUniforms; import dev.engine_room.flywheel.backend.engine.uniform.FrameUniforms;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@ -172,14 +172,27 @@ public final class FlwCommands {
debug.then(Commands.literal("lightSections") debug.then(Commands.literal("lightSections")
.then(Commands.literal("on") .then(Commands.literal("on")
.executes(context -> { .executes(context -> {
LightStorage.DEBUG = true; BackendDebugFlags.LIGHT_STORAGE_VIEW = true;
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
})) }))
.then(Commands.literal("off") .then(Commands.literal("off")
.executes(context -> { .executes(context -> {
LightStorage.DEBUG = false; BackendDebugFlags.LIGHT_STORAGE_VIEW = false;
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
}))); })));
debug.then(Commands.literal("pauseUpdates")
.then(Commands.literal("on")
.executes(context -> {
ImplDebugFlags.PAUSE_UPDATES = true;
return Command.SINGLE_SUCCESS;
}))
.then(Commands.literal("off")
.executes(context -> {
ImplDebugFlags.PAUSE_UPDATES = false;
return Command.SINGLE_SUCCESS;
})));
return debug; return debug;
} }