mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-11-13 05:54:01 +01:00
Lit beyond bounds
- Add callback to LitVisual to allow LitVisuals to change sections - Move visual managers' tick/frame plan creation back into VisualManagerImpl. - Some small reorganization in LitVisualStorage.
This commit is contained in:
parent
e7c27109e5
commit
4698d7b394
@ -26,10 +26,33 @@ public interface LitVisual extends Visual {
|
|||||||
/**
|
/**
|
||||||
* Collect the sections that this visual is contained in.
|
* Collect the sections that this visual is contained in.
|
||||||
* <br>
|
* <br>
|
||||||
* This method is only called upon visual creation.
|
* 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.
|
* @param consumer The consumer to provide the sections to.
|
||||||
* @see SectionPos#asLong
|
* @see SectionPos#asLong
|
||||||
*/
|
*/
|
||||||
void collectLightSections(LongConsumer consumer);
|
void collectLightSections(LongConsumer consumer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the notifier object.
|
||||||
|
* <br>
|
||||||
|
* This method is only called once, upon visual creation,
|
||||||
|
* after {@link #init} and before {@link #collectLightSections}.
|
||||||
|
*
|
||||||
|
* @param notifier The notifier.
|
||||||
|
*/
|
||||||
|
void initLightSectionNotifier(Notifier notifier);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A notifier object that can be used to indicate to the impl
|
||||||
|
* that the sections a visual is contained in have changed.
|
||||||
|
*/
|
||||||
|
interface Notifier {
|
||||||
|
/**
|
||||||
|
* Invoke this to indicate to the impl that your visual has moved to a different set of sections.
|
||||||
|
* <br>
|
||||||
|
* The next frame, the impl will call {@link LitVisual#collectLightSections} again.
|
||||||
|
*/
|
||||||
|
void notifySectionsChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,20 +98,14 @@ public class VisualizationManagerImpl implements VisualizationManager {
|
|||||||
entities = new VisualManagerImpl<>(entitiesStorage);
|
entities = new VisualManagerImpl<>(entitiesStorage);
|
||||||
effects = new VisualManagerImpl<>(effectsStorage);
|
effects = new VisualManagerImpl<>(effectsStorage);
|
||||||
|
|
||||||
tickPlan = NestedPlan.of(SimplePlan.<VisualTickContext>of(context -> blockEntities.processQueue(0))
|
tickPlan = NestedPlan.of(blockEntities.tickPlan(), entities.tickPlan(), effects.tickPlan())
|
||||||
.then(blockEntitiesStorage.getTickPlan()), SimplePlan.<VisualTickContext>of(context -> entities.processQueue(0))
|
|
||||||
.then(entitiesStorage.getTickPlan()), SimplePlan.<VisualTickContext>of(context -> effects.processQueue(0))
|
|
||||||
.then(effectsStorage.getTickPlan()))
|
|
||||||
.then(RaisePlan.raise(tickFlag))
|
.then(RaisePlan.raise(tickFlag))
|
||||||
.simplify();
|
.simplify();
|
||||||
|
|
||||||
var recreate = SimplePlan.<RenderContext>of(context -> blockEntitiesStorage.recreateAll(context.partialTick()), context -> entitiesStorage.recreateAll(context.partialTick()), context -> effectsStorage.recreateAll(context.partialTick()));
|
var recreate = SimplePlan.<RenderContext>of(context -> blockEntitiesStorage.recreateAll(context.partialTick()), context -> entitiesStorage.recreateAll(context.partialTick()), context -> effectsStorage.recreateAll(context.partialTick()));
|
||||||
|
|
||||||
var update = MapContextPlan.map(this::createVisualFrameContext)
|
var update = MapContextPlan.map(this::createVisualFrameContext)
|
||||||
.to(NestedPlan.of(SimplePlan.<VisualFrameContext>of(context -> blockEntities.processQueue(0))
|
.to(NestedPlan.of(blockEntities.framePlan(), entities.framePlan(), effects.framePlan()));
|
||||||
.then(blockEntitiesStorage.getFramePlan()), SimplePlan.<VisualFrameContext>of(context -> entities.processQueue(0))
|
|
||||||
.then(entitiesStorage.getFramePlan()), SimplePlan.<VisualFrameContext>of(context -> effects.processQueue(0))
|
|
||||||
.then(effectsStorage.getFramePlan())));
|
|
||||||
|
|
||||||
framePlan = IfElsePlan.on((RenderContext ctx) -> engine.updateRenderOrigin(ctx.camera()))
|
framePlan = IfElsePlan.on((RenderContext ctx) -> engine.updateRenderOrigin(ctx.camera()))
|
||||||
.ifTrue(recreate)
|
.ifTrue(recreate)
|
||||||
|
@ -3,9 +3,13 @@ package com.jozufozu.flywheel.impl.visualization.manager;
|
|||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.task.Plan;
|
||||||
|
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
||||||
|
import com.jozufozu.flywheel.api.visual.VisualTickContext;
|
||||||
import com.jozufozu.flywheel.api.visualization.VisualManager;
|
import com.jozufozu.flywheel.api.visualization.VisualManager;
|
||||||
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
||||||
import com.jozufozu.flywheel.impl.visualization.storage.Transaction;
|
import com.jozufozu.flywheel.impl.visualization.storage.Transaction;
|
||||||
|
import com.jozufozu.flywheel.lib.task.SimplePlan;
|
||||||
|
|
||||||
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<>();
|
||||||
@ -60,4 +64,13 @@ public class VisualManagerImpl<T, S extends Storage<T>> implements VisualManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Plan<VisualFrameContext> framePlan() {
|
||||||
|
return SimplePlan.<VisualFrameContext>of(context -> processQueue(context.partialTick()))
|
||||||
|
.then(storage.getFramePlan());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Plan<VisualTickContext> tickPlan() {
|
||||||
|
return SimplePlan.<VisualTickContext>of(context -> processQueue(0))
|
||||||
|
.then(storage.getTickPlan());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,13 @@ package com.jozufozu.flywheel.impl.visualization.storage;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Queue;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.task.Plan;
|
import com.jozufozu.flywheel.api.task.Plan;
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.api.visual.LitVisual;
|
import com.jozufozu.flywheel.api.visual.LitVisual;
|
||||||
@ -30,25 +34,28 @@ public class LitVisualStorage {
|
|||||||
private final Map<LitVisual, LongSet> visuals2Sections = new WeakHashMap<>();
|
private final Map<LitVisual, LongSet> visuals2Sections = new WeakHashMap<>();
|
||||||
private final Long2ObjectMap<List<Updater>> sections2Visuals = new Long2ObjectOpenHashMap<>();
|
private final Long2ObjectMap<List<Updater>> sections2Visuals = new Long2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
private final LongSet sectionsQueue = new LongOpenHashSet();
|
private final Queue<LitVisual> movedVisuals = new ConcurrentLinkedQueue<>();
|
||||||
|
private final LongSet sectionsUpdatedThisFrame = new LongOpenHashSet();
|
||||||
|
|
||||||
private long updateId = INITIAL_UPDATE_ID;
|
private long updateId = INITIAL_UPDATE_ID;
|
||||||
|
|
||||||
public Plan<VisualFrameContext> plan() {
|
public Plan<VisualFrameContext> plan() {
|
||||||
return (SimplyComposedPlan<VisualFrameContext>) (TaskExecutor taskExecutor, VisualFrameContext context, Runnable onCompletion) -> {
|
return (SimplyComposedPlan<VisualFrameContext>) (TaskExecutor taskExecutor, VisualFrameContext context, Runnable onCompletion) -> {
|
||||||
if (sectionsQueue.isEmpty()) {
|
processMoved();
|
||||||
|
|
||||||
|
if (sectionsUpdatedThisFrame.isEmpty()) {
|
||||||
onCompletion.run();
|
onCompletion.run();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sync = new Synchronizer(sectionsQueue.size(), () -> {
|
var sync = new Synchronizer(sectionsUpdatedThisFrame.size(), () -> {
|
||||||
sectionsQueue.clear();
|
sectionsUpdatedThisFrame.clear();
|
||||||
onCompletion.run();
|
onCompletion.run();
|
||||||
});
|
});
|
||||||
|
|
||||||
long updateId = getNextUpdateId();
|
long updateId = getNextUpdateId();
|
||||||
|
|
||||||
for (long section : sectionsQueue) {
|
for (long section : sectionsUpdatedThisFrame) {
|
||||||
var visuals = sections2Visuals.get(section);
|
var visuals = sections2Visuals.get(section);
|
||||||
if (visuals != null && !visuals.isEmpty()) {
|
if (visuals != null && !visuals.isEmpty()) {
|
||||||
taskExecutor.execute(() -> PlanUtil.distribute(taskExecutor, updateId, sync, visuals, Updater::updateLight));
|
taskExecutor.execute(() -> PlanUtil.distribute(taskExecutor, updateId, sync, visuals, Updater::updateLight));
|
||||||
@ -59,6 +66,16 @@ public class LitVisualStorage {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processMoved() {
|
||||||
|
LitVisual visual;
|
||||||
|
while ((visual = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private long getNextUpdateId() {
|
private long getNextUpdateId() {
|
||||||
long out = this.updateId;
|
long out = this.updateId;
|
||||||
|
|
||||||
@ -75,37 +92,47 @@ public class LitVisualStorage {
|
|||||||
return visuals2Sections.isEmpty();
|
return visuals2Sections.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(LitVisual visual) {
|
public void addAndInitNotifier(LitVisual visual) {
|
||||||
|
visual.initLightSectionNotifier(new LitVisualNotifierImpl(visual));
|
||||||
|
add(visual);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add(LitVisual visual) {
|
||||||
LongSet sections = new LongArraySet();
|
LongSet sections = new LongArraySet();
|
||||||
|
|
||||||
visual.collectLightSections(sections::add);
|
visual.collectLightSections(sections::add);
|
||||||
|
|
||||||
Updater updater;
|
// 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()) {
|
if (sections.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
} else if (sections.size() == 1) {
|
|
||||||
updater = new Updater.Simple(visual);
|
|
||||||
} else {
|
|
||||||
updater = new Updater.Synced(visual, new AtomicLong(NEVER_UPDATED));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var updater = createUpdater(visual, sections.size());
|
||||||
|
|
||||||
for (long section : sections) {
|
for (long section : sections) {
|
||||||
sections2Visuals.computeIfAbsent(section, $ -> new ObjectArrayList<>())
|
sections2Visuals.computeIfAbsent(section, $ -> new ObjectArrayList<>())
|
||||||
.add(updater);
|
.add(updater);
|
||||||
}
|
}
|
||||||
|
|
||||||
visuals2Sections.put(visual, sections);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enqueueLightUpdateSections(LongSet sections) {
|
public void enqueueLightUpdateSections(LongSet sections) {
|
||||||
sectionsQueue.addAll(sections);
|
sectionsUpdatedThisFrame.addAll(sections);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(LitVisual visual) {
|
/**
|
||||||
|
* Remove the visual from this storage.
|
||||||
|
*
|
||||||
|
* @param visual The visual to remove.
|
||||||
|
* @return {@code true} if the visual was removed, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
public boolean remove(LitVisual visual) {
|
||||||
var sections = visuals2Sections.remove(visual);
|
var sections = visuals2Sections.remove(visual);
|
||||||
|
|
||||||
if (sections == null) {
|
if (sections == null) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (long section : sections) {
|
for (long section : sections) {
|
||||||
@ -114,12 +141,14 @@ public class LitVisualStorage {
|
|||||||
listeners.remove(indexOfUpdater(listeners, visual));
|
listeners.remove(indexOfUpdater(listeners, visual));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
visuals2Sections.clear();
|
visuals2Sections.clear();
|
||||||
sections2Visuals.clear();
|
sections2Visuals.clear();
|
||||||
sectionsQueue.clear();
|
sectionsUpdatedThisFrame.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int indexOfUpdater(List<Updater> listeners, LitVisual visual) {
|
private static int indexOfUpdater(List<Updater> listeners, LitVisual visual) {
|
||||||
@ -132,6 +161,15 @@ public class LitVisualStorage {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static Updater createUpdater(LitVisual visual, int sectionCount) {
|
||||||
|
if (sectionCount == 1) {
|
||||||
|
return new Updater.Simple(visual);
|
||||||
|
} else {
|
||||||
|
return new Updater.Synced(visual, new AtomicLong(NEVER_UPDATED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Breaking this into 2 separate cases allows us to avoid the sync overhead in the common case.
|
// Breaking this into 2 separate cases allows us to avoid the sync overhead in the common case.
|
||||||
// TODO: is it faster to only use the synced variant to avoid virtual dispatches?
|
// TODO: is it faster to only use the synced variant to avoid virtual dispatches?
|
||||||
sealed interface Updater {
|
sealed interface Updater {
|
||||||
@ -160,4 +198,17 @@ 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,7 @@ public abstract class Storage<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (visual instanceof LitVisual lit) {
|
if (visual instanceof LitVisual lit) {
|
||||||
litVisuals.add(lit);
|
litVisuals.addAndInitNotifier(lit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package com.jozufozu.flywheel.lib.visual;
|
|||||||
|
|
||||||
import java.util.function.LongConsumer;
|
import java.util.function.LongConsumer;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.joml.FrustumIntersection;
|
import org.joml.FrustumIntersection;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.visual.BlockEntityVisual;
|
import com.jozufozu.flywheel.api.visual.BlockEntityVisual;
|
||||||
@ -40,6 +41,8 @@ public abstract class AbstractBlockEntityVisual<T extends BlockEntity> extends A
|
|||||||
protected final BlockPos pos;
|
protected final BlockPos pos;
|
||||||
protected final BlockPos visualPos;
|
protected final BlockPos visualPos;
|
||||||
protected final BlockState blockState;
|
protected final BlockState blockState;
|
||||||
|
@Nullable
|
||||||
|
protected LitVisual.Notifier notifier;
|
||||||
|
|
||||||
public AbstractBlockEntityVisual(VisualizationContext ctx, T blockEntity) {
|
public AbstractBlockEntityVisual(VisualizationContext ctx, T blockEntity) {
|
||||||
super(ctx, blockEntity.getLevel());
|
super(ctx, blockEntity.getLevel());
|
||||||
@ -59,6 +62,11 @@ public abstract class AbstractBlockEntityVisual<T extends BlockEntity> extends A
|
|||||||
consumer.accept(SectionPos.asLong(pos));
|
consumer.accept(SectionPos.asLong(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initLightSectionNotifier(Notifier notifier) {
|
||||||
|
this.notifier = notifier;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldReset() {
|
public boolean shouldReset() {
|
||||||
return blockEntity.getBlockState() != blockState;
|
return blockEntity.getBlockState() != blockState;
|
||||||
|
Loading…
Reference in New Issue
Block a user