mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-27 23:47:09 +01:00
Store bought engines
- Add InstancerStorage so engines can share common code. - Instanced and Indirect DrawManagers extend InstancerStorage, while BatchingEngine keeps an anonymous class for it. - AbstractEngine now has a InstancerStorage getter and does some delegation so the implementations don't have to. - InstancedInstancer directly stores the list of DrawCalls it belongs to. - InstancingEngine no longer accepts a context parameter. - Make the /flywheel backend command default to the flywheel nampspace.
This commit is contained in:
parent
8fa095cd16
commit
2039a964e2
11 changed files with 274 additions and 247 deletions
|
@ -10,7 +10,6 @@ import com.jozufozu.flywheel.backend.engine.indirect.IndirectEngine;
|
||||||
import com.jozufozu.flywheel.backend.engine.instancing.InstancingEngine;
|
import com.jozufozu.flywheel.backend.engine.instancing.InstancingEngine;
|
||||||
import com.jozufozu.flywheel.gl.GlCompat;
|
import com.jozufozu.flywheel.gl.GlCompat;
|
||||||
import com.jozufozu.flywheel.lib.backend.SimpleBackend;
|
import com.jozufozu.flywheel.lib.backend.SimpleBackend;
|
||||||
import com.jozufozu.flywheel.lib.context.Contexts;
|
|
||||||
import com.jozufozu.flywheel.lib.util.ShadersModHandler;
|
import com.jozufozu.flywheel.lib.util.ShadersModHandler;
|
||||||
|
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
|
@ -33,7 +32,7 @@ public class Backends {
|
||||||
public static final Backend INSTANCING = SimpleBackend.builder()
|
public static final Backend INSTANCING = SimpleBackend.builder()
|
||||||
.engineMessage(Component.literal("Using Instancing Engine")
|
.engineMessage(Component.literal("Using Instancing Engine")
|
||||||
.withStyle(ChatFormatting.GREEN))
|
.withStyle(ChatFormatting.GREEN))
|
||||||
.engineFactory(level -> new InstancingEngine(256, Contexts.WORLD))
|
.engineFactory(level -> new InstancingEngine(256))
|
||||||
.fallback(() -> Backends.BATCHING)
|
.fallback(() -> Backends.BATCHING)
|
||||||
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.supportsInstancing() && InstancingPrograms.allLoaded())
|
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.supportsInstancing() && InstancingPrograms.allLoaded())
|
||||||
.register(Flywheel.rl("instancing"));
|
.register(Flywheel.rl("instancing"));
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
package com.jozufozu.flywheel.backend.engine;
|
package com.jozufozu.flywheel.backend.engine;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.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.model.Model;
|
||||||
|
|
||||||
import net.minecraft.client.Camera;
|
import net.minecraft.client.Camera;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
@ -15,6 +20,11 @@ public abstract class AbstractEngine implements Engine {
|
||||||
sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance;
|
sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model, RenderStage stage) {
|
||||||
|
return getStorage().getInstancer(type, model, stage);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean updateRenderOrigin(Camera camera) {
|
public boolean updateRenderOrigin(Camera camera) {
|
||||||
Vec3 cameraPos = camera.getPosition();
|
Vec3 cameraPos = camera.getPosition();
|
||||||
|
@ -28,7 +38,7 @@ public abstract class AbstractEngine implements Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderOrigin = BlockPos.containing(cameraPos);
|
renderOrigin = BlockPos.containing(cameraPos);
|
||||||
onRenderOriginChanged();
|
getStorage().onRenderOriginChanged();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +47,5 @@ public abstract class AbstractEngine implements Engine {
|
||||||
return renderOrigin;
|
return renderOrigin;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onRenderOriginChanged() {
|
protected abstract InstancerStorage<? extends AbstractInstancer<?>> getStorage();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,4 +108,8 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "AbstractInstancer[" + getInstanceCount() + ']';
|
return "AbstractInstancer[" + getInstanceCount() + ']';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void delete() {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package com.jozufozu.flywheel.backend.engine;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
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.model.Model;
|
||||||
|
|
||||||
|
public abstract class InstancerStorage<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.
|
||||||
|
* Write access to this map must be synchronized on {@link #creationLock}.
|
||||||
|
* <br>
|
||||||
|
* See {@link #getInstancer} for insertion details.
|
||||||
|
*/
|
||||||
|
private final Map<InstancerKey<?>, N> instancers = new HashMap<>();
|
||||||
|
/**
|
||||||
|
* A list of instancers that have not yet been initialized.
|
||||||
|
* <br>
|
||||||
|
* All new instancers land here before having resources allocated in {@link #flush}.
|
||||||
|
* Write access to this list must be synchronized on {@link #creationLock}.
|
||||||
|
*/
|
||||||
|
private final List<UninitializedInstancer<N, ?>> uninitializedInstancers = new ArrayList<>();
|
||||||
|
/**
|
||||||
|
* Mutex for {@link #instancers} and {@link #uninitializedInstancers}.
|
||||||
|
*/
|
||||||
|
private final Object creationLock = new Object();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of initialized instancers.
|
||||||
|
* <br>
|
||||||
|
* These are instancers that may need to be cleared or deleted.
|
||||||
|
*/
|
||||||
|
private final List<N> initializedInstancers = new ArrayList<>();
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <I extends Instance> Instancer<I> getInstancer(InstanceType<I> type, Model model, RenderStage stage) {
|
||||||
|
InstancerKey<I> key = new InstancerKey<>(type, model, stage);
|
||||||
|
|
||||||
|
N instancer = instancers.get(key);
|
||||||
|
// Happy path: instancer is already initialized.
|
||||||
|
if (instancer != null) {
|
||||||
|
return (Instancer<I>) instancer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unhappy path: instancer is not initialized, need to sync to make sure we don't create duplicates.
|
||||||
|
synchronized (creationLock) {
|
||||||
|
// Someone else might have initialized it while we were waiting for the lock.
|
||||||
|
instancer = instancers.get(key);
|
||||||
|
if (instancer != null) {
|
||||||
|
return (Instancer<I>) instancer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new instancer and add it to the uninitialized list.
|
||||||
|
instancer = create(type);
|
||||||
|
instancers.put(key, instancer);
|
||||||
|
uninitializedInstancers.add(new UninitializedInstancer<>(key, instancer, model, stage));
|
||||||
|
return (Instancer<I>) instancer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidate() {
|
||||||
|
instancers.clear();
|
||||||
|
uninitializedInstancers.clear();
|
||||||
|
|
||||||
|
initializedInstancers.forEach(AbstractInstancer::delete);
|
||||||
|
initializedInstancers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flush() {
|
||||||
|
for (var instancer : uninitializedInstancers) {
|
||||||
|
add(instancer.key(), instancer.instancer(), instancer.model(), instancer.stage());
|
||||||
|
initializedInstancers.add(instancer.instancer());
|
||||||
|
}
|
||||||
|
uninitializedInstancers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onRenderOriginChanged() {
|
||||||
|
initializedInstancers.forEach(AbstractInstancer::clear);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract <I extends Instance> N create(InstanceType<I> type);
|
||||||
|
|
||||||
|
protected abstract <I extends Instance> void add(InstancerKey<I> key, N instancer, Model model, RenderStage stage);
|
||||||
|
|
||||||
|
private record UninitializedInstancer<N, I extends Instance>(InstancerKey<I> key, N instancer, Model model, RenderStage stage) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
package com.jozufozu.flywheel.backend.engine.batching;
|
package com.jozufozu.flywheel.backend.engine.batching;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -10,13 +9,14 @@ import com.jozufozu.flywheel.api.event.RenderContext;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.instance.Instancer;
|
|
||||||
import com.jozufozu.flywheel.api.model.Mesh;
|
import com.jozufozu.flywheel.api.model.Mesh;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.api.task.Plan;
|
import com.jozufozu.flywheel.api.task.Plan;
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.backend.engine.AbstractEngine;
|
import com.jozufozu.flywheel.backend.engine.AbstractEngine;
|
||||||
|
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
||||||
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
||||||
|
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
|
||||||
import com.jozufozu.flywheel.lib.task.Flag;
|
import com.jozufozu.flywheel.lib.task.Flag;
|
||||||
import com.jozufozu.flywheel.lib.task.NamedFlag;
|
import com.jozufozu.flywheel.lib.task.NamedFlag;
|
||||||
import com.jozufozu.flywheel.lib.task.SimplyComposedPlan;
|
import com.jozufozu.flywheel.lib.task.SimplyComposedPlan;
|
||||||
|
@ -27,9 +27,27 @@ import net.minecraft.client.renderer.RenderType;
|
||||||
|
|
||||||
public class BatchingEngine extends AbstractEngine implements SimplyComposedPlan<RenderContext> {
|
public class BatchingEngine extends AbstractEngine implements SimplyComposedPlan<RenderContext> {
|
||||||
private final BatchedDrawTracker drawTracker = new BatchedDrawTracker();
|
private final BatchedDrawTracker drawTracker = new BatchedDrawTracker();
|
||||||
private final Map<InstancerKey<?>, BatchedInstancer<?>> instancers = new HashMap<>();
|
|
||||||
private final List<UninitializedInstancer> uninitializedInstancers = new ArrayList<>();
|
// TODO: reintroduce BatchedDrawManager
|
||||||
private final List<BatchedInstancer<?>> initializedInstancers = new ArrayList<>();
|
private final InstancerStorage<BatchedInstancer<?>> storage = new InstancerStorage<>() {
|
||||||
|
@Override
|
||||||
|
protected <I extends Instance> BatchedInstancer<?> create(InstanceType<I> type) {
|
||||||
|
return new BatchedInstancer<>(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected <I extends Instance> void add(InstancerKey<I> key, BatchedInstancer<?> instancer, Model model, RenderStage stage) {
|
||||||
|
var stagePlan = stagePlans.computeIfAbsent(stage, renderStage -> new BatchedStagePlan(renderStage, drawTracker));
|
||||||
|
var meshes = model.getMeshes();
|
||||||
|
for (var entry : meshes.entrySet()) {
|
||||||
|
var material = entry.getKey();
|
||||||
|
RenderType renderType = material.getFallbackRenderType();
|
||||||
|
var transformCall = new TransformCall<>(instancer, material, alloc(entry.getValue(), renderType.format()));
|
||||||
|
stagePlan.put(renderType, transformCall);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private final Map<RenderStage, BatchedStagePlan> stagePlans = new EnumMap<>(RenderStage.class);
|
private final Map<RenderStage, BatchedStagePlan> stagePlans = new EnumMap<>(RenderStage.class);
|
||||||
private final Map<VertexFormat, BatchedMeshPool> meshPools = new HashMap<>();
|
private final Map<VertexFormat, BatchedMeshPool> meshPools = new HashMap<>();
|
||||||
|
|
||||||
|
@ -39,20 +57,6 @@ public class BatchingEngine extends AbstractEngine implements SimplyComposedPlan
|
||||||
super(maxOriginDistance);
|
super(maxOriginDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model, RenderStage stage) {
|
|
||||||
InstancerKey<I> key = new InstancerKey<>(type, model, stage);
|
|
||||||
BatchedInstancer<I> instancer = (BatchedInstancer<I>) instancers.get(key);
|
|
||||||
// FIXME: This needs to be synchronized like InstancingEngine
|
|
||||||
if (instancer == null) {
|
|
||||||
instancer = new BatchedInstancer<>(type);
|
|
||||||
instancers.put(key, instancer);
|
|
||||||
uninitializedInstancers.add(new UninitializedInstancer(instancer, model, stage));
|
|
||||||
}
|
|
||||||
return instancer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(TaskExecutor taskExecutor, RenderContext context, Runnable onCompletion) {
|
public void execute(TaskExecutor taskExecutor, RenderContext context, Runnable onCompletion) {
|
||||||
flush();
|
flush();
|
||||||
|
@ -100,49 +104,29 @@ public class BatchingEngine extends AbstractEngine implements SimplyComposedPlan
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onRenderOriginChanged() {
|
protected InstancerStorage<? extends AbstractInstancer<?>> getStorage() {
|
||||||
initializedInstancers.forEach(BatchedInstancer::clear);
|
return storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete() {
|
public void delete() {
|
||||||
instancers.clear();
|
storage.invalidate();
|
||||||
|
|
||||||
meshPools.values()
|
meshPools.values()
|
||||||
.forEach(BatchedMeshPool::delete);
|
.forEach(BatchedMeshPool::delete);
|
||||||
meshPools.clear();
|
meshPools.clear();
|
||||||
|
|
||||||
initializedInstancers.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flush() {
|
private void flush() {
|
||||||
for (var instancer : uninitializedInstancers) {
|
storage.flush();
|
||||||
add(instancer.instancer(), instancer.model(), instancer.stage());
|
|
||||||
}
|
|
||||||
uninitializedInstancers.clear();
|
|
||||||
|
|
||||||
for (var pool : meshPools.values()) {
|
for (var pool : meshPools.values()) {
|
||||||
pool.flush();
|
pool.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void add(BatchedInstancer<?> instancer, Model model, RenderStage stage) {
|
|
||||||
var stagePlan = stagePlans.computeIfAbsent(stage, renderStage -> new BatchedStagePlan(renderStage, drawTracker));
|
|
||||||
var meshes = model.getMeshes();
|
|
||||||
for (var entry : meshes.entrySet()) {
|
|
||||||
var material = entry.getKey();
|
|
||||||
RenderType renderType = material.getFallbackRenderType();
|
|
||||||
var transformCall = new TransformCall<>(instancer, material, alloc(entry.getValue(), renderType.format()));
|
|
||||||
stagePlan.put(renderType, transformCall);
|
|
||||||
}
|
|
||||||
initializedInstancers.add(instancer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BatchedMeshPool.BufferedMesh alloc(Mesh mesh, VertexFormat format) {
|
private BatchedMeshPool.BufferedMesh alloc(Mesh mesh, VertexFormat format) {
|
||||||
return meshPools.computeIfAbsent(format, BatchedMeshPool::new)
|
return meshPools.computeIfAbsent(format, BatchedMeshPool::new)
|
||||||
.alloc(mesh);
|
.alloc(mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
private record UninitializedInstancer(BatchedInstancer<?> instancer, Model model, RenderStage stage) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +1,22 @@
|
||||||
package com.jozufozu.flywheel.backend.engine.indirect;
|
package com.jozufozu.flywheel.backend.engine.indirect;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.instance.Instancer;
|
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
||||||
|
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
|
||||||
import com.jozufozu.flywheel.lib.util.Pair;
|
import com.jozufozu.flywheel.lib.util.Pair;
|
||||||
|
|
||||||
public class IndirectDrawManager {
|
public class IndirectDrawManager extends InstancerStorage<IndirectInstancer<?>> {
|
||||||
private final Map<InstancerKey<?>, IndirectInstancer<?>> instancers = new HashMap<>();
|
|
||||||
private final List<UninitializedInstancer> uninitializedInstancers = new ArrayList<>();
|
|
||||||
private final List<IndirectInstancer<?>> initializedInstancers = new ArrayList<>();
|
|
||||||
public final Map<Pair<InstanceType<?>, VertexType>, IndirectCullingGroup<?>> renderLists = new HashMap<>();
|
public final Map<Pair<InstanceType<?>, VertexType>, IndirectCullingGroup<?>> renderLists = new HashMap<>();
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <I extends Instance> Instancer<I> getInstancer(InstanceType<I> type, Model model, RenderStage stage) {
|
|
||||||
InstancerKey<I> key = new InstancerKey<>(type, model, stage);
|
|
||||||
// FIXME: This needs to be synchronized like InstancingEngine
|
|
||||||
IndirectInstancer<I> instancer = (IndirectInstancer<I>) instancers.get(key);
|
|
||||||
if (instancer == null) {
|
|
||||||
instancer = new IndirectInstancer<>(type);
|
|
||||||
instancers.put(key, instancer);
|
|
||||||
uninitializedInstancers.add(new UninitializedInstancer(instancer, model, stage));
|
|
||||||
}
|
|
||||||
return instancer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void flush() {
|
public void flush() {
|
||||||
for (var instancer : uninitializedInstancers) {
|
super.flush();
|
||||||
add(instancer.instancer(), instancer.model(), instancer.stage());
|
|
||||||
}
|
|
||||||
uninitializedInstancers.clear();
|
|
||||||
|
|
||||||
for (IndirectCullingGroup<?> value : renderLists.values()) {
|
for (IndirectCullingGroup<?> value : renderLists.values()) {
|
||||||
value.beginFrame();
|
value.beginFrame();
|
||||||
|
@ -45,33 +24,11 @@ public class IndirectDrawManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void invalidate() {
|
public void invalidate() {
|
||||||
instancers.clear();
|
super.invalidate();
|
||||||
|
|
||||||
renderLists.values()
|
renderLists.values()
|
||||||
.forEach(IndirectCullingGroup::delete);
|
.forEach(IndirectCullingGroup::delete);
|
||||||
renderLists.clear();
|
renderLists.clear();
|
||||||
|
|
||||||
initializedInstancers.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearInstancers() {
|
|
||||||
initializedInstancers.forEach(IndirectInstancer::clear);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private <I extends Instance> void add(IndirectInstancer<I> instancer, Model model, RenderStage stage) {
|
|
||||||
var meshes = model.getMeshes();
|
|
||||||
for (var entry : meshes.entrySet()) {
|
|
||||||
var material = entry.getKey();
|
|
||||||
var mesh = entry.getValue();
|
|
||||||
|
|
||||||
var indirectList = (IndirectCullingGroup<I>) renderLists.computeIfAbsent(Pair.of(instancer.type, mesh.vertexType()), p -> new IndirectCullingGroup<>(p.first(), p.second()));
|
|
||||||
|
|
||||||
indirectList.add(instancer, stage, material, mesh);
|
|
||||||
|
|
||||||
break; // TODO: support multiple meshes per model
|
|
||||||
}
|
|
||||||
initializedInstancers.add(instancer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasStage(RenderStage stage) {
|
public boolean hasStage(RenderStage stage) {
|
||||||
|
@ -83,6 +40,23 @@ public class IndirectDrawManager {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private record UninitializedInstancer(IndirectInstancer<?> instancer, Model model, RenderStage stage) {
|
@Override
|
||||||
|
protected <I extends Instance> IndirectInstancer<?> create(InstanceType<I> type) {
|
||||||
|
return new IndirectInstancer<>(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected <I extends Instance> void add(InstancerKey<I> key, IndirectInstancer<?> instancer, Model model, RenderStage stage) {
|
||||||
|
var meshes = model.getMeshes();
|
||||||
|
for (var entry : meshes.entrySet()) {
|
||||||
|
var material = entry.getKey();
|
||||||
|
var mesh = entry.getValue();
|
||||||
|
|
||||||
|
var indirectList = (IndirectCullingGroup<I>) renderLists.computeIfAbsent(Pair.of(key.type(), mesh.vertexType()), p -> new IndirectCullingGroup<>(p.first(), p.second()));
|
||||||
|
|
||||||
|
indirectList.add((IndirectInstancer<I>) instancer, stage, material, mesh);
|
||||||
|
|
||||||
|
break; // TODO: support multiple meshes per model
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,17 +7,15 @@ import org.lwjgl.opengl.GL32;
|
||||||
import com.jozufozu.flywheel.api.event.RenderContext;
|
import com.jozufozu.flywheel.api.event.RenderContext;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
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.model.Model;
|
|
||||||
import com.jozufozu.flywheel.api.task.Plan;
|
import com.jozufozu.flywheel.api.task.Plan;
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.backend.engine.AbstractEngine;
|
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.gl.GlStateTracker;
|
import com.jozufozu.flywheel.gl.GlStateTracker;
|
||||||
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
||||||
import com.jozufozu.flywheel.lib.task.Flag;
|
import com.jozufozu.flywheel.lib.task.Flag;
|
||||||
import com.jozufozu.flywheel.lib.task.NamedFlag;
|
import com.jozufozu.flywheel.lib.task.NamedFlag;
|
||||||
import com.jozufozu.flywheel.lib.task.RaisePlan;
|
|
||||||
import com.jozufozu.flywheel.lib.task.SyncedPlan;
|
import com.jozufozu.flywheel.lib.task.SyncedPlan;
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
|
||||||
|
@ -31,44 +29,37 @@ public class IndirectEngine extends AbstractEngine {
|
||||||
super(maxOriginDistance);
|
super(maxOriginDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model, RenderStage stage) {
|
|
||||||
return drawManager.getInstancer(type, model, stage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Plan<RenderContext> createFramePlan() {
|
public Plan<RenderContext> createFramePlan() {
|
||||||
return SyncedPlan.<RenderContext>of(this::flushDrawManager)
|
return SyncedPlan.of(this::flushDrawManager);
|
||||||
.then(RaisePlan.raise(flushFlag));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flushDrawManager() {
|
private void flushDrawManager() {
|
||||||
try (var state = GlStateTracker.getRestoreState()) {
|
try (var state = GlStateTracker.getRestoreState()) {
|
||||||
drawManager.flush();
|
drawManager.flush();
|
||||||
}
|
}
|
||||||
|
flushFlag.raise();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) {
|
public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) {
|
||||||
if (drawManager.hasStage(stage)) {
|
executor.syncUntil(flushFlag::isRaised);
|
||||||
executor.syncUntil(flushFlag::isRaised);
|
|
||||||
|
|
||||||
try (var restoreState = GlStateTracker.getRestoreState()) {
|
|
||||||
setup();
|
|
||||||
|
|
||||||
for (var list : drawManager.renderLists.values()) {
|
|
||||||
list.submit(stage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stage.isLast()) {
|
if (stage.isLast()) {
|
||||||
// Need to sync here to ensure this frame has everything executed
|
|
||||||
// in case we didn't have any stages to draw this frame.
|
|
||||||
executor.syncUntil(flushFlag::isRaised);
|
|
||||||
flushFlag.lower();
|
flushFlag.lower();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (!drawManager.hasStage(stage)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (var restoreState = GlStateTracker.getRestoreState()) {
|
||||||
|
setup();
|
||||||
|
|
||||||
|
for (var list : drawManager.renderLists.values()) {
|
||||||
|
list.submit(stage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderCrumblingInstances(TaskExecutor executor, RenderContext context, List<Instance> instances, int progress) {
|
public void renderCrumblingInstances(TaskExecutor executor, RenderContext context, List<Instance> instances, int progress) {
|
||||||
|
@ -87,8 +78,8 @@ public class IndirectEngine extends AbstractEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onRenderOriginChanged() {
|
protected InstancerStorage<? extends AbstractInstancer<?>> getStorage() {
|
||||||
drawManager.clearInstancers();
|
return drawManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
package com.jozufozu.flywheel.backend.engine.instancing;
|
package com.jozufozu.flywheel.backend.engine.instancing;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
@ -16,40 +14,14 @@ import com.google.common.collect.ListMultimap;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.instance.Instancer;
|
|
||||||
import com.jozufozu.flywheel.api.model.Mesh;
|
import com.jozufozu.flywheel.api.model.Mesh;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
||||||
|
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
|
||||||
|
|
||||||
public class InstancedDrawManager {
|
public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>> {
|
||||||
/**
|
|
||||||
* A map of instancer keys to instancers.
|
|
||||||
* <br>
|
|
||||||
* This map is populated as instancers are requested and contains both initialized and uninitialized instancers.
|
|
||||||
* Write access to this map must be synchronized on {@link #creationLock}.
|
|
||||||
* <br>
|
|
||||||
* See {@link #getInstancer} for insertion details.
|
|
||||||
*/
|
|
||||||
private final Map<InstancerKey<?>, InstancedInstancer<?>> instancers = new HashMap<>();
|
|
||||||
/**
|
|
||||||
* A list of instancers that have not yet been initialized.
|
|
||||||
* <br>
|
|
||||||
* All new instancers land here before having resources allocated in {@link #flush}.
|
|
||||||
* Write access to this list must be synchronized on {@link #creationLock}.
|
|
||||||
*/
|
|
||||||
private final List<UninitializedInstancer> uninitializedInstancers = new ArrayList<>();
|
|
||||||
/**
|
|
||||||
* Mutex for {@link #instancers} and {@link #uninitializedInstancers}.
|
|
||||||
*/
|
|
||||||
private final Object creationLock = new Object();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A map of initialized instancers to their draw calls.
|
|
||||||
* <br>
|
|
||||||
* This map is populated in {@link #flush} and contains only initialized instancers.
|
|
||||||
*/
|
|
||||||
private final Map<InstancedInstancer<?>, List<DrawCall>> initializedInstancers = new HashMap<>();
|
|
||||||
/**
|
/**
|
||||||
* The set of draw calls to make in each {@link RenderStage}.
|
* The set of draw calls to make in each {@link RenderStage}.
|
||||||
*/
|
*/
|
||||||
|
@ -64,37 +36,8 @@ public class InstancedDrawManager {
|
||||||
return drawSets.getOrDefault(stage, DrawSet.EMPTY);
|
return drawSets.getOrDefault(stage, DrawSet.EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <I extends Instance> Instancer<I> getInstancer(InstanceType<I> type, Model model, RenderStage stage) {
|
|
||||||
InstancerKey<I> key = new InstancerKey<>(type, model, stage);
|
|
||||||
|
|
||||||
InstancedInstancer<I> instancer = (InstancedInstancer<I>) instancers.get(key);
|
|
||||||
// Happy path: instancer is already initialized.
|
|
||||||
if (instancer != null) {
|
|
||||||
return instancer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unhappy path: instancer is not initialized, need to sync to make sure we don't create duplicates.
|
|
||||||
synchronized (creationLock) {
|
|
||||||
// Someone else might have initialized it while we were waiting for the lock.
|
|
||||||
instancer = (InstancedInstancer<I>) instancers.get(key);
|
|
||||||
if (instancer != null) {
|
|
||||||
return instancer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new instancer and add it to the uninitialized list.
|
|
||||||
instancer = new InstancedInstancer<>(type);
|
|
||||||
instancers.put(key, instancer);
|
|
||||||
uninitializedInstancers.add(new UninitializedInstancer(instancer, model, stage));
|
|
||||||
return instancer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void flush() {
|
public void flush() {
|
||||||
for (var instancer : uninitializedInstancers) {
|
super.flush();
|
||||||
add(instancer.instancer(), instancer.model(), instancer.stage());
|
|
||||||
}
|
|
||||||
uninitializedInstancers.clear();
|
|
||||||
|
|
||||||
for (var pool : meshPools.values()) {
|
for (var pool : meshPools.values()) {
|
||||||
pool.flush();
|
pool.flush();
|
||||||
|
@ -102,7 +45,7 @@ public class InstancedDrawManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void invalidate() {
|
public void invalidate() {
|
||||||
instancers.clear();
|
super.invalidate();
|
||||||
|
|
||||||
meshPools.values()
|
meshPools.values()
|
||||||
.forEach(InstancedMeshPool::delete);
|
.forEach(InstancedMeshPool::delete);
|
||||||
|
@ -112,23 +55,24 @@ public class InstancedDrawManager {
|
||||||
.forEach(DrawSet::delete);
|
.forEach(DrawSet::delete);
|
||||||
drawSets.clear();
|
drawSets.clear();
|
||||||
|
|
||||||
initializedInstancers.keySet()
|
|
||||||
.forEach(InstancedInstancer::delete);
|
|
||||||
initializedInstancers.clear();
|
|
||||||
|
|
||||||
eboCache.invalidate();
|
eboCache.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearInstancers() {
|
private InstancedMeshPool.BufferedMesh alloc(Mesh mesh) {
|
||||||
initializedInstancers.keySet()
|
return meshPools.computeIfAbsent(mesh.vertexType(), InstancedMeshPool::new)
|
||||||
.forEach(InstancedInstancer::clear);
|
.alloc(mesh, eboCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void add(InstancedInstancer<?> instancer, Model model, RenderStage stage) {
|
@Override
|
||||||
|
protected <I extends Instance> InstancedInstancer<I> create(InstanceType<I> type) {
|
||||||
|
return new InstancedInstancer<>(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected <I extends Instance> void add(InstancerKey<I> key, InstancedInstancer<?> instancer, Model model, RenderStage stage) {
|
||||||
instancer.init();
|
instancer.init();
|
||||||
|
|
||||||
DrawSet drawSet = drawSets.computeIfAbsent(stage, DrawSet::new);
|
DrawSet drawSet = drawSets.computeIfAbsent(stage, DrawSet::new);
|
||||||
List<DrawCall> drawCalls = new ArrayList<>();
|
|
||||||
|
|
||||||
var meshes = model.getMeshes();
|
var meshes = model.getMeshes();
|
||||||
for (var entry : meshes.entrySet()) {
|
for (var entry : meshes.entrySet()) {
|
||||||
|
@ -138,18 +82,8 @@ public class InstancedDrawManager {
|
||||||
DrawCall drawCall = new DrawCall(instancer, mesh, shaderState);
|
DrawCall drawCall = new DrawCall(instancer, mesh, shaderState);
|
||||||
|
|
||||||
drawSet.put(shaderState, drawCall);
|
drawSet.put(shaderState, drawCall);
|
||||||
drawCalls.add(drawCall);
|
instancer.addDrawCall(drawCall);
|
||||||
}
|
}
|
||||||
initializedInstancers.put(instancer, drawCalls);
|
|
||||||
}
|
|
||||||
|
|
||||||
private InstancedMeshPool.BufferedMesh alloc(Mesh mesh) {
|
|
||||||
return meshPools.computeIfAbsent(mesh.vertexType(), InstancedMeshPool::new)
|
|
||||||
.alloc(mesh, eboCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<DrawCall> drawCallsForInstancer(InstancedInstancer<?> instancer) {
|
|
||||||
return initializedInstancers.getOrDefault(instancer, List.of());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class DrawSet implements Iterable<Map.Entry<ShaderState, Collection<DrawCall>>> {
|
public static class DrawSet implements Iterable<Map.Entry<ShaderState, Collection<DrawCall>>> {
|
||||||
|
@ -187,7 +121,4 @@ public class InstancedDrawManager {
|
||||||
.iterator();
|
.iterator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private record UninitializedInstancer(InstancedInstancer<?> instancer, Model model, RenderStage stage) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package com.jozufozu.flywheel.backend.engine.instancing;
|
package com.jozufozu.flywheel.backend.engine.instancing;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
|
@ -21,6 +23,8 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
|
||||||
private final Set<GlVertexArray> boundTo = new HashSet<>();
|
private final Set<GlVertexArray> boundTo = new HashSet<>();
|
||||||
private GlBuffer vbo;
|
private GlBuffer vbo;
|
||||||
|
|
||||||
|
private final List<DrawCall> drawCalls = new ArrayList<>();
|
||||||
|
|
||||||
public InstancedInstancer(InstanceType<I> type) {
|
public InstancedInstancer(InstanceType<I> type) {
|
||||||
super(type);
|
super(type);
|
||||||
instanceFormat = type.getLayout();
|
instanceFormat = type.getLayout();
|
||||||
|
@ -111,4 +115,12 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
|
||||||
vbo.delete();
|
vbo.delete();
|
||||||
vbo = null;
|
vbo = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addDrawCall(DrawCall drawCall) {
|
||||||
|
drawCalls.add(drawCall);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DrawCall> drawCalls() {
|
||||||
|
return drawCalls;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,23 +12,20 @@ import com.jozufozu.flywheel.api.context.Context;
|
||||||
import com.jozufozu.flywheel.api.event.RenderContext;
|
import com.jozufozu.flywheel.api.event.RenderContext;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
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.model.Model;
|
|
||||||
import com.jozufozu.flywheel.api.task.Plan;
|
import com.jozufozu.flywheel.api.task.Plan;
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.backend.compile.InstancingPrograms;
|
import com.jozufozu.flywheel.backend.compile.InstancingPrograms;
|
||||||
import com.jozufozu.flywheel.backend.engine.AbstractEngine;
|
import com.jozufozu.flywheel.backend.engine.AbstractEngine;
|
||||||
|
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
||||||
import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl;
|
import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl;
|
||||||
|
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
|
||||||
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
|
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
|
||||||
import com.jozufozu.flywheel.backend.engine.indirect.Textures;
|
|
||||||
import com.jozufozu.flywheel.gl.GlStateTracker;
|
import com.jozufozu.flywheel.gl.GlStateTracker;
|
||||||
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
||||||
import com.jozufozu.flywheel.lib.context.Contexts;
|
import com.jozufozu.flywheel.lib.context.Contexts;
|
||||||
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
||||||
import com.jozufozu.flywheel.lib.task.Flag;
|
import com.jozufozu.flywheel.lib.task.Flag;
|
||||||
import com.jozufozu.flywheel.lib.task.NamedFlag;
|
import com.jozufozu.flywheel.lib.task.NamedFlag;
|
||||||
import com.jozufozu.flywheel.lib.task.RaisePlan;
|
|
||||||
import com.jozufozu.flywheel.lib.task.SyncedPlan;
|
import com.jozufozu.flywheel.lib.task.SyncedPlan;
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
|
||||||
|
@ -36,54 +33,45 @@ import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.resources.model.ModelBakery;
|
import net.minecraft.client.resources.model.ModelBakery;
|
||||||
|
|
||||||
public class InstancingEngine extends AbstractEngine {
|
public class InstancingEngine extends AbstractEngine {
|
||||||
private final Context context;
|
|
||||||
private final InstancedDrawManager drawManager = new InstancedDrawManager();
|
private final InstancedDrawManager drawManager = new InstancedDrawManager();
|
||||||
|
|
||||||
private final Flag flushFlag = new NamedFlag("flushed");
|
private final Flag flushFlag = new NamedFlag("flushed");
|
||||||
|
|
||||||
public InstancingEngine(int maxOriginDistance, Context context) {
|
public InstancingEngine(int maxOriginDistance) {
|
||||||
super(maxOriginDistance);
|
super(maxOriginDistance);
|
||||||
this.context = context;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model, RenderStage stage) {
|
|
||||||
return drawManager.getInstancer(type, model, stage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Plan<RenderContext> createFramePlan() {
|
public Plan<RenderContext> createFramePlan() {
|
||||||
return SyncedPlan.<RenderContext>of(this::flushDrawManager)
|
return SyncedPlan.of(this::flushDrawManager);
|
||||||
.then(RaisePlan.raise(flushFlag));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flushDrawManager() {
|
private void flushDrawManager() {
|
||||||
try (var restoreState = GlStateTracker.getRestoreState()) {
|
try (var restoreState = GlStateTracker.getRestoreState()) {
|
||||||
drawManager.flush();
|
drawManager.flush();
|
||||||
}
|
}
|
||||||
|
flushFlag.raise();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) {
|
public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) {
|
||||||
var drawSet = drawManager.get(stage);
|
executor.syncUntil(flushFlag::isRaised);
|
||||||
|
|
||||||
if (!drawSet.isEmpty()) {
|
|
||||||
executor.syncUntil(flushFlag::isRaised);
|
|
||||||
|
|
||||||
try (var state = GlStateTracker.getRestoreState()) {
|
|
||||||
setup();
|
|
||||||
|
|
||||||
render(drawSet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stage.isLast()) {
|
if (stage.isLast()) {
|
||||||
// Need to sync here to ensure this frame has everything executed
|
|
||||||
// in case we didn't have any stages to draw this frame.
|
|
||||||
executor.syncUntil(flushFlag::isRaised);
|
|
||||||
flushFlag.lower();
|
flushFlag.lower();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
var drawSet = drawManager.get(stage);
|
||||||
|
|
||||||
|
if (drawSet.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (var state = GlStateTracker.getRestoreState()) {
|
||||||
|
setup();
|
||||||
|
|
||||||
|
render(drawSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderCrumblingInstances(TaskExecutor executor, RenderContext context, List<Instance> instances, int progress) {
|
public void renderCrumblingInstances(TaskExecutor executor, RenderContext context, List<Instance> instances, int progress) {
|
||||||
|
@ -139,7 +127,7 @@ public class InstancingEngine extends AbstractEngine {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DrawCall> draws = drawManager.drawCallsForInstancer(instancer);
|
List<DrawCall> draws = instancer.drawCalls();
|
||||||
|
|
||||||
draws.removeIf(DrawCall::isInvalid);
|
draws.removeIf(DrawCall::isInvalid);
|
||||||
|
|
||||||
|
@ -173,7 +161,7 @@ public class InstancingEngine extends AbstractEngine {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
setup(shader, context);
|
setup(shader, Contexts.WORLD);
|
||||||
|
|
||||||
shader.material().setup();
|
shader.material().setup();
|
||||||
|
|
||||||
|
@ -201,8 +189,8 @@ public class InstancingEngine extends AbstractEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onRenderOriginChanged() {
|
protected InstancerStorage<? extends AbstractInstancer<?>> getStorage() {
|
||||||
drawManager.clearInstancers();
|
return drawManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -4,21 +4,39 @@ import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.api.backend.Backend;
|
import com.jozufozu.flywheel.api.backend.Backend;
|
||||||
|
import com.jozufozu.flywheel.lib.util.ResourceUtil;
|
||||||
import com.mojang.brigadier.StringReader;
|
import com.mojang.brigadier.StringReader;
|
||||||
import com.mojang.brigadier.arguments.ArgumentType;
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
||||||
|
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||||
import com.mojang.brigadier.suggestion.Suggestions;
|
import com.mojang.brigadier.suggestion.Suggestions;
|
||||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||||
|
|
||||||
|
import net.minecraft.ResourceLocationException;
|
||||||
import net.minecraft.commands.SharedSuggestionProvider;
|
import net.minecraft.commands.SharedSuggestionProvider;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public class BackendArgument implements ArgumentType<Backend> {
|
public class BackendArgument implements ArgumentType<Backend> {
|
||||||
private static final List<String> STRING_IDS = Backend.REGISTRY.getAllIds().stream().map(ResourceLocation::toString).toList();
|
private static final List<String> STRING_IDS = Backend.REGISTRY.getAllIds()
|
||||||
|
.stream()
|
||||||
|
.map(rl -> {
|
||||||
|
if (Flywheel.ID
|
||||||
|
.equals(rl.getNamespace())) {
|
||||||
|
return rl.getPath();
|
||||||
|
} else {
|
||||||
|
return rl.toString();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
private static final SimpleCommandExceptionType ERROR_INVALID = new SimpleCommandExceptionType(Component.translatable("argument.id.invalid"));
|
||||||
|
|
||||||
public static final DynamicCommandExceptionType ERROR_UNKNOWN_BACKEND = new DynamicCommandExceptionType(arg -> {
|
public static final DynamicCommandExceptionType ERROR_UNKNOWN_BACKEND = new DynamicCommandExceptionType(arg -> {
|
||||||
return Component.literal("Unknown backend '" + arg + "'");
|
return Component.literal("Unknown backend '" + arg + "'");
|
||||||
|
@ -28,7 +46,7 @@ public class BackendArgument implements ArgumentType<Backend> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Backend parse(StringReader reader) throws CommandSyntaxException {
|
public Backend parse(StringReader reader) throws CommandSyntaxException {
|
||||||
ResourceLocation id = ResourceLocation.read(reader);
|
ResourceLocation id = getRead(reader);
|
||||||
Backend backend = Backend.REGISTRY.get(id);
|
Backend backend = Backend.REGISTRY.get(id);
|
||||||
|
|
||||||
if (backend == null) {
|
if (backend == null) {
|
||||||
|
@ -38,6 +56,27 @@ public class BackendArgument implements ArgumentType<Backend> {
|
||||||
return backend;
|
return backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copied from {@link ResourceLocation#read}, but defaults to flywheel namespace.
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private static ResourceLocation getRead(StringReader reader) throws CommandSyntaxException {
|
||||||
|
int i = reader.getCursor();
|
||||||
|
|
||||||
|
while(reader.canRead() && ResourceLocation.isAllowedInResourceLocation(reader.peek())) {
|
||||||
|
reader.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
String s = reader.getString().substring(i, reader.getCursor());
|
||||||
|
|
||||||
|
try {
|
||||||
|
return ResourceUtil.defaultToFlywheelNamespace(s);
|
||||||
|
} catch (ResourceLocationException resourcelocationexception) {
|
||||||
|
reader.setCursor(i);
|
||||||
|
throw ERROR_INVALID.createWithContext(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||||
return SharedSuggestionProvider.suggest(STRING_IDS, builder);
|
return SharedSuggestionProvider.suggest(STRING_IDS, builder);
|
||||||
|
|
Loading…
Reference in a new issue