Merge branch '1.20.1/dev' into 1.21.1/dev

This commit is contained in:
Jozufozu 2025-02-28 22:05:28 -08:00
commit f4123add3a
8 changed files with 112 additions and 111 deletions

View file

@ -15,7 +15,6 @@ import dev.engine_room.flywheel.api.task.Plan;
import dev.engine_room.flywheel.api.visualization.VisualEmbedding; import dev.engine_room.flywheel.api.visualization.VisualEmbedding;
import dev.engine_room.flywheel.api.visualization.VisualizationContext; import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.backend.FlwBackend; import dev.engine_room.flywheel.backend.FlwBackend;
import dev.engine_room.flywheel.backend.compile.core.ShaderException;
import dev.engine_room.flywheel.backend.engine.embed.EmbeddedEnvironment; import dev.engine_room.flywheel.backend.engine.embed.EmbeddedEnvironment;
import dev.engine_room.flywheel.backend.engine.embed.Environment; import dev.engine_room.flywheel.backend.engine.embed.Environment;
import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage; import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage;
@ -96,7 +95,7 @@ public class EngineImpl implements Engine {
Uniforms.update(context); Uniforms.update(context);
environmentStorage.flush(); environmentStorage.flush();
drawManager.render(lightStorage, environmentStorage); drawManager.render(lightStorage, environmentStorage);
} catch (ShaderException e) { } catch (Exception e) {
FlwBackend.LOGGER.error("Falling back", e); FlwBackend.LOGGER.error("Falling back", e);
triggerFallback(); triggerFallback();
} }
@ -106,7 +105,7 @@ public class EngineImpl implements Engine {
public void renderCrumbling(RenderContext context, List<CrumblingBlock> crumblingBlocks) { public void renderCrumbling(RenderContext context, List<CrumblingBlock> crumblingBlocks) {
try (var state = GlStateTracker.getRestoreState()) { try (var state = GlStateTracker.getRestoreState()) {
drawManager.renderCrumbling(crumblingBlocks); drawManager.renderCrumbling(crumblingBlocks);
} catch (ShaderException e) { } catch (Exception e) {
FlwBackend.LOGGER.error("Falling back", e); FlwBackend.LOGGER.error("Falling back", e);
triggerFallback(); triggerFallback();
} }

View file

@ -7,6 +7,7 @@ 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.TickableVisual; import dev.engine_room.flywheel.api.visual.TickableVisual;
import dev.engine_room.flywheel.api.visualization.VisualManager; import dev.engine_room.flywheel.api.visualization.VisualManager;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
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;
@ -54,21 +55,25 @@ public class VisualManagerImpl<T, S extends Storage<T>> implements VisualManager
queue.add(Transaction.update(obj)); queue.add(Transaction.update(obj));
} }
public void processQueue(float partialTick) { public void processQueue(VisualizationContext visualizationContext, float partialTick) {
var storage = getStorage(); var storage = getStorage();
Transaction<T> transaction; Transaction<T> transaction;
while ((transaction = queue.poll()) != null) { while ((transaction = queue.poll()) != null) {
transaction.apply(storage, partialTick); switch (transaction.action()) {
case ADD -> storage.add(visualizationContext, transaction.obj(), partialTick);
case REMOVE -> storage.remove(transaction.obj());
case UPDATE -> storage.update(transaction.obj(), partialTick);
}
} }
} }
public Plan<DynamicVisual.Context> framePlan() { public Plan<DynamicVisual.Context> framePlan(VisualizationContext visualizationContext) {
return SimplePlan.<DynamicVisual.Context>of(context -> processQueue(context.partialTick())) return SimplePlan.<DynamicVisual.Context>of(context -> processQueue(visualizationContext, context.partialTick()))
.then(storage.framePlan()); .then(storage.framePlan());
} }
public Plan<TickableVisual.Context> tickPlan() { public Plan<TickableVisual.Context> tickPlan(VisualizationContext visualizationContext) {
return SimplePlan.<TickableVisual.Context>of(context -> processQueue(1)) return SimplePlan.<TickableVisual.Context>of(context -> processQueue(visualizationContext, 1))
.then(storage.tickPlan()); .then(storage.tickPlan());
} }

View file

@ -59,9 +59,15 @@ public class VisualizationManagerImpl implements VisualizationManager {
private static final LevelAttached<VisualizationManagerImpl> MANAGERS = new LevelAttached<>(VisualizationManagerImpl::new, VisualizationManagerImpl::delete); private static final LevelAttached<VisualizationManagerImpl> MANAGERS = new LevelAttached<>(VisualizationManagerImpl::new, VisualizationManagerImpl::delete);
private final TaskExecutorImpl taskExecutor; private final TaskExecutorImpl taskExecutor;
private final Engine engine;
private final DistanceUpdateLimiterImpl frameLimiter; private final DistanceUpdateLimiterImpl frameLimiter;
private final RenderDispatcherImpl renderDispatcher = new RenderDispatcherImpl(); private final RenderDispatcherImpl renderDispatcher = new RenderDispatcherImpl();
private final LevelAccessor level;
// VisualizationManagerImpl can (and should!) be constructed off of the main thread, but it may be
// difficult for engines to avoid OpenGL calls which would not be safe. Shove all the init logic
// that depends on engine construction into here, and defer until we get invoked on the main thread.
@Nullable
private LateInit lateInit;
private final VisualManagerImpl<BlockEntity, BlockEntityStorage> blockEntities; private final VisualManagerImpl<BlockEntity, BlockEntityStorage> blockEntities;
private final VisualManagerImpl<Entity, EntityStorage> entities; private final VisualManagerImpl<Entity, EntityStorage> entities;
@ -70,49 +76,14 @@ public class VisualizationManagerImpl implements VisualizationManager {
private final Flag frameFlag = new Flag("frame"); private final Flag frameFlag = new Flag("frame");
private final Flag tickFlag = new Flag("tick"); private final Flag tickFlag = new Flag("tick");
private final Plan<RenderContext> framePlan;
private final Plan<TickableVisual.Context> tickPlan;
private VisualizationManagerImpl(LevelAccessor level) { private VisualizationManagerImpl(LevelAccessor level) {
this.level = level;
taskExecutor = FlwTaskExecutor.get(); taskExecutor = FlwTaskExecutor.get();
engine = BackendManager.currentBackend()
.createEngine(level);
frameLimiter = createUpdateLimiter(); frameLimiter = createUpdateLimiter();
var visualizationContext = engine.createVisualizationContext(); blockEntities = new VisualManagerImpl<>(new BlockEntityStorage());
var blockEntitiesStorage = new BlockEntityStorage(visualizationContext); entities = new VisualManagerImpl<>(new EntityStorage());
var entitiesStorage = new EntityStorage(visualizationContext); effects = new VisualManagerImpl<>(new EffectStorage());
var effectsStorage = new EffectStorage(visualizationContext);
blockEntities = new VisualManagerImpl<>(blockEntitiesStorage);
entities = new VisualManagerImpl<>(entitiesStorage);
effects = new VisualManagerImpl<>(effectsStorage);
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(blockEntities.framePlan(), entities.framePlan(), effects.framePlan()));
framePlan = IfElsePlan.on((RenderContext ctx) -> engine.updateRenderOrigin(ctx.camera()))
.ifTrue(recreate)
.ifFalse(update)
.plan()
.then(SimplePlan.of(() -> {
if (blockEntities.areGpuLightSectionsDirty() || entities.areGpuLightSectionsDirty() || effects.areGpuLightSectionsDirty()) {
var out = new LongOpenHashSet();
out.addAll(blockEntities.gpuLightSections());
out.addAll(entities.gpuLightSections());
out.addAll(effects.gpuLightSections());
engine.lightSections(out);
}
}))
.then(engine.createFramePlan())
.then(RaisePlan.raise(frameFlag));
tickPlan = NestedPlan.of(blockEntities.tickPlan(), entities.tickPlan(), effects.tickPlan())
.then(RaisePlan.raise(tickFlag));
if (level instanceof Level l) { if (level instanceof Level l) {
LevelExtension.getAllLoadedEntities(l) LevelExtension.getAllLoadedEntities(l)
@ -120,16 +91,65 @@ public class VisualizationManagerImpl implements VisualizationManager {
} }
} }
private DynamicVisual.Context createVisualFrameContext(RenderContext ctx) { private class LateInit {
Vec3i renderOrigin = engine.renderOrigin(); private final Engine engine;
var cameraPos = ctx.camera()
.getPosition();
Matrix4f viewProjection = new Matrix4f(ctx.viewProjection()); private final Plan<RenderContext> framePlan;
viewProjection.translate((float) (renderOrigin.getX() - cameraPos.x), (float) (renderOrigin.getY() - cameraPos.y), (float) (renderOrigin.getZ() - cameraPos.z)); private final Plan<TickableVisual.Context> tickPlan;
FrustumIntersection frustum = new FrustumIntersection(viewProjection);
return new DynamicVisualContextImpl(ctx.camera(), frustum, ctx.partialTick(), frameLimiter); private LateInit(LevelAccessor level) {
engine = BackendManager.currentBackend()
.createEngine(level);
var visualizationContext = engine.createVisualizationContext();
var recreate = SimplePlan.<RenderContext>of(context -> blockEntities.getStorage()
.recreateAll(visualizationContext, context.partialTick()), context -> entities.getStorage()
.recreateAll(visualizationContext, context.partialTick()), context -> effects.getStorage()
.recreateAll(visualizationContext, context.partialTick()));
var update = MapContextPlan.map(this::createVisualFrameContext)
.to(NestedPlan.of(blockEntities.framePlan(visualizationContext), entities.framePlan(visualizationContext), effects.framePlan(visualizationContext)));
framePlan = IfElsePlan.on((RenderContext ctx) -> engine.updateRenderOrigin(ctx.camera()))
.ifTrue(recreate)
.ifFalse(update)
.plan()
.then(SimplePlan.of(() -> {
if (blockEntities.areGpuLightSectionsDirty() || entities.areGpuLightSectionsDirty() || effects.areGpuLightSectionsDirty()) {
var out = new LongOpenHashSet();
out.addAll(blockEntities.gpuLightSections());
out.addAll(entities.gpuLightSections());
out.addAll(effects.gpuLightSections());
engine.lightSections(out);
}
}))
.then(engine.createFramePlan())
.then(RaisePlan.raise(frameFlag));
tickPlan = NestedPlan.of(blockEntities.tickPlan(visualizationContext), entities.tickPlan(visualizationContext), effects.tickPlan(visualizationContext))
.then(RaisePlan.raise(tickFlag));
}
private DynamicVisual.Context createVisualFrameContext(RenderContext ctx) {
Vec3i renderOrigin = engine.renderOrigin();
var cameraPos = ctx.camera()
.getPosition();
Matrix4f viewProjection = new Matrix4f(ctx.viewProjection());
viewProjection.translate((float) (renderOrigin.getX() - cameraPos.x), (float) (renderOrigin.getY() - cameraPos.y), (float) (renderOrigin.getZ() - cameraPos.z));
FrustumIntersection frustum = new FrustumIntersection(viewProjection);
return new DynamicVisualContextImpl(ctx.camera(), frustum, ctx.partialTick(), frameLimiter);
}
}
private LateInit lateInit() {
if (lateInit == null) {
lateInit = new LateInit(level);
}
return lateInit;
} }
private DistanceUpdateLimiterImpl createUpdateLimiter() { private DistanceUpdateLimiterImpl createUpdateLimiter() {
@ -191,7 +211,11 @@ public class VisualizationManagerImpl implements VisualizationManager {
@Override @Override
public Vec3i renderOrigin() { public Vec3i renderOrigin() {
return engine.renderOrigin(); if (lateInit == null) {
return Vec3i.ZERO;
} else {
return lateInit.engine.renderOrigin();
}
} }
@Override @Override
@ -225,7 +249,7 @@ public class VisualizationManagerImpl implements VisualizationManager {
taskExecutor.syncUntil(tickFlag::isRaised); taskExecutor.syncUntil(tickFlag::isRaised);
tickFlag.lower(); tickFlag.lower();
tickPlan.execute(taskExecutor, TickableVisualContextImpl.INSTANCE); lateInit().tickPlan.execute(taskExecutor, TickableVisualContextImpl.INSTANCE);
} }
/** /**
@ -240,7 +264,7 @@ public class VisualizationManagerImpl implements VisualizationManager {
frameLimiter.tick(); frameLimiter.tick();
framePlan.execute(taskExecutor, context); lateInit().framePlan.execute(taskExecutor, context);
} }
/** /**
@ -248,7 +272,7 @@ public class VisualizationManagerImpl implements VisualizationManager {
*/ */
private void render(RenderContext context) { private void render(RenderContext context) {
taskExecutor.syncUntil(frameFlag::isRaised); taskExecutor.syncUntil(frameFlag::isRaised);
engine.render(context); lateInit().engine.render(context);
} }
private void renderCrumbling(RenderContext context, Long2ObjectMap<SortedSet<BlockDestructionProgress>> destructionProgress) { private void renderCrumbling(RenderContext context, Long2ObjectMap<SortedSet<BlockDestructionProgress>> destructionProgress) {
@ -292,12 +316,12 @@ public class VisualizationManagerImpl implements VisualizationManager {
} }
if (!crumblingBlocks.isEmpty()) { if (!crumblingBlocks.isEmpty()) {
engine.renderCrumbling(context, crumblingBlocks); lateInit().engine.renderCrumbling(context, crumblingBlocks);
} }
} }
public void onLightUpdate(SectionPos sectionPos, LightLayer layer) { public void onLightUpdate(SectionPos sectionPos, LightLayer layer) {
engine.onLightUpdate(sectionPos, layer); lateInit().engine.onLightUpdate(sectionPos, layer);
long longPos = sectionPos.asLong(); long longPos = sectionPos.asLong();
blockEntities.onLightUpdate(longPos); blockEntities.onLightUpdate(longPos);
entities.onLightUpdate(longPos); entities.onLightUpdate(longPos);
@ -315,7 +339,9 @@ public class VisualizationManagerImpl implements VisualizationManager {
blockEntities.invalidate(); blockEntities.invalidate();
entities.invalidate(); entities.invalidate();
effects.invalidate(); effects.invalidate();
engine.delete(); if (lateInit != null) {
lateInit.engine.delete();
}
} }
private class RenderDispatcherImpl implements RenderDispatcher { private class RenderDispatcherImpl implements RenderDispatcher {
@ -335,6 +361,7 @@ public class VisualizationManagerImpl implements VisualizationManager {
} }
} }
private record CrumblingBlockImpl(BlockPos pos, int progress, List<Instance> instances) implements Engine.CrumblingBlock { private record CrumblingBlockImpl(BlockPos pos, int progress,
List<Instance> instances) implements dev.engine_room.flywheel.api.backend.Engine.CrumblingBlock {
} }
} }

View file

@ -15,10 +15,6 @@ import net.minecraft.world.level.block.entity.BlockEntity;
public class BlockEntityStorage extends Storage<BlockEntity> { public class BlockEntityStorage extends Storage<BlockEntity> {
private final Long2ObjectMap<BlockEntityVisual<?>> posLookup = new Long2ObjectOpenHashMap<>(); private final Long2ObjectMap<BlockEntityVisual<?>> posLookup = new Long2ObjectOpenHashMap<>();
public BlockEntityStorage(VisualizationContext visualizationContext) {
super(visualizationContext);
}
@Nullable @Nullable
public BlockEntityVisual<?> visualAtPos(long pos) { public BlockEntityVisual<?> visualAtPos(long pos) {
return posLookup.get(pos); return posLookup.get(pos);
@ -50,7 +46,7 @@ public class BlockEntityStorage extends Storage<BlockEntity> {
@Override @Override
@Nullable @Nullable
protected BlockEntityVisual<?> createRaw(BlockEntity obj, float partialTick) { protected BlockEntityVisual<?> createRaw(VisualizationContext visualizationContext, BlockEntity obj, float partialTick) {
var visualizer = VisualizationHelper.getVisualizer(obj); var visualizer = VisualizationHelper.getVisualizer(obj);
if (visualizer == null) { if (visualizer == null) {
return null; return null;
@ -72,9 +68,9 @@ public class BlockEntityStorage extends Storage<BlockEntity> {
} }
@Override @Override
public void recreateAll(float partialTick) { public void recreateAll(VisualizationContext visualizationContext, float partialTick) {
posLookup.clear(); posLookup.clear();
super.recreateAll(partialTick); super.recreateAll(visualizationContext, partialTick);
} }
@Override @Override

View file

@ -5,12 +5,8 @@ import dev.engine_room.flywheel.api.visual.EffectVisual;
import dev.engine_room.flywheel.api.visualization.VisualizationContext; import dev.engine_room.flywheel.api.visualization.VisualizationContext;
public class EffectStorage extends Storage<Effect> { public class EffectStorage extends Storage<Effect> {
public EffectStorage(VisualizationContext visualizationContext) {
super(visualizationContext);
}
@Override @Override
protected EffectVisual<?> createRaw(Effect obj, float partialTick) { protected EffectVisual<?> createRaw(VisualizationContext visualizationContext, Effect obj, float partialTick) {
return obj.visualize(visualizationContext, partialTick); return obj.visualize(visualizationContext, partialTick);
} }

View file

@ -7,18 +7,14 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
public class EntityStorage extends Storage<Entity> { public class EntityStorage extends Storage<Entity> {
public EntityStorage(VisualizationContext visualizationContext) {
super(visualizationContext);
}
@Override @Override
protected EntityVisual<?> createRaw(Entity obj, float partialTick) { protected EntityVisual<?> createRaw(VisualizationContext context, Entity obj, float partialTick) {
var visualizer = VisualizationHelper.getVisualizer(obj); var visualizer = VisualizationHelper.getVisualizer(obj);
if (visualizer == null) { if (visualizer == null) {
return null; return null;
} }
return visualizer.createVisual(visualizationContext, obj, partialTick); return visualizer.createVisual(context, obj, partialTick);
} }
@Override @Override

View file

@ -25,8 +25,6 @@ import dev.engine_room.flywheel.lib.visual.SimpleTickableVisual;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
public abstract class Storage<T> { public abstract class Storage<T> {
protected final VisualizationContext visualizationContext;
private final Map<T, Visual> visuals = new Reference2ObjectOpenHashMap<>(); private final Map<T, Visual> visuals = new Reference2ObjectOpenHashMap<>();
protected final PlanMap<DynamicVisual, DynamicVisual.Context> dynamicVisuals = new PlanMap<>(); protected final PlanMap<DynamicVisual, DynamicVisual.Context> dynamicVisuals = new PlanMap<>();
protected final PlanMap<TickableVisual, TickableVisual.Context> tickableVisuals = new PlanMap<>(); protected final PlanMap<TickableVisual, TickableVisual.Context> tickableVisuals = new PlanMap<>();
@ -35,10 +33,6 @@ public abstract class Storage<T> {
protected final LightUpdatedVisualStorage lightUpdatedVisuals = new LightUpdatedVisualStorage(); protected final LightUpdatedVisualStorage lightUpdatedVisuals = new LightUpdatedVisualStorage();
protected final ShaderLightVisualStorage shaderLightVisuals = new ShaderLightVisualStorage(); protected final ShaderLightVisualStorage shaderLightVisuals = new ShaderLightVisualStorage();
public Storage(VisualizationContext visualizationContext) {
this.visualizationContext = visualizationContext;
}
public Collection<Visual> getAllVisuals() { public Collection<Visual> getAllVisuals() {
return visuals.values(); return visuals.values();
} }
@ -71,11 +65,16 @@ public abstract class Storage<T> {
*/ */
public abstract boolean willAccept(T obj); public abstract boolean willAccept(T obj);
public void add(T obj, float partialTick) { public void add(VisualizationContext visualizationContext, T obj, float partialTick) {
Visual visual = visuals.get(obj); Visual visual = visuals.get(obj);
if (visual == null) { if (visual == null) {
create(obj, partialTick); visual = createRaw(visualizationContext, obj, partialTick);
if (visual != null) {
setup(visual, partialTick);
visuals.put(obj, visual);
}
} }
} }
@ -120,7 +119,7 @@ public abstract class Storage<T> {
visual.update(partialTick); visual.update(partialTick);
} }
public void recreateAll(float partialTick) { public void recreateAll(VisualizationContext visualizationContext, float partialTick) {
dynamicVisuals.clear(); dynamicVisuals.clear();
tickableVisuals.clear(); tickableVisuals.clear();
simpleDynamicVisuals.clear(); simpleDynamicVisuals.clear();
@ -131,7 +130,7 @@ public abstract class Storage<T> {
visuals.replaceAll((obj, visual) -> { visuals.replaceAll((obj, visual) -> {
visual.delete(); visual.delete();
var out = createRaw(obj, partialTick); var out = createRaw(visualizationContext, obj, partialTick);
if (out != null) { if (out != null) {
setup(out, partialTick); setup(out, partialTick);
@ -141,17 +140,8 @@ public abstract class Storage<T> {
}); });
} }
private void create(T obj, float partialTick) {
var visual = createRaw(obj, partialTick);
if (visual != null) {
setup(visual, partialTick);
visuals.put(obj, visual);
}
}
@Nullable @Nullable
protected abstract Visual createRaw(T obj, float partialTick); protected abstract Visual createRaw(VisualizationContext visualizationContext, T obj, float partialTick);
private void setup(Visual visual, float partialTick) { private void setup(Visual visual, float partialTick) {
if (visual instanceof DynamicVisual dynamic) { if (visual instanceof DynamicVisual dynamic) {

View file

@ -12,12 +12,4 @@ public record Transaction<T>(T obj, Action action) {
public static <T> Transaction<T> update(T obj) { public static <T> Transaction<T> update(T obj) {
return new Transaction<>(obj, Action.UPDATE); return new Transaction<>(obj, Action.UPDATE);
} }
public void apply(Storage<T> storage, float partialTick) {
switch (action) {
case ADD -> storage.add(obj, partialTick);
case REMOVE -> storage.remove(obj);
case UPDATE -> storage.update(obj, partialTick);
}
}
} }