mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-11-10 12:34:11 +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.
|
||||
* <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.
|
||||
* @see SectionPos#asLong
|
||||
*/
|
||||
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);
|
||||
effects = new VisualManagerImpl<>(effectsStorage);
|
||||
|
||||
tickPlan = NestedPlan.of(SimplePlan.<VisualTickContext>of(context -> blockEntities.processQueue(0))
|
||||
.then(blockEntitiesStorage.getTickPlan()), SimplePlan.<VisualTickContext>of(context -> entities.processQueue(0))
|
||||
.then(entitiesStorage.getTickPlan()), SimplePlan.<VisualTickContext>of(context -> effects.processQueue(0))
|
||||
.then(effectsStorage.getTickPlan()))
|
||||
tickPlan = NestedPlan.of(blockEntities.tickPlan(), entities.tickPlan(), effects.tickPlan())
|
||||
.then(RaisePlan.raise(tickFlag))
|
||||
.simplify();
|
||||
|
||||
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)
|
||||
.to(NestedPlan.of(SimplePlan.<VisualFrameContext>of(context -> blockEntities.processQueue(0))
|
||||
.then(blockEntitiesStorage.getFramePlan()), SimplePlan.<VisualFrameContext>of(context -> entities.processQueue(0))
|
||||
.then(entitiesStorage.getFramePlan()), SimplePlan.<VisualFrameContext>of(context -> effects.processQueue(0))
|
||||
.then(effectsStorage.getFramePlan())));
|
||||
.to(NestedPlan.of(blockEntities.framePlan(), entities.framePlan(), effects.framePlan()));
|
||||
|
||||
framePlan = IfElsePlan.on((RenderContext ctx) -> engine.updateRenderOrigin(ctx.camera()))
|
||||
.ifTrue(recreate)
|
||||
|
@ -3,9 +3,13 @@ package com.jozufozu.flywheel.impl.visualization.manager;
|
||||
import java.util.Queue;
|
||||
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.impl.visualization.storage.Storage;
|
||||
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> {
|
||||
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.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.jozufozu.flywheel.api.task.Plan;
|
||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||
import com.jozufozu.flywheel.api.visual.LitVisual;
|
||||
@ -30,25 +34,28 @@ public class LitVisualStorage {
|
||||
private final Map<LitVisual, LongSet> visuals2Sections = new WeakHashMap<>();
|
||||
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;
|
||||
|
||||
public Plan<VisualFrameContext> plan() {
|
||||
return (SimplyComposedPlan<VisualFrameContext>) (TaskExecutor taskExecutor, VisualFrameContext context, Runnable onCompletion) -> {
|
||||
if (sectionsQueue.isEmpty()) {
|
||||
processMoved();
|
||||
|
||||
if (sectionsUpdatedThisFrame.isEmpty()) {
|
||||
onCompletion.run();
|
||||
return;
|
||||
}
|
||||
|
||||
var sync = new Synchronizer(sectionsQueue.size(), () -> {
|
||||
sectionsQueue.clear();
|
||||
var sync = new Synchronizer(sectionsUpdatedThisFrame.size(), () -> {
|
||||
sectionsUpdatedThisFrame.clear();
|
||||
onCompletion.run();
|
||||
});
|
||||
|
||||
long updateId = getNextUpdateId();
|
||||
|
||||
for (long section : sectionsQueue) {
|
||||
for (long section : sectionsUpdatedThisFrame) {
|
||||
var visuals = sections2Visuals.get(section);
|
||||
if (visuals != null && !visuals.isEmpty()) {
|
||||
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() {
|
||||
long out = this.updateId;
|
||||
|
||||
@ -75,37 +92,47 @@ public class LitVisualStorage {
|
||||
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();
|
||||
|
||||
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()) {
|
||||
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) {
|
||||
sections2Visuals.computeIfAbsent(section, $ -> new ObjectArrayList<>())
|
||||
.add(updater);
|
||||
}
|
||||
|
||||
visuals2Sections.put(visual, 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);
|
||||
|
||||
if (sections == null) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (long section : sections) {
|
||||
@ -114,12 +141,14 @@ public class LitVisualStorage {
|
||||
listeners.remove(indexOfUpdater(listeners, visual));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
visuals2Sections.clear();
|
||||
sections2Visuals.clear();
|
||||
sectionsQueue.clear();
|
||||
sectionsUpdatedThisFrame.clear();
|
||||
}
|
||||
|
||||
private static int indexOfUpdater(List<Updater> listeners, LitVisual visual) {
|
||||
@ -132,6 +161,15 @@ public class LitVisualStorage {
|
||||
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.
|
||||
// TODO: is it faster to only use the synced variant to avoid virtual dispatches?
|
||||
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) {
|
||||
litVisuals.add(lit);
|
||||
litVisuals.addAndInitNotifier(lit);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package com.jozufozu.flywheel.lib.visual;
|
||||
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.FrustumIntersection;
|
||||
|
||||
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 visualPos;
|
||||
protected final BlockState blockState;
|
||||
@Nullable
|
||||
protected LitVisual.Notifier notifier;
|
||||
|
||||
public AbstractBlockEntityVisual(VisualizationContext ctx, T blockEntity) {
|
||||
super(ctx, blockEntity.getLevel());
|
||||
@ -59,6 +62,11 @@ public abstract class AbstractBlockEntityVisual<T extends BlockEntity> extends A
|
||||
consumer.accept(SectionPos.asLong(pos));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initLightSectionNotifier(Notifier notifier) {
|
||||
this.notifier = notifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldReset() {
|
||||
return blockEntity.getBlockState() != blockState;
|
||||
|
Loading…
Reference in New Issue
Block a user