From cfcb4246af5ace62ddfe794eb6c40796cf2c5af7 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Tue, 12 Nov 2024 13:33:30 -0800 Subject: [PATCH] Effective debugging - 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 --- .../flywheel/backend/engine/LightLut.java | 57 +++++- .../flywheel/backend/engine/LightStorage.java | 167 +++++++++++++++++- .../flywheel/impl/FlwCommands.java | 95 ++++++---- .../flywheel/impl/FlwCommands.java | 86 +++++---- 4 files changed, 325 insertions(+), 80 deletions(-) diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java index 885c616dd..ffe0b2b4d 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java @@ -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> indices = new Layer<>(); + public final Layer> indices = new Layer<>(); public void add(long position, int index) { final var x = SectionPos.x(position); @@ -49,7 +49,7 @@ public IntArrayList flatten() { return out; } - private static final class Layer { + public static final class Layer { private boolean hasBase = false; private int base = 0; private Object[] nextLayer = new Object[0]; @@ -79,23 +79,34 @@ public void fillLut(IntArrayList lut, BiConsumer 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 ifAbsent) { @@ -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]; @@ -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; diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java index f8b91be89..9b3f779f7 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java @@ -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; @@ -35,7 +45,9 @@ * *

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; @@ -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 @@ -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. *

When set, this will be processed in the next frame plan. It may not be set every frame. @@ -79,6 +102,22 @@ public void onLightUpdate(long section) { public Plan 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; } @@ -442,4 +481,130 @@ private enum SectionEdge { this.sectionOffset = sectionOffset; } } + + public class DebugVisual implements EffectVisual, SimpleDynamicVisual { + + private final InstanceRecycler 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(); + } + } } diff --git a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java index 46817bcaf..45e0703d6 100644 --- a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java +++ b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java @@ -10,6 +10,7 @@ import dev.engine_room.flywheel.api.backend.BackendManager; import dev.engine_room.flywheel.backend.compile.LightSmoothness; 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.FrameUniforms; import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; @@ -85,7 +86,57 @@ public static void registerClientCommands(CommandDispatcher { + var oldValue = FabricFlwConfig.INSTANCE.backendConfig.lightSmoothness; + var newValue = context.getArgument("mode", LightSmoothness.class); + + if (oldValue != newValue) { + FabricFlwConfig.INSTANCE.backendConfig.lightSmoothness = newValue; + FabricFlwConfig.INSTANCE.save(); + PipelineCompiler.deleteAll(); + } + return Command.SINGLE_SUCCESS; + }))); + + command.then(ClientCommandManager.literal("useLightDirections") + .executes(context -> { + if (FabricFlwConfig.INSTANCE.backendConfig.useLightDirections) { + context.getSource() + .sendFeedback(Component.translatable("command.flywheel.use_light_directions.get.on")); + } else { + context.getSource() + .sendFeedback(Component.translatable("command.flywheel.use_light_directions.get.off")); + } + return Command.SINGLE_SUCCESS; + }) + .then(ClientCommandManager.literal("on") + .executes(context -> { + FabricFlwConfig.INSTANCE.backendConfig.useLightDirections = true; + FabricFlwConfig.INSTANCE.save(); + context.getSource() + .sendFeedback(Component.translatable("command.flywheel.use_light_directions.set.on")); + return Command.SINGLE_SUCCESS; + })) + .then(ClientCommandManager.literal("off") + .executes(context -> { + FabricFlwConfig.INSTANCE.backendConfig.useLightDirections = false; + FabricFlwConfig.INSTANCE.save(); + context.getSource() + .sendFeedback(Component.translatable("command.flywheel.use_light_directions.set.off")); + return Command.SINGLE_SUCCESS; + }))); + + command.then(createDebugCommand()); + + dispatcher.register(command); + } + + private static LiteralArgumentBuilder createDebugCommand() { + var debug = ClientCommandManager.literal("debug"); + + debug.then(ClientCommandManager.literal("crumbling") .then(ClientCommandManager.argument("pos", BlockPosArgument.blockPos()) .then(ClientCommandManager.argument("stage", IntegerArgumentType.integer(0, 9)) .executes(context -> { @@ -105,7 +156,7 @@ public static void registerClientCommands(CommandDispatcher { DebugMode mode = context.getArgument("mode", DebugMode.class); @@ -113,7 +164,7 @@ public static void registerClientCommands(CommandDispatcher { FrameUniforms.captureFrustum(); @@ -125,49 +176,19 @@ public static void registerClientCommands(CommandDispatcher { - var oldValue = FabricFlwConfig.INSTANCE.backendConfig.lightSmoothness; - var newValue = context.getArgument("mode", LightSmoothness.class); - - if (oldValue != newValue) { - FabricFlwConfig.INSTANCE.backendConfig.lightSmoothness = newValue; - FabricFlwConfig.INSTANCE.save(); - PipelineCompiler.deleteAll(); - } - return Command.SINGLE_SUCCESS; - }))); - - command.then(ClientCommandManager.literal("useLightDirections") - .executes(context -> { - if (FabricFlwConfig.INSTANCE.backendConfig.useLightDirections) { - context.getSource() - .sendFeedback(Component.translatable("command.flywheel.use_light_directions.get.on")); - } else { - context.getSource() - .sendFeedback(Component.translatable("command.flywheel.use_light_directions.get.off")); - } - return Command.SINGLE_SUCCESS; - }) + debug.then(ClientCommandManager.literal("lightSections") .then(ClientCommandManager.literal("on") .executes(context -> { - FabricFlwConfig.INSTANCE.backendConfig.useLightDirections = true; - FabricFlwConfig.INSTANCE.save(); - context.getSource() - .sendFeedback(Component.translatable("command.flywheel.use_light_directions.set.on")); + LightStorage.DEBUG = true; return Command.SINGLE_SUCCESS; })) .then(ClientCommandManager.literal("off") .executes(context -> { - FabricFlwConfig.INSTANCE.backendConfig.useLightDirections = false; - FabricFlwConfig.INSTANCE.save(); - context.getSource() - .sendFeedback(Component.translatable("command.flywheel.use_light_directions.set.off")); + LightStorage.DEBUG = false; return Command.SINGLE_SUCCESS; }))); - dispatcher.register(command); + return debug; } // Client version of BlockPosArgument.getBlockPos diff --git a/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java b/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java index 87a80c76f..0fcb96d35 100644 --- a/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java +++ b/forge/src/main/java/dev/engine_room/flywheel/impl/FlwCommands.java @@ -8,6 +8,7 @@ import dev.engine_room.flywheel.api.backend.BackendManager; import dev.engine_room.flywheel.backend.compile.LightSmoothness; 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.FrameUniforms; import net.minecraft.client.Minecraft; @@ -83,7 +84,52 @@ public static void registerClientCommands(RegisterClientCommandsEvent event) { return Command.SINGLE_SUCCESS; }))); - command.then(Commands.literal("crumbling") + var lightSmoothnessValue = ForgeFlwConfig.INSTANCE.client.backendConfig.lightSmoothness; + command.then(Commands.literal("lightSmoothness") + .then(Commands.argument("mode", LightSmoothnessArgument.INSTANCE) + .executes(context -> { + var oldValue = lightSmoothnessValue.get(); + var newValue = context.getArgument("mode", LightSmoothness.class); + + if (oldValue != newValue) { + lightSmoothnessValue.set(newValue); + PipelineCompiler.deleteAll(); + } + return Command.SINGLE_SUCCESS; + }))); + + var useLightDirectionsValue = ForgeFlwConfig.INSTANCE.client.backendConfig.useLightDirections; + command.then(Commands.literal("useLightDirections") + .executes(context -> { + if (useLightDirectionsValue.get()) { + sendMessage(context.getSource(), Component.translatable("command.flywheel.use_light_directions.get.on")); + } else { + sendMessage(context.getSource(), Component.translatable("command.flywheel.use_light_directions.get.off")); + } + return Command.SINGLE_SUCCESS; + }) + .then(Commands.literal("on") + .executes(context -> { + useLightDirectionsValue.set(true); + sendMessage(context.getSource(), Component.translatable("command.flywheel.use_light_directions.set.on")); + return Command.SINGLE_SUCCESS; + })) + .then(Commands.literal("off") + .executes(context -> { + useLightDirectionsValue.set(false); + sendMessage(context.getSource(), Component.translatable("command.flywheel.use_light_directions.set.off")); + return Command.SINGLE_SUCCESS; + }))); + + command.then(createDebugCommand()); + + event.getDispatcher().register(command); + } + + private static LiteralArgumentBuilder createDebugCommand() { + var debug = Commands.literal("debug"); + + debug.then(Commands.literal("crumbling") .then(Commands.argument("pos", BlockPosArgument.blockPos()) .then(Commands.argument("stage", IntegerArgumentType.integer(0, 9)) .executes(context -> { @@ -103,7 +149,7 @@ public static void registerClientCommands(RegisterClientCommandsEvent event) { return Command.SINGLE_SUCCESS; })))); - command.then(Commands.literal("debug") + debug.then(Commands.literal("shader") .then(Commands.argument("mode", DebugModeArgument.INSTANCE) .executes(context -> { DebugMode mode = context.getArgument("mode", DebugMode.class); @@ -111,7 +157,7 @@ public static void registerClientCommands(RegisterClientCommandsEvent event) { return Command.SINGLE_SUCCESS; }))); - command.then(Commands.literal("frustum") + debug.then(Commands.literal("frustum") .then(Commands.literal("capture") .executes(context -> { FrameUniforms.captureFrustum(); @@ -123,44 +169,18 @@ public static void registerClientCommands(RegisterClientCommandsEvent event) { return Command.SINGLE_SUCCESS; }))); - var lightSmoothnessValue = ForgeFlwConfig.INSTANCE.client.backendConfig.lightSmoothness; - command.then(Commands.literal("lightSmoothness") - .then(Commands.argument("mode", LightSmoothnessArgument.INSTANCE) - .executes(context -> { - var oldValue = lightSmoothnessValue.get(); - var newValue = context.getArgument("mode", LightSmoothness.class); - - if (oldValue != newValue) { - lightSmoothnessValue.set(newValue); - PipelineCompiler.deleteAll(); - } - return Command.SINGLE_SUCCESS; - }))); - - var useLightDirectionsValue = ForgeFlwConfig.INSTANCE.client.backendConfig.useLightDirections; - command.then(Commands.literal("useLightDirections") - .executes(context -> { - if (useLightDirectionsValue.get()) { - sendMessage(context.getSource(), Component.translatable("command.flywheel.use_light_directions.get.on")); - } else { - sendMessage(context.getSource(), Component.translatable("command.flywheel.use_light_directions.get.off")); - } - return Command.SINGLE_SUCCESS; - }) + debug.then(Commands.literal("lightSections") .then(Commands.literal("on") .executes(context -> { - useLightDirectionsValue.set(true); - sendMessage(context.getSource(), Component.translatable("command.flywheel.use_light_directions.set.on")); + LightStorage.DEBUG = true; return Command.SINGLE_SUCCESS; })) .then(Commands.literal("off") .executes(context -> { - useLightDirectionsValue.set(false); - sendMessage(context.getSource(), Component.translatable("command.flywheel.use_light_directions.set.off")); + LightStorage.DEBUG = false; return Command.SINGLE_SUCCESS; }))); - - event.getDispatcher().register(command); + return debug; } private static void sendMessage(CommandSourceStack source, Component message) {