From caa02f2666e5c9aeb7b1e9532e50c0beb949043e Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sun, 14 Jul 2024 16:25:39 -0700 Subject: [PATCH] Lighter tracking - Deduplicate section tracking logic between Lit and SmoothLit - Now there is one SectionPropertyImpl which the 2 storages add listeners to --- .../flywheel/api/visual/LitVisual.java | 45 +--------------- .../api/visual/SectionTrackedVisual.java | 26 ++++++++++ .../flywheel/api/visual/SmoothLitVisual.java | 23 +-------- .../lib/visual/AbstractBlockEntityVisual.java | 15 ++---- .../storage/LitVisualStorage.java | 51 ++++++++----------- .../storage/SectionPropertyImpl.java | 26 ++++++++++ .../storage/SmoothLitVisualStorage.java | 32 +++++------- .../impl/visualization/storage/Storage.java | 18 +++++-- 8 files changed, 110 insertions(+), 126 deletions(-) create mode 100644 common/src/api/java/dev/engine_room/flywheel/api/visual/SectionTrackedVisual.java create mode 100644 common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/SectionPropertyImpl.java diff --git a/common/src/api/java/dev/engine_room/flywheel/api/visual/LitVisual.java b/common/src/api/java/dev/engine_room/flywheel/api/visual/LitVisual.java index 9891962ea..bd01158cd 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/visual/LitVisual.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/visual/LitVisual.java @@ -1,40 +1,13 @@ package dev.engine_room.flywheel.api.visual; -import java.util.function.LongConsumer; - -import org.jetbrains.annotations.ApiStatus; - -import net.minecraft.core.SectionPos; - /** * A visual that listens to light updates. * *

If your visual moves around in the level at all, you should use {@link TickableVisual} or {@link DynamicVisual}, * and poll for light yourself along with listening for updates. When your visual moves to a different section, call - * {@link Notifier#notifySectionsChanged}.

+ * {@link SectionProperty#lightSections}.

*/ -public interface LitVisual extends Visual { - /** - * Set the notifier object. - * - *

This method is only called once right after the visual - * is created and before {@link #collectLightSections}.

- * - * @param notifier The notifier. - */ - void setLightSectionNotifier(Notifier notifier); - - /** - * Collect the sections that this visual is contained in. - * - *

This method is called upon visual creation, and the frame after - * {@link Notifier#notifySectionsChanged} is called.

- * - * @param consumer The consumer to provide the sections to. - * @see SectionPos#asLong - */ - void collectLightSections(LongConsumer consumer); - +public non-sealed interface LitVisual extends SectionTrackedVisual { /** * Called when a section this visual is contained in receives a light update. * @@ -48,18 +21,4 @@ public interface LitVisual extends Visual { *

This method not is invoked automatically after visual creation.

*/ void updateLight(float partialTick); - - /** - * A notifier object that can be used to indicate to the impl - * that the sections a visual is contained in have changed. - */ - @ApiStatus.NonExtendable - interface Notifier { - /** - * Invoke this to indicate to the impl that your visual has moved to a different set of sections. - *
- * The next frame, the impl will call {@link LitVisual#collectLightSections} again. - */ - void notifySectionsChanged(); - } } diff --git a/common/src/api/java/dev/engine_room/flywheel/api/visual/SectionTrackedVisual.java b/common/src/api/java/dev/engine_room/flywheel/api/visual/SectionTrackedVisual.java new file mode 100644 index 000000000..92bcca70d --- /dev/null +++ b/common/src/api/java/dev/engine_room/flywheel/api/visual/SectionTrackedVisual.java @@ -0,0 +1,26 @@ +package dev.engine_room.flywheel.api.visual; + +import org.jetbrains.annotations.ApiStatus; + +import it.unimi.dsi.fastutil.longs.LongSet; + +public sealed interface SectionTrackedVisual extends Visual permits SmoothLitVisual, LitVisual { + /** + * Set the section property object. + * + *

This method is only called once, upon visual creation. + *
If the property is assigned to in this method, the + * visual will immediately be tracked in the given sections. + * + * @param property The property. + */ + void setSectionProperty(SectionProperty property); + + @ApiStatus.NonExtendable + interface SectionProperty { + /** + * Assign the set of sections this visual wants to track itself in. + */ + void lightSections(LongSet sections); + } +} diff --git a/common/src/api/java/dev/engine_room/flywheel/api/visual/SmoothLitVisual.java b/common/src/api/java/dev/engine_room/flywheel/api/visual/SmoothLitVisual.java index c22d6dfdf..23b09d197 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/visual/SmoothLitVisual.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/visual/SmoothLitVisual.java @@ -1,11 +1,7 @@ package dev.engine_room.flywheel.api.visual; -import org.jetbrains.annotations.ApiStatus; - -import it.unimi.dsi.fastutil.longs.LongSet; - /** - * An interface allowing visuals to request light data on the GPU for a set of sections. + * A marker interface allowing visuals to request light data on the GPU for a set of sections. * *

Sections passed into {@link SectionProperty#lightSections} will have their light data handed to the * backend and queryable by {@code flw_light*} functions in shaders. @@ -13,21 +9,6 @@ import it.unimi.dsi.fastutil.longs.LongSet; * Note that the queryable light data is shared across all visuals, so even if one specific visual does not * request a given section, the data will be available if another visual does. */ -public interface SmoothLitVisual extends Visual { - /** - * Set the section property object. - * - *

This method is only called once, upon visual creation. - * - * @param property The property. - */ - void setSectionProperty(SectionProperty property); +public non-sealed interface SmoothLitVisual extends SectionTrackedVisual { - @ApiStatus.NonExtendable - interface SectionProperty { - /** - * Assign the set of sections this visual wants to have light data for. - */ - void lightSections(LongSet sections); - } } diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/visual/AbstractBlockEntityVisual.java b/common/src/lib/java/dev/engine_room/flywheel/lib/visual/AbstractBlockEntityVisual.java index 59722198e..1aab4baa6 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/visual/AbstractBlockEntityVisual.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/visual/AbstractBlockEntityVisual.java @@ -1,7 +1,5 @@ package dev.engine_room.flywheel.lib.visual; -import java.util.function.LongConsumer; - import org.jetbrains.annotations.Nullable; import org.joml.FrustumIntersection; @@ -13,6 +11,7 @@ import dev.engine_room.flywheel.api.visualization.VisualManager; import dev.engine_room.flywheel.api.visualization.VisualizationContext; import dev.engine_room.flywheel.lib.instance.FlatLit; import dev.engine_room.flywheel.lib.math.MoreMath; +import it.unimi.dsi.fastutil.longs.LongSet; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; @@ -40,7 +39,7 @@ public abstract class AbstractBlockEntityVisual extends A protected final BlockPos visualPos; protected final BlockState blockState; @Nullable - protected LitVisual.Notifier notifier; + protected SectionProperty lightSections; public AbstractBlockEntityVisual(VisualizationContext ctx, T blockEntity, float partialTick) { super(ctx, blockEntity.getLevel(), partialTick); @@ -51,13 +50,9 @@ public abstract class AbstractBlockEntityVisual extends A } @Override - public void setLightSectionNotifier(Notifier notifier) { - this.notifier = notifier; - } - - @Override - public void collectLightSections(LongConsumer consumer) { - consumer.accept(SectionPos.asLong(pos)); + public void setSectionProperty(SectionProperty property) { + this.lightSections = property; + lightSections.lightSections(LongSet.of(SectionPos.asLong(pos))); } /** diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/LitVisualStorage.java b/common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/LitVisualStorage.java index 3651fe278..c7cf7b916 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/LitVisualStorage.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/LitVisualStorage.java @@ -31,7 +31,7 @@ public class LitVisualStorage { private final Map visuals2Sections = new WeakHashMap<>(); private final Long2ObjectMap> sections2Visuals = new Long2ObjectOpenHashMap<>(); - private final Queue movedVisuals = new ConcurrentLinkedQueue<>(); + private final Queue movedVisuals = new ConcurrentLinkedQueue<>(); private final LongSet sectionsUpdatedThisFrame = new LongOpenHashSet(); private long updateId = INITIAL_UPDATE_ID; @@ -65,11 +65,11 @@ public class LitVisualStorage { } private void processMoved() { - LitVisual visual; - while ((visual = movedVisuals.poll()) != null) { + MovedVisual moved; + while ((moved = movedVisuals.poll()) != null) { // If the visual isn't there when we try to remove it that means it was deleted before we got to it. - if (remove(visual)) { - add(visual); + if (remove(moved.visual)) { + updateTracking(moved.tracker, moved.visual); } } } @@ -90,24 +90,27 @@ public class LitVisualStorage { return visuals2Sections.isEmpty(); } - public void setNotifierAndAdd(LitVisual visual) { - visual.setLightSectionNotifier(new LitVisualNotifierImpl(visual)); - add(visual); + public void add(SectionPropertyImpl tracker, LitVisual visual) { + var moved = new MovedVisual(tracker, visual); + tracker.addListener(() -> movedVisuals.add(moved)); + + updateTracking(tracker, visual); } - private void add(LitVisual visual) { - LongSet sections = new LongArraySet(); + public void updateTracking(SectionPropertyImpl tracker, LitVisual visual) { + if (tracker.sections.isEmpty()) { + // Add the visual to the map even if sections is empty, this way we can distinguish from deleted visuals + visuals2Sections.put(visual, LongSet.of()); - visual.collectLightSections(sections::add); - - // Add the visual to the map even if sections is empty, this way we can distinguish from deleted visuals - visuals2Sections.put(visual, sections); - - // Don't bother creating an updater if the visual isn't in any sections. - if (sections.isEmpty()) { + // Don't bother creating an updater if the visual isn't in any sections. return; } + // Create a copy of the array, so we know what section to remove the visual from later. + var sections = new LongArraySet(tracker.sections); + + visuals2Sections.put(visual, sections); + var updater = createUpdater(visual, sections.size()); for (long section : sections) { @@ -198,16 +201,6 @@ public class LitVisualStorage { } } - private final class LitVisualNotifierImpl implements LitVisual.Notifier { - private final LitVisual litVisual; - - private LitVisualNotifierImpl(LitVisual litVisual) { - this.litVisual = litVisual; - } - - @Override - public void notifySectionsChanged() { - movedVisuals.add(litVisual); - } - } + private record MovedVisual(SectionPropertyImpl tracker, LitVisual visual) { + } } diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/SectionPropertyImpl.java b/common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/SectionPropertyImpl.java new file mode 100644 index 000000000..88c5bf0d7 --- /dev/null +++ b/common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/SectionPropertyImpl.java @@ -0,0 +1,26 @@ +package dev.engine_room.flywheel.impl.visualization.storage; + +import java.util.ArrayList; +import java.util.List; + +import dev.engine_room.flywheel.api.visual.SmoothLitVisual; +import it.unimi.dsi.fastutil.longs.LongArraySet; +import it.unimi.dsi.fastutil.longs.LongSet; + +public class SectionPropertyImpl implements SmoothLitVisual.SectionProperty { + public final LongSet sections = new LongArraySet(); + + private final List listeners = new ArrayList<>(2); + + @Override + public void lightSections(LongSet sections) { + this.sections.clear(); + this.sections.addAll(sections); + + listeners.forEach(Runnable::run); + } + + public void addListener(Runnable listener) { + listeners.add(listener); + } +} diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/SmoothLitVisualStorage.java b/common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/SmoothLitVisualStorage.java index 09c66b833..dc6b0fbac 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/SmoothLitVisualStorage.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/SmoothLitVisualStorage.java @@ -5,13 +5,12 @@ import java.util.Map; import org.jetbrains.annotations.Nullable; 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 visuals = new Reference2ObjectOpenHashMap<>(); + private final Map visuals = new Reference2ObjectOpenHashMap<>(); @Nullable private LongSet cachedSections; @@ -20,9 +19,13 @@ public class SmoothLitVisualStorage { return cachedSections == null; } + public void markDirty() { + cachedSections = null; + } + public LongSet sections() { cachedSections = new LongOpenHashSet(); - for (SectionProperty value : visuals.values()) { + for (var value : visuals.values()) { cachedSections.addAll(value.sections); } return cachedSections; @@ -32,25 +35,18 @@ public class SmoothLitVisualStorage { visuals.remove(smoothLit); } - public void add(SmoothLitVisual smoothLit) { - var sections = new SectionProperty(); - visuals.put(smoothLit, sections); - smoothLit.setSectionProperty(sections); + public void add(SectionPropertyImpl tracker, SmoothLitVisual smoothLit) { + visuals.put(smoothLit, tracker); + + tracker.addListener(this::markDirty); + + if (!tracker.sections.isEmpty()) { + markDirty(); + } } public void clear() { visuals.clear(); } - private 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); - - SmoothLitVisualStorage.this.cachedSections = null; - } - } } diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/Storage.java b/common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/Storage.java index 84505dcb7..3f8fab082 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/Storage.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/visualization/storage/Storage.java @@ -11,6 +11,7 @@ import org.jetbrains.annotations.Nullable; import dev.engine_room.flywheel.api.task.Plan; import dev.engine_room.flywheel.api.visual.DynamicVisual; import dev.engine_room.flywheel.api.visual.LitVisual; +import dev.engine_room.flywheel.api.visual.SectionTrackedVisual; import dev.engine_room.flywheel.api.visual.SmoothLitVisual; import dev.engine_room.flywheel.api.visual.TickableVisual; import dev.engine_room.flywheel.api.visual.Visual; @@ -159,12 +160,19 @@ public abstract class Storage { } } - if (visual instanceof LitVisual lit) { - litVisuals.setNotifierAndAdd(lit); - } + if (visual instanceof SectionTrackedVisual tracked) { + SectionPropertyImpl sectionProperty = new SectionPropertyImpl(); - if (visual instanceof SmoothLitVisual smoothLit) { - smoothLitVisuals.add(smoothLit); + // Give the visual a chance to fill in the property. + tracked.setSectionProperty(sectionProperty); + + if (visual instanceof LitVisual lit) { + litVisuals.add(sectionProperty, lit); + } + + if (visual instanceof SmoothLitVisual smoothLit) { + smoothLitVisuals.add(sectionProperty, smoothLit); + } } }