mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-27 07:26:48 +01:00
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
This commit is contained in:
parent
b90b80c8b0
commit
e4df896710
23 changed files with 321 additions and 284 deletions
|
@ -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.
|
||||
*
|
||||
* <p>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.</p>
|
||||
*/
|
||||
void delete();
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -138,7 +138,7 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void delete() {
|
||||
protected void _delete() {
|
||||
pipeline.values()
|
||||
.forEach(GlProgram::delete);
|
||||
culling.values()
|
||||
|
|
|
@ -66,7 +66,7 @@ public class InstancingPrograms extends AtomicReferenceCounted {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void delete() {
|
||||
protected void _delete() {
|
||||
pipeline.values()
|
||||
.forEach(GlProgram::delete);
|
||||
}
|
||||
|
|
|
@ -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 <I extends Instance> Instancer<I> instancer(Environment environment, InstanceType<I> 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<? extends AbstractInstancer<?>> 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,6 +25,8 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
|
|||
protected AbstractInstancer(InstanceType<I> type, Environment environment) {
|
||||
this.type = type;
|
||||
this.environment = environment;
|
||||
|
||||
environment.acquire();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -159,6 +161,10 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
|
|||
deleted.clear();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
environment.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AbstractInstancer[" + instanceCount() + ']';
|
||||
|
|
|
@ -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<N extends AbstractInstancer<?>> {
|
||||
public abstract class DrawManager<N extends AbstractInstancer<?>> {
|
||||
/**
|
||||
* A map of instancer keys to instancers.
|
||||
* <br>
|
||||
* This map is populated as instancers are requested and contains both initialized and uninitialized instancers.
|
||||
*/
|
||||
protected final Map<InstancerKey<?>, N> instancers = new ConcurrentHashMap<>();
|
||||
protected final ReferenceSet<Environment> environments = new ReferenceLinkedOpenHashSet<>();
|
||||
/**
|
||||
* A list of instancers that have not yet been initialized.
|
||||
* <br>
|
||||
|
@ -37,12 +34,6 @@ public abstract class InstancerStorage<N extends AbstractInstancer<?>> {
|
|||
}
|
||||
|
||||
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<N extends AbstractInstancer<?>> {
|
|||
.forEach(AbstractInstancer::clear);
|
||||
}
|
||||
|
||||
public abstract void renderCrumbling(List<Engine.CrumblingBlock> crumblingBlocks);
|
||||
|
||||
public abstract void renderStage(RenderStage stage);
|
||||
|
||||
protected abstract <I extends Instance> N create(InstancerKey<I> type);
|
||||
|
||||
protected abstract <I extends Instance> void initialize(InstancerKey<I> key, N instancer);
|
||||
|
@ -68,15 +63,13 @@ public abstract class InstancerStorage<N extends AbstractInstancer<?>> {
|
|||
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<N, I extends Instance>(InstancerKey<I> key, N instancer) {
|
|
@ -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<? extends AbstractInstancer<?>> drawManager;
|
||||
private final EnvironmentStorage environmentStorage = new EnvironmentStorage(this);
|
||||
private final Flag flushFlag = new NamedFlag("flushed");
|
||||
|
||||
private BlockPos renderOrigin = BlockPos.ZERO;
|
||||
|
||||
public EngineImpl(DrawManager<? extends AbstractInstancer<?>> drawManager, int maxOriginDistance) {
|
||||
this.drawManager = drawManager;
|
||||
sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plan<RenderContext> 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<CrumblingBlock> 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 <I extends Instance> Instancer<I> instancer(Environment environment, InstanceType<I> 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<EmbeddedEnvironment> environments = ReferenceSets.synchronize(new ReferenceLinkedOpenHashSet<>());
|
||||
private final Queue<EmbeddedEnvironment> 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();
|
||||
}
|
||||
}
|
|
@ -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 <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model) {
|
||||
return engine.instancer(GlobalEnvironment.INSTANCE, type, model, renderStage);
|
||||
|
|
|
@ -189,7 +189,7 @@ public class MeshPool {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void delete() {
|
||||
protected void _delete() {
|
||||
MeshPool.this.dirty = true;
|
||||
MeshPool.this.anyToRemove = true;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -10,5 +10,7 @@ public interface Environment {
|
|||
|
||||
void setupCull(GlProgram cullProgram);
|
||||
|
||||
void delete();
|
||||
void acquire();
|
||||
|
||||
void release();
|
||||
}
|
||||
|
|
|
@ -25,7 +25,12 @@ public class GlobalEnvironment implements Environment {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
public void acquire() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,9 +77,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
|
||||
if (instanceCount == 0) {
|
||||
iterator.remove();
|
||||
for (IndirectDraw draw : instancer.draws()) {
|
||||
draw.delete();
|
||||
}
|
||||
instancer.delete();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<IndirectInstancer<?>> {
|
||||
public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
||||
private final IndirectPrograms programs;
|
||||
private final StagingBuffer stagingBuffer;
|
||||
private final MeshPool meshPool;
|
||||
|
@ -49,6 +49,7 @@ public class IndirectDrawManager extends InstancerStorage<IndirectInstancer<?>>
|
|||
|
||||
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<IndirectInstancer<?>>
|
|||
}
|
||||
|
||||
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<IndirectInstancer<?>>
|
|||
meshPool.delete();
|
||||
|
||||
crumblingDrawBuffer.delete();
|
||||
|
||||
programs.release();
|
||||
}
|
||||
|
||||
public void renderCrumbling(List<Engine.CrumblingBlock> crumblingBlocks) {
|
||||
|
|
|
@ -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<RenderContext> 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<CrumblingBlock> crumblingBlocks) {
|
||||
executor.syncUntil(flushFlag::isRaised);
|
||||
|
||||
drawManager.renderCrumbling(crumblingBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InstancerStorage<? extends AbstractInstancer<?>> getStorage() {
|
||||
return drawManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
drawManager.delete();
|
||||
programs.release();
|
||||
}
|
||||
}
|
|
@ -101,4 +101,13 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||
MemoryUtil.memPutInt(ptr, index);
|
||||
writer.write(ptr + IndirectBuffers.INT_SIZE, instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
super.delete();
|
||||
|
||||
for (IndirectDraw draw : draws()) {
|
||||
draw.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<InstancedInstancer<?>> {
|
||||
public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
||||
/**
|
||||
* The set of draw calls to make in each {@link RenderStage}.
|
||||
*/
|
||||
|
|
|
@ -107,6 +107,8 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||
}
|
||||
|
||||
public void delete() {
|
||||
super.delete();
|
||||
|
||||
if (vbo == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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<RenderContext> 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<CrumblingBlock> crumblingBlocks) {
|
||||
// Need to wait for flush before we can inspect instancer state.
|
||||
executor.syncUntil(flushFlag::isRaised);
|
||||
|
||||
drawManager.renderCrumbling(crumblingBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InstancerStorage<? extends AbstractInstancer<?>> getStorage() {
|
||||
return drawManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
drawManager.delete();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue