Skip to content

Commit

Permalink
Effective debugging
Browse files Browse the repository at this point in the history
- Add light storage debug view courtesy of effect visuals
- Yellow boxes are filled in sections
- Red/green/blue bars represent the LUT
- Cyan boxes are the "wasted space" in the LUT. Freely representable
  sections that are simply not filled in
  • Loading branch information
Jozufozu committed Nov 12, 2024
1 parent 6f0a2b0 commit cfcb424
Show file tree
Hide file tree
Showing 4 changed files with 325 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// Massive kudos to RogueLogix for figuring out this LUT scheme.
// First layer is Y, then X, then Z.
public final class LightLut {
private final Layer<Layer<IntLayer>> indices = new Layer<>();
public final Layer<Layer<IntLayer>> indices = new Layer<>();

public void add(long position, int index) {
final var x = SectionPos.x(position);
Expand Down Expand Up @@ -49,7 +49,7 @@ public IntArrayList flatten() {
return out;
}

private static final class Layer<T> {
public static final class Layer<T> {
private boolean hasBase = false;
private int base = 0;
private Object[] nextLayer = new Object[0];
Expand Down Expand Up @@ -79,23 +79,34 @@ public void fillLut(IntArrayList lut, BiConsumer<T, IntArrayList> inner) {
}
}

public int base() {
return base;
}

public int size() {
return nextLayer.length;
}

@Nullable
public T get(int i) {
if (!hasBase) {
public T getRaw(int i) {
if (i < 0) {
return null;
}

if (i < base) {
if (i >= nextLayer.length) {
return null;
}

final var offset = i - base;
return (T) nextLayer[i];
}

if (offset >= nextLayer.length) {
@Nullable
public T get(int i) {
if (!hasBase) {
return null;
}

return (T) nextLayer[offset];
return getRaw(i - base);
}

public T computeIfAbsent(int i, Supplier<T> ifAbsent) {
Expand Down Expand Up @@ -142,7 +153,7 @@ private void rebase(int newBase) {
}
}

private static final class IntLayer {
public static final class IntLayer {
private boolean hasBase = false;
private int base = 0;
private int[] indices = new int[0];
Expand All @@ -156,6 +167,34 @@ public void fillLut(IntArrayList lut) {
}
}

public int base() {
return base;
}

public int size() {
return indices.length;
}

public int getRaw(int i) {
if (i < 0) {
return 0;
}

if (i >= indices.length) {
return 0;
}

return indices[i];
}

public int get(int i) {
if (!hasBase) {
return 0;
}

return getRaw(i - base);
}

public void set(int i, int index) {
if (!hasBase) {
base = i;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,25 @@
import org.lwjgl.system.MemoryUtil;

import dev.engine_room.flywheel.api.task.Plan;
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.engine.indirect.StagingBuffer;
import dev.engine_room.flywheel.backend.gl.buffer.GlBuffer;
import dev.engine_room.flywheel.lib.instance.InstanceTypes;
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
import dev.engine_room.flywheel.lib.math.MoreMath;
import dev.engine_room.flywheel.lib.task.SimplePlan;
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
import dev.engine_room.flywheel.lib.visual.component.HitboxComponent;
import dev.engine_room.flywheel.lib.visual.util.InstanceRecycler;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.longs.Long2IntMap;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
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.world.level.LevelAccessor;
Expand All @@ -35,7 +45,9 @@
*
* <p>Thus, each section occupies 5832 bytes.
*/
public class LightStorage {
public class LightStorage implements Effect {
public static boolean DEBUG = false;

public static final int BLOCKS_PER_SECTION = 18 * 18 * 18;
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;
Expand All @@ -50,6 +62,7 @@ public class LightStorage {

private final BitSet changed = new BitSet();
private boolean needsLutRebuild = false;
private boolean isDebugOn = false;

private final LongSet updatedSections = new LongOpenHashSet();
@Nullable
Expand All @@ -63,6 +76,16 @@ public LightStorage(LevelAccessor level) {
section2ArenaIndex.defaultReturnValue(INVALID_SECTION);
}

@Override
public LevelAccessor level() {
return level;
}

@Override
public EffectVisual<?> visualize(VisualizationContext ctx, float partialTick) {
return new DebugVisual(ctx, partialTick);
}

/**
* Set the set of requested sections.
* <p> When set, this will be processed in the next frame plan. It may not be set every frame.
Expand All @@ -79,6 +102,22 @@ public void onLightUpdate(long section) {

public <C> Plan<C> createFramePlan() {
return SimplePlan.of(() -> {
if (DEBUG != isDebugOn) {
var visualizationManager = VisualizationManager.get(level);

// Really should be non-null, but just in case.
if (visualizationManager != null) {
if (DEBUG) {
visualizationManager.effects()
.queueAdd(this);
} else {
visualizationManager.effects()
.queueRemove(this);
}
}
isDebugOn = DEBUG;
}

if (updatedSections.isEmpty() && requestedSections == null) {
return;
}
Expand Down Expand Up @@ -442,4 +481,130 @@ private enum SectionEdge {
this.sectionOffset = sectionOffset;
}
}

public class DebugVisual implements EffectVisual<LightStorage>, SimpleDynamicVisual {

private final InstanceRecycler<TransformedInstance> boxes;

public DebugVisual(VisualizationContext ctx, float partialTick) {
boxes = new InstanceRecycler<>(() -> ctx.instancerProvider()
.instancer(InstanceTypes.TRANSFORMED, HitboxComponent.BOX_MODEL)
.createInstance());
}

@Override
public void beginFrame(Context ctx) {
boxes.resetCount();

setupSectionBoxes();
setupLutRangeBoxes();

boxes.discardExtra();
}

private void setupSectionBoxes() {
section2ArenaIndex.keySet()
.forEach(l -> {
var x = SectionPos.x(l);
var y = SectionPos.y(l);
var z = SectionPos.z(l);

var instance = boxes.get();

instance.setIdentityTransform()
.scale(16)
.translate(x, y, z)
.color(255, 255, 0)
.light(LightTexture.FULL_BRIGHT)
.setChanged();
});
}

private void setupLutRangeBoxes() {
var first = lut.indices;

var base1 = first.base();
var size1 = first.size();

for (int y = 0; y < size1; y++) {
var second = first.getRaw(y);

if (second == null) {
continue;
}

var base2 = second.base();
var size2 = second.size();

for (int x = 0; x < size2; x++) {
var third = second.getRaw(x);

if (third == null) {
continue;
}

var base3 = third.base();
var size3 = third.size();

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)
.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)
.light(LightTexture.FULL_BRIGHT)
.setChanged();
}
}
}
}
}

@Override
public void update(float partialTick) {

}

@Override
public void delete() {
boxes.delete();
}
}
}
Loading

0 comments on commit cfcb424

Please sign in to comment.