From e4df896710e3d2b72e33e2beb821ec0f003956d3 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Sun, 3 Mar 2024 14:53:07 -0800 Subject: [PATCH] Protect the environment - Environment/Embedding ownership is complicated, reference counting to the rescue! - VisualEmbeddings are acquired on construction and deleted manually - Instancers acquire/delete their environments - EmbeddedEnvironment now has three (3) delete methods - Add EnvironmentStorage to create, track, update, and delete environments - Remove InstancingEngine/IndirectEngine and move shared logic to EngineImpl - There's probably more that can be done to make DrawManager cleaner now --- .../api/visualization/VisualEmbedding.java | 9 ++ .../jozufozu/flywheel/backend/Backends.java | 9 +- .../backend/compile/IndirectPrograms.java | 2 +- .../backend/compile/InstancingPrograms.java | 2 +- .../backend/engine/AbstractEngine.java | 85 ---------- .../backend/engine/AbstractInstancer.java | 6 + ...InstancerStorage.java => DrawManager.java} | 23 +-- .../flywheel/backend/engine/EngineImpl.java | 145 ++++++++++++++++++ .../backend/engine/EnvironmentStorage.java | 51 ++++++ .../backend/engine/InstancerProviderImpl.java | 2 +- .../flywheel/backend/engine/MeshPool.java | 2 +- .../engine/embed/EmbeddedEnvironment.java | 55 +++++-- .../backend/engine/embed/Environment.java | 4 +- .../engine/embed/GlobalEnvironment.java | 7 +- .../engine/indirect/IndirectCullingGroup.java | 4 +- .../engine/indirect/IndirectDrawManager.java | 31 ++-- .../engine/indirect/IndirectEngine.java | 77 ---------- .../engine/indirect/IndirectInstancer.java | 9 ++ .../instancing/InstancedDrawManager.java | 4 +- .../engine/instancing/InstancedInstancer.java | 2 + .../engine/instancing/InstancingEngine.java | 68 -------- .../backend/util/AtomicReferenceCounted.java | 4 +- .../backend/util/ReferenceCounted.java | 4 +- 23 files changed, 321 insertions(+), 284 deletions(-) delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/engine/AbstractEngine.java rename src/main/java/com/jozufozu/flywheel/backend/engine/{InstancerStorage.java => DrawManager.java} (78%) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/engine/EngineImpl.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/engine/EnvironmentStorage.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectEngine.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancingEngine.java diff --git a/src/main/java/com/jozufozu/flywheel/api/visualization/VisualEmbedding.java b/src/main/java/com/jozufozu/flywheel/api/visualization/VisualEmbedding.java index 6b84b53ba..daa4fc311 100644 --- a/src/main/java/com/jozufozu/flywheel/api/visualization/VisualEmbedding.java +++ b/src/main/java/com/jozufozu/flywheel/api/visualization/VisualEmbedding.java @@ -48,4 +48,13 @@ public interface VisualEmbedding extends VisualizationContext { * @param sizeZ The size of the box in the z direction. */ void invalidateLight(int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ); + + /** + * Delete this embedding. + * + *

After this method exits, the embedding will continue to function in the state it was in before + * this method was called. Once all child instancers are deleted, the resources owned by this embedding + * will be freed. Creating new instancers after calling this method will throw an error.

+ */ + void delete(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backends.java b/src/main/java/com/jozufozu/flywheel/backend/Backends.java index 132782e66..dc668de74 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backends.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backends.java @@ -4,8 +4,9 @@ import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.backend.Backend; import com.jozufozu.flywheel.backend.compile.IndirectPrograms; import com.jozufozu.flywheel.backend.compile.InstancingPrograms; -import com.jozufozu.flywheel.backend.engine.indirect.IndirectEngine; -import com.jozufozu.flywheel.backend.engine.instancing.InstancingEngine; +import com.jozufozu.flywheel.backend.engine.EngineImpl; +import com.jozufozu.flywheel.backend.engine.indirect.IndirectDrawManager; +import com.jozufozu.flywheel.backend.engine.instancing.InstancedDrawManager; import com.jozufozu.flywheel.backend.gl.GlCompat; import com.jozufozu.flywheel.lib.backend.SimpleBackend; import com.jozufozu.flywheel.lib.util.ShadersModHandler; @@ -15,7 +16,7 @@ public final class Backends { * Use GPU instancing to render everything. */ public static final Backend INSTANCING = SimpleBackend.builder() - .engineFactory(level -> new InstancingEngine(InstancingPrograms.get(), 256)) + .engineFactory(level -> new EngineImpl(new InstancedDrawManager(InstancingPrograms.get()), 256)) .supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.supportsInstancing() && InstancingPrograms.allLoaded()) .register(Flywheel.rl("instancing")); @@ -23,7 +24,7 @@ public final class Backends { * Use Compute shaders to cull instances. */ public static final Backend INDIRECT = SimpleBackend.builder() - .engineFactory(level -> new IndirectEngine(IndirectPrograms.get(), 256)) + .engineFactory(level -> new EngineImpl(new IndirectDrawManager(IndirectPrograms.get()), 256)) .fallback(() -> Backends.INSTANCING) .supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.supportsIndirect() && IndirectPrograms.allLoaded()) .register(Flywheel.rl("indirect")); diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java b/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java index 921c57e40..8dc46f6e9 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/IndirectPrograms.java @@ -138,7 +138,7 @@ public class IndirectPrograms extends AtomicReferenceCounted { } @Override - protected void delete() { + protected void _delete() { pipeline.values() .forEach(GlProgram::delete); culling.values() diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/InstancingPrograms.java b/src/main/java/com/jozufozu/flywheel/backend/compile/InstancingPrograms.java index ee4924656..5a8589a66 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/InstancingPrograms.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/InstancingPrograms.java @@ -66,7 +66,7 @@ public class InstancingPrograms extends AtomicReferenceCounted { } @Override - protected void delete() { + protected void _delete() { pipeline.values() .forEach(GlProgram::delete); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/AbstractEngine.java b/src/main/java/com/jozufozu/flywheel/backend/engine/AbstractEngine.java deleted file mode 100644 index 2d529517a..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/AbstractEngine.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.jozufozu.flywheel.backend.engine; - -import com.jozufozu.flywheel.api.backend.Engine; -import com.jozufozu.flywheel.api.event.RenderStage; -import com.jozufozu.flywheel.api.instance.Instance; -import com.jozufozu.flywheel.api.instance.InstanceType; -import com.jozufozu.flywheel.api.instance.Instancer; -import com.jozufozu.flywheel.api.instance.InstancerProvider; -import com.jozufozu.flywheel.api.model.Model; -import com.jozufozu.flywheel.api.visualization.VisualEmbedding; -import com.jozufozu.flywheel.api.visualization.VisualizationContext; -import com.jozufozu.flywheel.backend.engine.embed.EmbeddedEnvironment; -import com.jozufozu.flywheel.backend.engine.embed.Environment; - -import net.minecraft.client.Camera; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Vec3i; -import net.minecraft.world.phys.Vec3; - -public abstract class AbstractEngine implements Engine { - protected final int sqrMaxOriginDistance; - protected BlockPos renderOrigin = BlockPos.ZERO; - - public AbstractEngine(int maxOriginDistance) { - sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance; - } - - public Instancer instancer(Environment environment, InstanceType type, Model model, RenderStage stage) { - return getStorage().getInstancer(environment, type, model, stage); - } - - @Override - public VisualizationContext createVisualizationContext(RenderStage stage) { - return new VisualizationContextImpl(stage); - } - - @Override - public boolean updateRenderOrigin(Camera camera) { - Vec3 cameraPos = camera.getPosition(); - double dx = renderOrigin.getX() - cameraPos.x; - double dy = renderOrigin.getY() - cameraPos.y; - double dz = renderOrigin.getZ() - cameraPos.z; - double distanceSqr = dx * dx + dy * dy + dz * dz; - - if (distanceSqr <= sqrMaxOriginDistance) { - return false; - } - - renderOrigin = BlockPos.containing(cameraPos); - getStorage().onRenderOriginChanged(); - return true; - } - - @Override - public Vec3i renderOrigin() { - return renderOrigin; - } - - protected abstract InstancerStorage> getStorage(); - - private class VisualizationContextImpl implements VisualizationContext { - private final InstancerProviderImpl instancerProvider; - private final RenderStage stage; - - public VisualizationContextImpl(RenderStage stage) { - instancerProvider = new InstancerProviderImpl(AbstractEngine.this, stage); - this.stage = stage; - } - - @Override - public InstancerProvider instancerProvider() { - return instancerProvider; - } - - @Override - public Vec3i renderOrigin() { - return AbstractEngine.this.renderOrigin(); - } - - @Override - public VisualEmbedding createEmbedding() { - return new EmbeddedEnvironment(AbstractEngine.this, stage); - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/AbstractInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/engine/AbstractInstancer.java index 84becc5bd..95fece409 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/AbstractInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/AbstractInstancer.java @@ -25,6 +25,8 @@ public abstract class AbstractInstancer implements Instancer protected AbstractInstancer(InstanceType type, Environment environment) { this.type = type; this.environment = environment; + + environment.acquire(); } @Override @@ -159,6 +161,10 @@ public abstract class AbstractInstancer implements Instancer deleted.clear(); } + public void delete() { + environment.release(); + } + @Override public String toString() { return "AbstractInstancer[" + instanceCount() + ']'; diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/InstancerStorage.java b/src/main/java/com/jozufozu/flywheel/backend/engine/DrawManager.java similarity index 78% rename from src/main/java/com/jozufozu/flywheel/backend/engine/InstancerStorage.java rename to src/main/java/com/jozufozu/flywheel/backend/engine/DrawManager.java index a521aa49d..ae0969004 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/InstancerStorage.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/DrawManager.java @@ -6,6 +6,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import com.jozufozu.flywheel.Flywheel; +import com.jozufozu.flywheel.api.backend.Engine; import com.jozufozu.flywheel.api.event.RenderStage; import com.jozufozu.flywheel.api.instance.Instance; import com.jozufozu.flywheel.api.instance.InstanceType; @@ -13,17 +14,13 @@ import com.jozufozu.flywheel.api.instance.Instancer; import com.jozufozu.flywheel.api.model.Model; import com.jozufozu.flywheel.backend.engine.embed.Environment; -import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; -import it.unimi.dsi.fastutil.objects.ReferenceSet; - -public abstract class InstancerStorage> { +public abstract class DrawManager> { /** * A map of instancer keys to instancers. *
* This map is populated as instancers are requested and contains both initialized and uninitialized instancers. */ protected final Map, N> instancers = new ConcurrentHashMap<>(); - protected final ReferenceSet environments = new ReferenceLinkedOpenHashSet<>(); /** * A list of instancers that have not yet been initialized. *
@@ -37,12 +34,6 @@ public abstract class InstancerStorage> { } public void delete() { - // FIXME: The ownership of environments is a bit weird. Their resources are created and destroyed by the engine, - // but the engine doesn't own the things themselves. This makes it hard for the engine to know when to delete - // environments. For now, we just delete all environments when the engine is deleted, but this is not ideal. - environments.forEach(Environment::delete); - environments.clear(); - instancers.clear(); initializationQueue.clear(); } @@ -61,6 +52,10 @@ public abstract class InstancerStorage> { .forEach(AbstractInstancer::clear); } + public abstract void renderCrumbling(List crumblingBlocks); + + public abstract void renderStage(RenderStage stage); + protected abstract N create(InstancerKey type); protected abstract void initialize(InstancerKey key, N instancer); @@ -68,15 +63,13 @@ public abstract class InstancerStorage> { private N createAndDeferInit(InstancerKey key) { var out = create(key); - environments.add(key.environment()); - // Only queue the instancer for initialization if it has anything to render. - if (checkAndWarnEmptyModel(key.model())) { + if (checkAndWarnEmptyModel(key.model())) { // Thread safety: this method is called atomically from within computeIfAbsent, // so we don't need extra synchronization to protect the queue. initializationQueue.add(new UninitializedInstancer<>(key, out)); } - return out; + return out; } protected record UninitializedInstancer(InstancerKey key, N instancer) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/EngineImpl.java b/src/main/java/com/jozufozu/flywheel/backend/engine/EngineImpl.java new file mode 100644 index 000000000..6f1a5754f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/EngineImpl.java @@ -0,0 +1,145 @@ +package com.jozufozu.flywheel.backend.engine; + +import java.util.List; + +import com.jozufozu.flywheel.api.backend.Engine; +import com.jozufozu.flywheel.api.event.RenderContext; +import com.jozufozu.flywheel.api.event.RenderStage; +import com.jozufozu.flywheel.api.instance.Instance; +import com.jozufozu.flywheel.api.instance.InstanceType; +import com.jozufozu.flywheel.api.instance.Instancer; +import com.jozufozu.flywheel.api.instance.InstancerProvider; +import com.jozufozu.flywheel.api.model.Model; +import com.jozufozu.flywheel.api.task.Plan; +import com.jozufozu.flywheel.api.task.TaskExecutor; +import com.jozufozu.flywheel.api.visualization.VisualEmbedding; +import com.jozufozu.flywheel.api.visualization.VisualizationContext; +import com.jozufozu.flywheel.backend.engine.embed.EmbeddedEnvironment; +import com.jozufozu.flywheel.backend.engine.embed.Environment; +import com.jozufozu.flywheel.backend.engine.uniform.Uniforms; +import com.jozufozu.flywheel.backend.gl.GlStateTracker; +import com.jozufozu.flywheel.lib.task.Flag; +import com.jozufozu.flywheel.lib.task.NamedFlag; +import com.jozufozu.flywheel.lib.task.SyncedPlan; + +import net.minecraft.client.Camera; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; +import net.minecraft.world.phys.Vec3; + +public class EngineImpl implements Engine { + private final int sqrMaxOriginDistance; + private final DrawManager> drawManager; + private final EnvironmentStorage environmentStorage = new EnvironmentStorage(this); + private final Flag flushFlag = new NamedFlag("flushed"); + + private BlockPos renderOrigin = BlockPos.ZERO; + + public EngineImpl(DrawManager> drawManager, int maxOriginDistance) { + this.drawManager = drawManager; + sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance; + } + + @Override + public Plan createFramePlan() { + return SyncedPlan.of(this::flush); + } + + @Override + public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) { + executor.syncUntil(flushFlag::isRaised); + if (stage.isLast()) { + flushFlag.lower(); + } + + drawManager.renderStage(stage); + } + + @Override + public void renderCrumbling(TaskExecutor executor, RenderContext context, List crumblingBlocks) { + // Need to wait for flush before we can inspect instancer state. + executor.syncUntil(flushFlag::isRaised); + + drawManager.renderCrumbling(crumblingBlocks); + } + + @Override + public VisualizationContext createVisualizationContext(RenderStage stage) { + return new VisualizationContextImpl(stage); + } + + @Override + public boolean updateRenderOrigin(Camera camera) { + Vec3 cameraPos = camera.getPosition(); + double dx = renderOrigin.getX() - cameraPos.x; + double dy = renderOrigin.getY() - cameraPos.y; + double dz = renderOrigin.getZ() - cameraPos.z; + double distanceSqr = dx * dx + dy * dy + dz * dz; + + if (distanceSqr <= sqrMaxOriginDistance) { + return false; + } + + renderOrigin = BlockPos.containing(cameraPos); + drawManager.onRenderOriginChanged(); + return true; + } + + @Override + public Vec3i renderOrigin() { + return renderOrigin; + } + + @Override + public void delete() { + drawManager.delete(); + environmentStorage.delete(); + } + + public Instancer instancer(Environment environment, InstanceType type, Model model, RenderStage stage) { + return drawManager.getInstancer(environment, type, model, stage); + } + + private void flush(RenderContext ctx) { + try (var state = GlStateTracker.getRestoreState()) { + Uniforms.updateContext(ctx); + drawManager.flush(); + environmentStorage.flush(); + } + + flushFlag.raise(); + } + + public VisualEmbedding createEmbedding(RenderStage renderStage) { + return environmentStorage.create(renderStage); + } + + public void freeEmbedding(EmbeddedEnvironment embeddedEnvironment) { + environmentStorage.enqueueDeletion(embeddedEnvironment); + } + + private class VisualizationContextImpl implements VisualizationContext { + private final InstancerProviderImpl instancerProvider; + private final RenderStage stage; + + public VisualizationContextImpl(RenderStage stage) { + instancerProvider = new InstancerProviderImpl(EngineImpl.this, stage); + this.stage = stage; + } + + @Override + public InstancerProvider instancerProvider() { + return instancerProvider; + } + + @Override + public Vec3i renderOrigin() { + return EngineImpl.this.renderOrigin(); + } + + @Override + public VisualEmbedding createEmbedding() { + return EngineImpl.this.createEmbedding(stage); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/EnvironmentStorage.java b/src/main/java/com/jozufozu/flywheel/backend/engine/EnvironmentStorage.java new file mode 100644 index 000000000..9a0e72270 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/EnvironmentStorage.java @@ -0,0 +1,51 @@ +package com.jozufozu.flywheel.backend.engine; + +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +import com.jozufozu.flywheel.api.event.RenderStage; +import com.jozufozu.flywheel.api.visualization.VisualEmbedding; +import com.jozufozu.flywheel.backend.engine.embed.EmbeddedEnvironment; + +import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; +import it.unimi.dsi.fastutil.objects.ReferenceSet; +import it.unimi.dsi.fastutil.objects.ReferenceSets; + +public class EnvironmentStorage { + protected final ReferenceSet environments = ReferenceSets.synchronize(new ReferenceLinkedOpenHashSet<>()); + private final Queue forDeletion = new ConcurrentLinkedQueue<>(); + private final EngineImpl engine; + + public EnvironmentStorage(EngineImpl engine) { + this.engine = engine; + } + + public VisualEmbedding create(RenderStage stage) { + var out = new EmbeddedEnvironment(engine, stage); + + environments.add(out); + + return out; + } + + public void enqueueDeletion(EmbeddedEnvironment environment) { + environments.remove(environment); + + forDeletion.add(environment); + } + + public void flush() { + EmbeddedEnvironment env; + + while ((env = forDeletion.poll()) != null) { + env.actuallyDelete(); + } + + environments.forEach(EmbeddedEnvironment::flush); + } + + public void delete() { + environments.forEach(EmbeddedEnvironment::actuallyDelete); + environments.clear(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/InstancerProviderImpl.java b/src/main/java/com/jozufozu/flywheel/backend/engine/InstancerProviderImpl.java index d0aacd2a5..6c95568f4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/InstancerProviderImpl.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/InstancerProviderImpl.java @@ -8,7 +8,7 @@ import com.jozufozu.flywheel.api.instance.InstancerProvider; import com.jozufozu.flywheel.api.model.Model; import com.jozufozu.flywheel.backend.engine.embed.GlobalEnvironment; -public record InstancerProviderImpl(AbstractEngine engine, RenderStage renderStage) implements InstancerProvider { +public record InstancerProviderImpl(EngineImpl engine, RenderStage renderStage) implements InstancerProvider { @Override public Instancer instancer(InstanceType type, Model model) { return engine.instancer(GlobalEnvironment.INSTANCE, type, model, renderStage); diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/MeshPool.java b/src/main/java/com/jozufozu/flywheel/backend/engine/MeshPool.java index 9c0b9768b..e789c3b7b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/MeshPool.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/MeshPool.java @@ -189,7 +189,7 @@ public class MeshPool { } @Override - protected void delete() { + protected void _delete() { MeshPool.this.dirty = true; MeshPool.this.anyToRemove = true; } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/embed/EmbeddedEnvironment.java b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/EmbeddedEnvironment.java index cdcc68a3a..28050386c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/embed/EmbeddedEnvironment.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/EmbeddedEnvironment.java @@ -14,13 +14,14 @@ import com.jozufozu.flywheel.api.model.Model; import com.jozufozu.flywheel.api.visualization.VisualEmbedding; import com.jozufozu.flywheel.backend.Samplers; import com.jozufozu.flywheel.backend.compile.ContextShader; -import com.jozufozu.flywheel.backend.engine.AbstractEngine; +import com.jozufozu.flywheel.backend.engine.EngineImpl; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.backend.util.AtomicReferenceCounted; import net.minecraft.core.Vec3i; import net.minecraft.world.level.BlockAndTintGetter; -public class EmbeddedEnvironment implements Environment, VisualEmbedding { +public class EmbeddedEnvironment extends AtomicReferenceCounted implements Environment, VisualEmbedding { private final Matrix4f pose = new Matrix4f(); private final Matrix3f normal = new Matrix3f(); @@ -28,10 +29,10 @@ public class EmbeddedEnvironment implements Environment, VisualEmbedding { private final EmbeddedLightTexture lightTexture = new EmbeddedLightTexture(); private final InstancerProvider instancerProvider; - private final AbstractEngine engine; + private final EngineImpl engine; private final RenderStage renderStage; - public EmbeddedEnvironment(AbstractEngine engine, RenderStage renderStage) { + public EmbeddedEnvironment(EngineImpl engine, RenderStage renderStage) { this.engine = engine; this.renderStage = renderStage; @@ -42,6 +43,22 @@ public class EmbeddedEnvironment implements Environment, VisualEmbedding { return engine.instancer(EmbeddedEnvironment.this, type, model, renderStage); } }; + + // Acquire the reference owned by the visual that created this. + acquire(); + } + + public void flush() { + if (lightVolume.empty()) { + return; + } + Samplers.EMBEDDED_LIGHT.makeActive(); + + lightTexture.bind(); + + lightTexture.ensureCapacity(lightVolume.sizeX, lightVolume.sizeY, lightVolume.sizeZ); + + lightTexture.upload(lightVolume.ptr(), lightVolume.sizeX, lightVolume.sizeY, lightVolume.sizeZ); } @Override @@ -72,10 +89,6 @@ public class EmbeddedEnvironment implements Environment, VisualEmbedding { lightTexture.bind(); - lightTexture.ensureCapacity(lightVolume.sizeX, lightVolume.sizeY, lightVolume.sizeZ); - - lightTexture.upload(lightVolume.ptr(), lightVolume.sizeX, lightVolume.sizeY, lightVolume.sizeZ); - float oneOverSizeX = 1f / (float) lightTexture.sizeX; float oneOverSizeY = 1f / (float) lightTexture.sizeY; float oneOverSizeZ = 1f / (float) lightTexture.sizeZ; @@ -105,11 +118,35 @@ public class EmbeddedEnvironment implements Environment, VisualEmbedding { @Override public VisualEmbedding createEmbedding() { - return new EmbeddedEnvironment(engine, renderStage); + return engine.createEmbedding(renderStage); } + /** + * Called by visuals + */ @Override public void delete() { + // Release the reference owned by the visual that created this. + // Note that visuals don't explicitly call acquire, instead the + // storage acquired a reference when this was constructed. + release(); + } + + /** + * Called when referenceCount goes to 0 + */ + @Override + public void _delete() { + engine.freeEmbedding(this); + } + + /** + * Called in EnvironmentStorage#flush + */ + public void actuallyDelete() { + // We could technically free the light volume right away in _delete, but + // the control flow here is so convoluted that it's probably best to do + // everything in one place. lightVolume.delete(); lightTexture.delete(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/embed/Environment.java b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/Environment.java index e139ac234..d9db66cc3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/embed/Environment.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/Environment.java @@ -10,5 +10,7 @@ public interface Environment { void setupCull(GlProgram cullProgram); - void delete(); + void acquire(); + + void release(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/embed/GlobalEnvironment.java b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/GlobalEnvironment.java index ff29e474d..6d7fbc987 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/embed/GlobalEnvironment.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/embed/GlobalEnvironment.java @@ -25,7 +25,12 @@ public class GlobalEnvironment implements Environment { } @Override - public void delete() { + public void acquire() { + + } + + @Override + public void release() { } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java index f524a2219..d0ffde51e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java @@ -77,9 +77,7 @@ public class IndirectCullingGroup { if (instanceCount == 0) { iterator.remove(); - for (IndirectDraw draw : instancer.draws()) { - draw.delete(); - } + instancer.delete(); continue; } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDrawManager.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDrawManager.java index 5b760f93d..e97f99a96 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDrawManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDrawManager.java @@ -19,9 +19,9 @@ import com.jozufozu.flywheel.backend.Samplers; import com.jozufozu.flywheel.backend.compile.ContextShader; import com.jozufozu.flywheel.backend.compile.IndirectPrograms; import com.jozufozu.flywheel.backend.engine.CommonCrumbling; +import com.jozufozu.flywheel.backend.engine.DrawManager; import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl; import com.jozufozu.flywheel.backend.engine.InstancerKey; -import com.jozufozu.flywheel.backend.engine.InstancerStorage; import com.jozufozu.flywheel.backend.engine.MaterialRenderState; import com.jozufozu.flywheel.backend.engine.MeshPool; import com.jozufozu.flywheel.backend.engine.TextureBinder; @@ -39,7 +39,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minecraft.client.resources.model.ModelBakery; -public class IndirectDrawManager extends InstancerStorage> { +public class IndirectDrawManager extends DrawManager> { private final IndirectPrograms programs; private final StagingBuffer stagingBuffer; private final MeshPool meshPool; @@ -49,6 +49,7 @@ public class IndirectDrawManager extends InstancerStorage> public IndirectDrawManager(IndirectPrograms programs) { this.programs = programs; + programs.acquire(); stagingBuffer = new StagingBuffer(this.programs); meshPool = new MeshPool(); @@ -81,17 +82,23 @@ public class IndirectDrawManager extends InstancerStorage> } public void renderStage(RenderStage stage) { - TextureBinder.bindLightAndOverlay(); - - vertexArray.bindForDraw(); - Uniforms.bindForDraw(); - - for (var group : cullingGroups.values()) { - group.submit(stage); + if (!hasStage(stage)) { + return; } - MaterialRenderState.reset(); - TextureBinder.resetLightAndOverlay(); + try (var restoreState = GlStateTracker.getRestoreState()) { + TextureBinder.bindLightAndOverlay(); + + vertexArray.bindForDraw(); + Uniforms.bindForDraw(); + + for (var group : cullingGroups.values()) { + group.submit(stage); + } + + MaterialRenderState.reset(); + TextureBinder.resetLightAndOverlay(); + } } @Override @@ -136,6 +143,8 @@ public class IndirectDrawManager extends InstancerStorage> meshPool.delete(); crumblingDrawBuffer.delete(); + + programs.release(); } public void renderCrumbling(List crumblingBlocks) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectEngine.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectEngine.java deleted file mode 100644 index 22385b430..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectEngine.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.jozufozu.flywheel.backend.engine.indirect; - -import java.util.List; - -import com.jozufozu.flywheel.api.event.RenderContext; -import com.jozufozu.flywheel.api.event.RenderStage; -import com.jozufozu.flywheel.api.task.Plan; -import com.jozufozu.flywheel.api.task.TaskExecutor; -import com.jozufozu.flywheel.backend.compile.IndirectPrograms; -import com.jozufozu.flywheel.backend.engine.AbstractEngine; -import com.jozufozu.flywheel.backend.engine.AbstractInstancer; -import com.jozufozu.flywheel.backend.engine.InstancerStorage; -import com.jozufozu.flywheel.backend.engine.uniform.Uniforms; -import com.jozufozu.flywheel.backend.gl.GlStateTracker; -import com.jozufozu.flywheel.lib.task.Flag; -import com.jozufozu.flywheel.lib.task.NamedFlag; -import com.jozufozu.flywheel.lib.task.SyncedPlan; - -public class IndirectEngine extends AbstractEngine { - private final IndirectPrograms programs; - private final IndirectDrawManager drawManager; - private final Flag flushFlag = new NamedFlag("flushed"); - - public IndirectEngine(IndirectPrograms programs, int maxOriginDistance) { - super(maxOriginDistance); - programs.acquire(); - this.programs = programs; - drawManager = new IndirectDrawManager(this.programs); - } - - @Override - public Plan createFramePlan() { - return SyncedPlan.of(this::flushDrawManager); - } - - private void flushDrawManager(RenderContext ctx) { - try (var state = GlStateTracker.getRestoreState()) { - Uniforms.updateContext(ctx); - drawManager.flush(); - } - flushFlag.raise(); - } - - @Override - public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) { - executor.syncUntil(flushFlag::isRaised); - if (stage.isLast()) { - flushFlag.lower(); - } - - if (!drawManager.hasStage(stage)) { - return; - } - - try (var restoreState = GlStateTracker.getRestoreState()) { - drawManager.renderStage(stage); - } - } - - @Override - public void renderCrumbling(TaskExecutor executor, RenderContext context, List crumblingBlocks) { - executor.syncUntil(flushFlag::isRaised); - - drawManager.renderCrumbling(crumblingBlocks); - } - - @Override - protected InstancerStorage> getStorage() { - return drawManager; - } - - @Override - public void delete() { - drawManager.delete(); - programs.release(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectInstancer.java index f2e92530a..75ce1ed43 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectInstancer.java @@ -101,4 +101,13 @@ public class IndirectInstancer extends AbstractInstancer MemoryUtil.memPutInt(ptr, index); writer.write(ptr + IndirectBuffers.INT_SIZE, instance); } + + @Override + public void delete() { + super.delete(); + + for (IndirectDraw draw : draws()) { + draw.delete(); + } + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedDrawManager.java b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedDrawManager.java index a2e029fc0..44d7b9f35 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedDrawManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedDrawManager.java @@ -23,9 +23,9 @@ import com.jozufozu.flywheel.backend.ShaderIndices; import com.jozufozu.flywheel.backend.compile.ContextShader; import com.jozufozu.flywheel.backend.compile.InstancingPrograms; import com.jozufozu.flywheel.backend.engine.CommonCrumbling; +import com.jozufozu.flywheel.backend.engine.DrawManager; import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl; import com.jozufozu.flywheel.backend.engine.InstancerKey; -import com.jozufozu.flywheel.backend.engine.InstancerStorage; import com.jozufozu.flywheel.backend.engine.MaterialEncoder; import com.jozufozu.flywheel.backend.engine.MaterialRenderState; import com.jozufozu.flywheel.backend.engine.MeshPool; @@ -41,7 +41,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minecraft.client.resources.model.ModelBakery; -public class InstancedDrawManager extends InstancerStorage> { +public class InstancedDrawManager extends DrawManager> { /** * The set of draw calls to make in each {@link RenderStage}. */ diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedInstancer.java index 6ff46fe46..da7481f60 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedInstancer.java @@ -107,6 +107,8 @@ public class InstancedInstancer extends AbstractInstancer } public void delete() { + super.delete(); + if (vbo == null) { return; } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancingEngine.java deleted file mode 100644 index ad8964712..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancingEngine.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.jozufozu.flywheel.backend.engine.instancing; - -import java.util.List; - -import com.jozufozu.flywheel.api.event.RenderContext; -import com.jozufozu.flywheel.api.event.RenderStage; -import com.jozufozu.flywheel.api.task.Plan; -import com.jozufozu.flywheel.api.task.TaskExecutor; -import com.jozufozu.flywheel.backend.compile.InstancingPrograms; -import com.jozufozu.flywheel.backend.engine.AbstractEngine; -import com.jozufozu.flywheel.backend.engine.AbstractInstancer; -import com.jozufozu.flywheel.backend.engine.InstancerStorage; -import com.jozufozu.flywheel.backend.engine.uniform.Uniforms; -import com.jozufozu.flywheel.backend.gl.GlStateTracker; -import com.jozufozu.flywheel.lib.task.Flag; -import com.jozufozu.flywheel.lib.task.NamedFlag; -import com.jozufozu.flywheel.lib.task.SyncedPlan; - -public class InstancingEngine extends AbstractEngine { - private final InstancedDrawManager drawManager; - private final Flag flushFlag = new NamedFlag("flushed"); - - public InstancingEngine(InstancingPrograms programs, int maxOriginDistance) { - super(maxOriginDistance); - drawManager = new InstancedDrawManager(programs); - } - - @Override - public Plan createFramePlan() { - return SyncedPlan.of(this::flushDrawManager); - } - - private void flushDrawManager(RenderContext ctx) { - try (var restoreState = GlStateTracker.getRestoreState()) { - Uniforms.updateContext(ctx); - drawManager.flush(); - } - flushFlag.raise(); - } - - @Override - public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) { - executor.syncUntil(flushFlag::isRaised); - if (stage.isLast()) { - flushFlag.lower(); - } - - drawManager.renderStage(stage); - } - - @Override - public void renderCrumbling(TaskExecutor executor, RenderContext context, List crumblingBlocks) { - // Need to wait for flush before we can inspect instancer state. - executor.syncUntil(flushFlag::isRaised); - - drawManager.renderCrumbling(crumblingBlocks); - } - - @Override - protected InstancerStorage> getStorage() { - return drawManager; - } - - @Override - public void delete() { - drawManager.delete(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/util/AtomicReferenceCounted.java b/src/main/java/com/jozufozu/flywheel/backend/util/AtomicReferenceCounted.java index cf45d7a5d..1aa97c09e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/util/AtomicReferenceCounted.java +++ b/src/main/java/com/jozufozu/flywheel/backend/util/AtomicReferenceCounted.java @@ -30,11 +30,11 @@ public abstract class AtomicReferenceCounted { int newCount = referenceCount.decrementAndGet(); if (newCount == 0) { isDeleted = true; - delete(); + _delete(); } else if (newCount < 0) { throw new IllegalStateException("Tried to delete instance of '" + getClass().getName() + "' more times than it was acquired!"); } } - protected abstract void delete(); + protected abstract void _delete(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/util/ReferenceCounted.java b/src/main/java/com/jozufozu/flywheel/backend/util/ReferenceCounted.java index ee3cf0786..fba534d88 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/util/ReferenceCounted.java +++ b/src/main/java/com/jozufozu/flywheel/backend/util/ReferenceCounted.java @@ -28,11 +28,11 @@ public abstract class ReferenceCounted { int newCount = --referenceCount; if (newCount == 0) { isDeleted = true; - delete(); + _delete(); } else if (newCount < 0) { throw new IllegalStateException("Tried to delete instance of '" + getClass().getName() + "' more times than it was acquired!"); } } - protected abstract void delete(); + protected abstract void _delete(); }