Sky's edge

- Fix darkness when collecting light sections from high in the air
- Add dummy data layer impl to return a constant
- Empty sky section returns 15
- Empty block section returns 0
- Copy vanilla's logic for determining which data layer to use for a
  given position's sky light value
- While I'm at it, retrieve light section storage via an accessor to
  avoid allocating SectionPos objects
- Improve lut debug view
This commit is contained in:
Jozufozu 2024-11-14 16:39:21 -08:00
parent ac544245b3
commit 6431a84f67
6 changed files with 215 additions and 118 deletions

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

@ -10,8 +10,10 @@ import dev.engine_room.flywheel.api.visual.Effect;
import dev.engine_room.flywheel.api.visual.EffectVisual;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.api.visualization.VisualizationManager;
import dev.engine_room.flywheel.backend.SkyLightSectionStorageExtension;
import dev.engine_room.flywheel.backend.engine.indirect.StagingBuffer;
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.TransformedInstance;
import dev.engine_room.flywheel.lib.math.MoreMath;
@ -27,9 +29,10 @@ import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.core.Vec3i;
import net.minecraft.world.level.LevelAccessor;
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.
@ -55,6 +58,9 @@ public class LightStorage implements Effect {
private static final int DEFAULT_ARENA_CAPACITY_SECTIONS = 64;
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 LightLut lut;
private final CpuArena arena;
@ -194,11 +200,6 @@ public class LightStorage implements Effect {
}
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);
changed.set(index);
@ -210,21 +211,21 @@ public class LightStorage implements Effect {
collectSolidData(ptr, section);
collectCenter(blockLight, skyLight, ptr, section);
collectCenter(ptr, section);
for (SectionEdge i : SectionEdge.values()) {
collectYZPlane(blockLight, skyLight, ptr, SectionPos.offset(section, i.sectionOffset, 0, 0), i);
collectXZPlane(blockLight, skyLight, ptr, SectionPos.offset(section, 0, i.sectionOffset, 0), i);
collectXYPlane(blockLight, skyLight, ptr, SectionPos.offset(section, 0, 0, i.sectionOffset), i);
collectYZPlane(ptr, SectionPos.offset(section, i.sectionOffset, 0, 0), i);
collectXZPlane(ptr, SectionPos.offset(section, 0, i.sectionOffset, 0), i);
collectXYPlane(ptr, SectionPos.offset(section, 0, 0, i.sectionOffset), i);
for (SectionEdge j : SectionEdge.values()) {
collectXStrip(blockLight, skyLight, 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);
collectZStrip(blockLight, skyLight, ptr, SectionPos.offset(section, i.sectionOffset, j.sectionOffset, 0), i, j);
collectXStrip(ptr, SectionPos.offset(section, 0, i.sectionOffset, j.sectionOffset), i, j);
collectYStrip(ptr, SectionPos.offset(section, i.sectionOffset, 0, j.sectionOffset), 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) {
@ -259,64 +260,59 @@ public class LightStorage implements Effect {
}
}
private void writeSolid(long ptr, int index, boolean blockValid) {
if (!blockValid) {
return;
}
int intIndex = index / Integer.SIZE;
int bitIndex = index % Integer.SIZE;
private DataLayer getSkyData(long section) {
var sky = level.getLightEngine()
.getLayerListener(LightLayer.SKY);
var skyStorage = (SkyLightSectionStorageExtension) ((LightEngineAccessor<?, ?>) sky).flywheel$storage();
long offset = intIndex * Integer.BYTES;
var out = skyStorage.flywheel$skyDataLayer(section);
int bitField = MemoryUtil.memGetInt(ptr + offset);
bitField |= 1 << bitIndex;
MemoryUtil.memPutInt(ptr + offset, bitField);
if (out == null) {
return EMPTY_SKY_DATA;
}
private void collectXStrip(LayerLightEventListener blockLight, LayerLightEventListener skyLight, long ptr, long section, SectionEdge y, SectionEdge z) {
var pos = SectionPos.of(section);
var blockData = blockLight.getDataLayerData(pos);
var skyData = skyLight.getDataLayerData(pos);
if (blockData == null || skyData == null) {
return;
return out;
}
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++) {
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) {
var pos = SectionPos.of(section);
var blockData = blockLight.getDataLayerData(pos);
var skyData = skyLight.getDataLayerData(pos);
if (blockData == null || skyData == null) {
return;
}
private void collectYStrip(long ptr, long section, SectionEdge x, SectionEdge z) {
var blockData = getBlockData(section);
var skyData = getSkyData(section);
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));
}
}
private void collectZStrip(LayerLightEventListener blockLight, LayerLightEventListener skyLight, long ptr, long section, SectionEdge x, SectionEdge y) {
var pos = SectionPos.of(section);
var blockData = blockLight.getDataLayerData(pos);
var skyData = skyLight.getDataLayerData(pos);
if (blockData == null || skyData == null) {
return;
}
private void collectZStrip(long ptr, long section, SectionEdge x, SectionEdge y) {
var blockData = getBlockData(section);
var skyData = getSkyData(section);
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));
}
}
private void collectYZPlane(LayerLightEventListener blockLight, LayerLightEventListener skyLight, long ptr, long section, SectionEdge x) {
var pos = SectionPos.of(section);
var blockData = blockLight.getDataLayerData(pos);
var skyData = skyLight.getDataLayerData(pos);
if (blockData == null || skyData == null) {
return;
}
private void collectYZPlane(long ptr, long section, SectionEdge x) {
var blockData = getBlockData(section);
var skyData = getSkyData(section);
for (int y = 0; y < 16; y++) {
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));
@ -324,13 +320,9 @@ public class LightStorage implements Effect {
}
}
private void collectXZPlane(LayerLightEventListener blockLight, LayerLightEventListener skyLight, long ptr, long section, SectionEdge y) {
var pos = SectionPos.of(section);
var blockData = blockLight.getDataLayerData(pos);
var skyData = skyLight.getDataLayerData(pos);
if (blockData == null || skyData == null) {
return;
}
private void collectXZPlane(long ptr, long section, SectionEdge y) {
var blockData = getBlockData(section);
var skyData = getSkyData(section);
for (int z = 0; z < 16; z++) {
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));
@ -338,13 +330,9 @@ public class LightStorage implements Effect {
}
}
private void collectXYPlane(LayerLightEventListener blockLight, LayerLightEventListener skyLight, long ptr, long section, SectionEdge z) {
var pos = SectionPos.of(section);
var blockData = blockLight.getDataLayerData(pos);
var skyData = skyLight.getDataLayerData(pos);
if (blockData == null || skyData == null) {
return;
}
private void collectXYPlane(long ptr, long section, SectionEdge z) {
var blockData = getBlockData(section);
var skyData = getSkyData(section);
for (int y = 0; y < 16; y++) {
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));
@ -352,13 +340,9 @@ public class LightStorage implements Effect {
}
}
private void collectCenter(LayerLightEventListener blockLight, LayerLightEventListener skyLight, long ptr, long section) {
var pos = SectionPos.of(section);
var blockData = blockLight.getDataLayerData(pos);
var skyData = skyLight.getDataLayerData(pos);
if (blockData == null || skyData == null) {
return;
}
private void collectCenter(long ptr, long section) {
var blockData = getBlockData(section);
var skyData = getSkyData(section);
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
@ -368,7 +352,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();
int xMin = SectionPos.sectionToBlockCoord(SectionPos.x(section));
int yMin = SectionPos.sectionToBlockCoord(SectionPos.y(section));
@ -485,8 +474,10 @@ public class LightStorage implements Effect {
public class DebugVisual implements EffectVisual<LightStorage>, SimpleDynamicVisual {
private final InstanceRecycler<TransformedInstance> boxes;
private final Vec3i renderOrigin;
public DebugVisual(VisualizationContext ctx, float partialTick) {
renderOrigin = ctx.renderOrigin();
boxes = new InstanceRecycler<>(() -> ctx.instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, HitboxComponent.BOX_MODEL)
.createInstance());
@ -505,15 +496,15 @@ public class LightStorage implements Effect {
private void setupSectionBoxes() {
section2ArenaIndex.keySet()
.forEach(l -> {
var x = SectionPos.x(l);
var y = SectionPos.y(l);
var z = SectionPos.z(l);
var x = SectionPos.x(l) * 16 - renderOrigin.getX();
var y = SectionPos.y(l) * 16 - renderOrigin.getY();
var z = SectionPos.z(l) * 16 - renderOrigin.getZ();
var instance = boxes.get();
instance.setIdentityTransform()
.scale(16)
.translate(x, y, z)
.scale(16)
.color(255, 255, 0)
.light(LightTexture.FULL_BRIGHT)
.setChanged();
@ -526,6 +517,14 @@ public class LightStorage implements Effect {
var base1 = first.base();
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++) {
var second = first.getRaw(y);
@ -536,6 +535,16 @@ public class LightStorage implements Effect {
var base2 = second.base();
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++) {
var third = second.getRaw(x);
@ -546,55 +555,43 @@ public class LightStorage implements Effect {
var base3 = third.base();
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++) {
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()
.setIdentityTransform()
.translate(x1, y2, z2)
.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)
.translate(x2, y2, debug3)
.scale(1, 1, size3 * 16)
.color(0, 0, 255)
.light(LightTexture.FULL_BRIGHT)
.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()
.setIdentityTransform()
.translate(x3, y3, z3)
.scale(4)
.color(0, 255, 255)
.translate(debug2, y2, minLocal3 * 16 - renderOrigin.getZ())
.scale(size2 * 16, 1, (maxLocal3 - minLocal3) * 16)
.color(255, 0, 0)
.light(LightTexture.FULL_BRIGHT)
.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
@ -607,4 +604,17 @@ public class LightStorage implements Effect {
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",
"LevelRendererAccessor",
"OptionsMixin",
"RenderSystemMixin"
"RenderSystemMixin",
"light.LightEngineAccessor",
"light.SkyDataLayerStorageMapAccessor",
"light.SkyLightSectionStorageMixin"
],
"injectors": {
"defaultRequire": 1