SmoothLit joins the battle

- Not attached to the name
- Add SmoothLitVisual opt in interface, allowing any visuals to
  contribute light sections to the arena
- Remove lightChunks from VisualEmbedding, it has been usurped
- Pass total collected light sections from BEs, Es, and effects to the
  engine interface. It seemed the most proper way to hand off
  information from the impl to the backend
- Add SmoothLitVisualStorage to maintain the set of collected sections,
  though at the moment it is very naive and simply unions everything
  upon request, which is also naively done every frame
This commit is contained in:
Jozufozu 2024-07-06 22:19:47 -07:00
parent b9a51d0e5f
commit 2837762cbf
14 changed files with 132 additions and 47 deletions

View file

@ -9,6 +9,7 @@ import dev.engine_room.flywheel.api.instance.Instance;
import dev.engine_room.flywheel.api.task.Plan; import dev.engine_room.flywheel.api.task.Plan;
import dev.engine_room.flywheel.api.task.TaskExecutor; import dev.engine_room.flywheel.api.task.TaskExecutor;
import dev.engine_room.flywheel.api.visualization.VisualizationContext; import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
@ -68,6 +69,13 @@ public interface Engine {
*/ */
void delete(); void delete();
/**
* Assign the set of sections that visuals have requested GPU light for.
*
* @param sections The set of sections.
*/
void lightSections(LongSet sections);
/** /**
* A block to be rendered as a crumbling overlay. * A block to be rendered as a crumbling overlay.
* @param progress The progress of the crumbling animation in the range [0, 10). * @param progress The progress of the crumbling animation in the range [0, 10).

View file

@ -2,6 +2,8 @@ package dev.engine_room.flywheel.api.visual;
import java.util.function.LongConsumer; import java.util.function.LongConsumer;
import org.jetbrains.annotations.ApiStatus;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
/** /**
@ -51,6 +53,7 @@ public interface LitVisual extends Visual {
* A notifier object that can be used to indicate to the impl * A notifier object that can be used to indicate to the impl
* that the sections a visual is contained in have changed. * that the sections a visual is contained in have changed.
*/ */
@ApiStatus.NonExtendable
interface Notifier { interface Notifier {
/** /**
* Invoke this to indicate to the impl that your visual has moved to a different set of sections. * Invoke this to indicate to the impl that your visual has moved to a different set of sections.

View file

@ -0,0 +1,24 @@
package dev.engine_room.flywheel.api.visual;
import org.jetbrains.annotations.ApiStatus;
import it.unimi.dsi.fastutil.longs.LongSet;
public interface SmoothLitVisual extends Visual {
/**
* Set the section property object.
*
* <p>This method is only called once, upon visual creation,
*
* @param property The property.
*/
void setSectionProperty(SectionProperty property);
@ApiStatus.NonExtendable
interface SectionProperty {
/**
* Invoke this to indicate to the impl that your visual has moved to a different set of sections.
*/
void lightSections(LongSet sections);
}
}

View file

@ -4,7 +4,6 @@ import org.joml.Matrix3fc;
import org.joml.Matrix4fc; import org.joml.Matrix4fc;
import dev.engine_room.flywheel.api.BackendImplemented; import dev.engine_room.flywheel.api.BackendImplemented;
import it.unimi.dsi.fastutil.longs.LongSet;
@BackendImplemented @BackendImplemented
public interface VisualEmbedding extends VisualizationContext { public interface VisualEmbedding extends VisualizationContext {
@ -16,8 +15,6 @@ public interface VisualEmbedding extends VisualizationContext {
*/ */
void transforms(Matrix4fc pose, Matrix3fc normal); void transforms(Matrix4fc pose, Matrix3fc normal);
void lightChunks(LongSet chunks);
/** /**
* Delete this embedding. * Delete this embedding.
* *

View file

@ -22,6 +22,7 @@ import dev.engine_room.flywheel.backend.gl.GlStateTracker;
import dev.engine_room.flywheel.lib.task.Flag; import dev.engine_room.flywheel.lib.task.Flag;
import dev.engine_room.flywheel.lib.task.NamedFlag; import dev.engine_room.flywheel.lib.task.NamedFlag;
import dev.engine_room.flywheel.lib.task.SyncedPlan; import dev.engine_room.flywheel.lib.task.SyncedPlan;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
@ -41,7 +42,7 @@ public class EngineImpl implements Engine {
this.drawManager = drawManager; this.drawManager = drawManager;
sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance; sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance;
environmentStorage = new EnvironmentStorage(); environmentStorage = new EnvironmentStorage();
lightStorage = new LightStorage(level, environmentStorage); lightStorage = new LightStorage(level);
} }
@Override @Override
@ -101,6 +102,11 @@ public class EngineImpl implements Engine {
lightStorage.delete(); lightStorage.delete();
} }
@Override
public void lightSections(LongSet sections) {
lightStorage.sections(sections);
}
public <I extends Instance> Instancer<I> instancer(Environment environment, InstanceType<I> type, Model model, RenderStage stage) { public <I extends Instance> Instancer<I> instancer(Environment environment, InstanceType<I> type, Model model, RenderStage stage) {
return drawManager.getInstancer(environment, type, model, stage); return drawManager.getInstancer(environment, type, model, stage);
} }

View file

@ -1,8 +1,6 @@
package dev.engine_room.flywheel.backend.engine; package dev.engine_room.flywheel.backend.engine;
import dev.engine_room.flywheel.backend.engine.embed.AbstractEmbeddedEnvironment; import dev.engine_room.flywheel.backend.engine.embed.AbstractEmbeddedEnvironment;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet;
import it.unimi.dsi.fastutil.objects.ReferenceSet; import it.unimi.dsi.fastutil.objects.ReferenceSet;
import it.unimi.dsi.fastutil.objects.ReferenceSets; import it.unimi.dsi.fastutil.objects.ReferenceSets;
@ -18,10 +16,4 @@ public class EnvironmentStorage {
environments.removeIf(AbstractEmbeddedEnvironment::isDeleted); environments.removeIf(AbstractEmbeddedEnvironment::isDeleted);
environments.forEach(AbstractEmbeddedEnvironment::flush); environments.forEach(AbstractEmbeddedEnvironment::flush);
} }
public LongSet allLightSections() {
var out = new LongOpenHashSet();
environments.forEach(e -> e.addLightSections(out));
return out;
}
} }

View file

@ -15,7 +15,6 @@ import dev.engine_room.flywheel.api.visualization.VisualEmbedding;
import dev.engine_room.flywheel.backend.compile.ContextShader; import dev.engine_room.flywheel.backend.compile.ContextShader;
import dev.engine_room.flywheel.backend.engine.EngineImpl; import dev.engine_room.flywheel.backend.engine.EngineImpl;
import dev.engine_room.flywheel.backend.gl.shader.GlProgram; import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
public abstract class AbstractEmbeddedEnvironment implements Environment, VisualEmbedding { public abstract class AbstractEmbeddedEnvironment implements Environment, VisualEmbedding {
@ -95,10 +94,6 @@ public abstract class AbstractEmbeddedEnvironment implements Environment, Visual
return deleted; return deleted;
} }
public void addLightSections(LongSet out) {
}
/** /**
* Called by visuals * Called by visuals
*/ */

View file

@ -2,11 +2,11 @@ package dev.engine_room.flywheel.backend.engine.embed;
import java.util.BitSet; import java.util.BitSet;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import dev.engine_room.flywheel.api.event.RenderContext; import dev.engine_room.flywheel.api.event.RenderContext;
import dev.engine_room.flywheel.api.task.Plan; import dev.engine_room.flywheel.api.task.Plan;
import dev.engine_room.flywheel.backend.engine.EnvironmentStorage;
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.lib.task.SimplePlan; import dev.engine_room.flywheel.lib.task.SimplePlan;
@ -40,7 +40,6 @@ public class LightStorage {
private static final int INVALID_SECTION = -1; private static final int INVALID_SECTION = -1;
private final LevelAccessor level; private final LevelAccessor level;
private final EnvironmentStorage environmentStorage;
private final Arena arena; private final Arena arena;
private final Long2IntMap section2ArenaIndex = new Long2IntOpenHashMap(); private final Long2IntMap section2ArenaIndex = new Long2IntOpenHashMap();
@ -51,18 +50,26 @@ public class LightStorage {
private final BitSet changed = new BitSet(); private final BitSet changed = new BitSet();
private boolean needsLutRebuild = false; private boolean needsLutRebuild = false;
public LightStorage(LevelAccessor level, EnvironmentStorage environmentStorage) { @Nullable
private LongSet requestedSections;
public LightStorage(LevelAccessor level) {
this.level = level; this.level = level;
this.environmentStorage = environmentStorage;
arena = new Arena(SECTION_SIZE_BYTES, DEFAULT_ARENA_CAPACITY_SECTIONS); arena = new Arena(SECTION_SIZE_BYTES, DEFAULT_ARENA_CAPACITY_SECTIONS);
} }
public void sections(LongSet sections) {
requestedSections = sections;
}
public Plan<RenderContext> createFramePlan() { public Plan<RenderContext> createFramePlan() {
return SimplePlan.of(() -> { return SimplePlan.of(() -> {
var allLightSections = environmentStorage.allLightSections(); if (requestedSections == null) {
return;
}
removeUnusedSections(allLightSections); removeUnusedSections(requestedSections);
var knownSections = section2ArenaIndex.keySet(); var knownSections = section2ArenaIndex.keySet();
@ -70,7 +77,7 @@ public class LightStorage {
.getUpdatedSections(); .getUpdatedSections();
// Only add the new sections. // Only add the new sections.
allLightSections.removeAll(knownSections); requestedSections.removeAll(knownSections);
for (long updatedSection : updatedSections) { for (long updatedSection : updatedSections) {
for (int x = -1; x <= 1; x++) { for (int x = -1; x <= 1; x++) {
@ -78,14 +85,14 @@ public class LightStorage {
for (int z = -1; z <= 1; z++) { for (int z = -1; z <= 1; z++) {
long section = SectionPos.offset(updatedSection, x, y, z); long section = SectionPos.offset(updatedSection, x, y, z);
if (knownSections.contains(section)) { if (knownSections.contains(section)) {
allLightSections.add(section); requestedSections.add(section);
} }
} }
} }
} }
} }
for (long section : allLightSections) { for (long section : requestedSections) {
addSection(section); addSection(section);
} }
}); });

View file

@ -5,7 +5,6 @@ import org.joml.Matrix4f;
import dev.engine_room.flywheel.api.event.RenderStage; import dev.engine_room.flywheel.api.event.RenderStage;
import dev.engine_room.flywheel.backend.engine.EngineImpl; import dev.engine_room.flywheel.backend.engine.EngineImpl;
import it.unimi.dsi.fastutil.longs.LongSet;
public class NestedEmbeddedEnvironment extends AbstractEmbeddedEnvironment { public class NestedEmbeddedEnvironment extends AbstractEmbeddedEnvironment {
private final AbstractEmbeddedEnvironment parent; private final AbstractEmbeddedEnvironment parent;
@ -15,11 +14,6 @@ public class NestedEmbeddedEnvironment extends AbstractEmbeddedEnvironment {
this.parent = parent; this.parent = parent;
} }
@Override
public void lightChunks(LongSet chunks) {
// noop
}
@Override @Override
public void composeMatrices(Matrix4f pose, Matrix3f normal) { public void composeMatrices(Matrix4f pose, Matrix3f normal) {
parent.composeMatrices(pose, normal); parent.composeMatrices(pose, normal);

View file

@ -5,27 +5,12 @@ import org.joml.Matrix4f;
import dev.engine_room.flywheel.api.event.RenderStage; import dev.engine_room.flywheel.api.event.RenderStage;
import dev.engine_room.flywheel.backend.engine.EngineImpl; import dev.engine_room.flywheel.backend.engine.EngineImpl;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongSet;
public class TopLevelEmbeddedEnvironment extends AbstractEmbeddedEnvironment { public class TopLevelEmbeddedEnvironment extends AbstractEmbeddedEnvironment {
private final LongSet lightSections = new LongArraySet();
public TopLevelEmbeddedEnvironment(EngineImpl engine, RenderStage renderStage) { public TopLevelEmbeddedEnvironment(EngineImpl engine, RenderStage renderStage) {
super(engine, renderStage); super(engine, renderStage);
} }
@Override
public void lightChunks(LongSet chunks) {
lightSections.clear();
lightSections.addAll(chunks);
}
@Override
public void addLightSections(LongSet out) {
out.addAll(lightSections);
}
@Override @Override
public void composeMatrices(Matrix4f pose, Matrix3f normal) { public void composeMatrices(Matrix4f pose, Matrix3f normal) {
pose.set(this.pose); pose.set(this.pose);

View file

@ -45,6 +45,7 @@ import dev.engine_room.flywheel.lib.task.RaisePlan;
import dev.engine_room.flywheel.lib.task.SimplePlan; import dev.engine_room.flywheel.lib.task.SimplePlan;
import dev.engine_room.flywheel.lib.util.LevelAttached; import dev.engine_room.flywheel.lib.util.LevelAttached;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
import net.minecraft.server.level.BlockDestructionProgress; import net.minecraft.server.level.BlockDestructionProgress;
@ -104,6 +105,14 @@ public class VisualizationManagerImpl implements VisualizationManager {
.ifTrue(recreate) .ifTrue(recreate)
.ifFalse(update) .ifFalse(update)
.plan() .plan()
.then(SimplePlan.of(() -> {
// TODO: Lazily re-evaluate the union'd set
var out = new LongOpenHashSet();
out.addAll(blockEntities.lightSections());
out.addAll(entities.lightSections());
out.addAll(effects.lightSections());
engine.lightSections(out);
}))
.then(RaisePlan.raise(frameVisualsFlag)) .then(RaisePlan.raise(frameVisualsFlag))
.then(engine.createFramePlan()) .then(engine.createFramePlan())
.then(RaisePlan.raise(frameFlag)); .then(RaisePlan.raise(frameFlag));

View file

@ -10,6 +10,7 @@ import dev.engine_room.flywheel.api.visualization.VisualManager;
import dev.engine_room.flywheel.impl.visualization.storage.Storage; import dev.engine_room.flywheel.impl.visualization.storage.Storage;
import dev.engine_room.flywheel.impl.visualization.storage.Transaction; import dev.engine_room.flywheel.impl.visualization.storage.Transaction;
import dev.engine_room.flywheel.lib.task.SimplePlan; import dev.engine_room.flywheel.lib.task.SimplePlan;
import it.unimi.dsi.fastutil.longs.LongSet;
public class VisualManagerImpl<T, S extends Storage<T>> implements VisualManager<T> { public class VisualManagerImpl<T, S extends Storage<T>> implements VisualManager<T> {
private final Queue<Transaction<T>> queue = new ConcurrentLinkedQueue<>(); private final Queue<Transaction<T>> queue = new ConcurrentLinkedQueue<>();
@ -74,4 +75,8 @@ public class VisualManagerImpl<T, S extends Storage<T>> implements VisualManager
return SimplePlan.<TickableVisual.Context>of(context -> processQueue(1)) return SimplePlan.<TickableVisual.Context>of(context -> processQueue(1))
.then(storage.tickPlan()); .then(storage.tickPlan());
} }
public LongSet lightSections() {
return getStorage().lightSections();
}
} }

View file

@ -0,0 +1,45 @@
package dev.engine_room.flywheel.impl.visualization.storage;
import java.util.Map;
import dev.engine_room.flywheel.api.visual.SmoothLitVisual;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
public class SmoothLitVisualStorage {
private final Map<SmoothLitVisual, SectionProperty> visuals = new Reference2ObjectOpenHashMap<>();
public LongSet sections() {
var out = new LongOpenHashSet();
for (SectionProperty value : visuals.values()) {
out.addAll(value.sections);
}
return out;
}
public void remove(SmoothLitVisual smoothLit) {
visuals.remove(smoothLit);
}
public void add(SmoothLitVisual smoothLit) {
var sections = new SectionProperty();
visuals.put(smoothLit, sections);
smoothLit.setSectionProperty(sections);
}
public void clear() {
visuals.clear();
}
private static final class SectionProperty implements SmoothLitVisual.SectionProperty {
private final LongSet sections = new LongArraySet();
@Override
public void lightSections(LongSet sections) {
this.sections.clear();
this.sections.addAll(sections);
}
}
}

View file

@ -11,6 +11,7 @@ import org.jetbrains.annotations.Nullable;
import dev.engine_room.flywheel.api.task.Plan; import dev.engine_room.flywheel.api.task.Plan;
import dev.engine_room.flywheel.api.visual.DynamicVisual; import dev.engine_room.flywheel.api.visual.DynamicVisual;
import dev.engine_room.flywheel.api.visual.LitVisual; import dev.engine_room.flywheel.api.visual.LitVisual;
import dev.engine_room.flywheel.api.visual.SmoothLitVisual;
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;
@ -19,6 +20,7 @@ import dev.engine_room.flywheel.lib.task.NestedPlan;
import dev.engine_room.flywheel.lib.task.PlanMap; import dev.engine_room.flywheel.lib.task.PlanMap;
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual; import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
import dev.engine_room.flywheel.lib.visual.SimpleTickableVisual; import dev.engine_room.flywheel.lib.visual.SimpleTickableVisual;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
public abstract class Storage<T> { public abstract class Storage<T> {
@ -28,6 +30,7 @@ public abstract class Storage<T> {
protected final List<SimpleDynamicVisual> simpleDynamicVisuals = new ArrayList<>(); protected final List<SimpleDynamicVisual> simpleDynamicVisuals = new ArrayList<>();
protected final List<SimpleTickableVisual> simpleTickableVisuals = new ArrayList<>(); protected final List<SimpleTickableVisual> simpleTickableVisuals = new ArrayList<>();
protected final LitVisualStorage litVisuals = new LitVisualStorage(); protected final LitVisualStorage litVisuals = new LitVisualStorage();
protected final SmoothLitVisualStorage smoothLitVisuals = new SmoothLitVisualStorage();
private final Map<T, Visual> visuals = new Reference2ObjectOpenHashMap<>(); private final Map<T, Visual> visuals = new Reference2ObjectOpenHashMap<>();
@ -71,6 +74,9 @@ public abstract class Storage<T> {
if (visual instanceof LitVisual lit) { if (visual instanceof LitVisual lit) {
litVisuals.remove(lit); litVisuals.remove(lit);
} }
if (visual instanceof SmoothLitVisual smoothLit) {
smoothLitVisuals.remove(smoothLit);
}
visual.delete(); visual.delete();
} }
@ -90,6 +96,7 @@ public abstract class Storage<T> {
simpleTickableVisuals.clear(); simpleTickableVisuals.clear();
simpleDynamicVisuals.clear(); simpleDynamicVisuals.clear();
litVisuals.clear(); litVisuals.clear();
smoothLitVisuals.clear();
visuals.replaceAll((obj, visual) -> { visuals.replaceAll((obj, visual) -> {
visual.delete(); visual.delete();
@ -156,6 +163,10 @@ public abstract class Storage<T> {
if (visual instanceof LitVisual lit) { if (visual instanceof LitVisual lit) {
litVisuals.setNotifierAndAdd(lit); litVisuals.setNotifierAndAdd(lit);
} }
if (visual instanceof SmoothLitVisual smoothLit) {
smoothLitVisuals.add(smoothLit);
}
} }
/** /**
@ -164,4 +175,8 @@ public abstract class Storage<T> {
* @return true if the object is currently capable of being visualized. * @return true if the object is currently capable of being visualized.
*/ */
public abstract boolean willAccept(T obj); public abstract boolean willAccept(T obj);
public LongSet lightSections() {
return smoothLitVisuals.sections();
}
} }