Contraptions and engines

- Sort of get the batching engine working for contraptions but this feels wrong
 - TaskEngine gets passed in methods
 - Better naming for TaskEngines
 - Do BufferSource ourselves
 - Change BufferBuilderMixin to allow for injection into BufferBuilder objects
This commit is contained in:
Jozufozu 2021-12-23 14:41:10 -08:00
parent 5cca71332d
commit 4d5391f5dc
19 changed files with 197 additions and 150 deletions

View file

@ -115,4 +115,9 @@ public abstract class AbstractInstancer<D extends InstanceData> implements Insta
return instanceData; return instanceData;
} }
@Override
public String toString() {
return "Instancer[" + modelData + ']';
}
} }

View file

@ -32,13 +32,11 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
protected final Map<T, AbstractInstance> instances; protected final Map<T, AbstractInstance> instances;
protected final Object2ObjectOpenHashMap<T, ITickableInstance> tickableInstances; protected final Object2ObjectOpenHashMap<T, ITickableInstance> tickableInstances;
protected final Object2ObjectOpenHashMap<T, IDynamicInstance> dynamicInstances; protected final Object2ObjectOpenHashMap<T, IDynamicInstance> dynamicInstances;
private final TaskEngine taskEngine;
protected int frame; protected int frame;
protected int tick; protected int tick;
public InstanceManager(TaskEngine taskEngine, MaterialManager materialManager) { public InstanceManager(MaterialManager materialManager) {
this.taskEngine = taskEngine;
this.materialManager = materialManager; this.materialManager = materialManager;
this.queuedUpdates = new HashSet<>(64); this.queuedUpdates = new HashSet<>(64);
this.queuedAdditions = new HashSet<>(64); this.queuedAdditions = new HashSet<>(64);
@ -78,7 +76,7 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
* Queued updates are processed. * Queued updates are processed.
* </p> * </p>
*/ */
public void tick(double cameraX, double cameraY, double cameraZ) { public void tick(TaskEngine taskEngine, double cameraX, double cameraY, double cameraZ) {
tick++; tick++;
processQueuedUpdates(); processQueuedUpdates();
@ -120,7 +118,7 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0) instance.tick(); if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0) instance.tick();
} }
public void beginFrame(Camera info) { public void beginFrame(TaskEngine taskEngine, Camera info) {
frame++; frame++;
processQueuedAdditions(); processQueuedAdditions();

View file

@ -16,6 +16,8 @@ import com.jozufozu.flywheel.event.RenderLayerEvent;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
/** /**
@ -29,11 +31,12 @@ public class InstanceWorld {
protected final InstanceManager<Entity> entityInstanceManager; protected final InstanceManager<Entity> entityInstanceManager;
protected final InstanceManager<BlockEntity> tileEntityInstanceManager; protected final InstanceManager<BlockEntity> tileEntityInstanceManager;
protected final BatchExecutor taskEngine; protected final ParallelTaskEngine taskEngine;
public InstanceWorld() { public InstanceWorld(LevelAccessor levelAccessor) {
Level world = (Level) levelAccessor;
this.taskEngine = new BatchExecutor(); this.taskEngine = new ParallelTaskEngine("Flywheel " + world.dimension().location());
this.taskEngine.startWorkers(); this.taskEngine.startWorkers();
FlwEngine engine = Backend.getInstance() FlwEngine engine = Backend.getInstance()
@ -42,19 +45,19 @@ public class InstanceWorld {
switch (engine) { switch (engine) {
case INSTANCING -> { case INSTANCING -> {
InstancingEngine<WorldProgram> manager = InstancingEngine.builder(Contexts.WORLD) InstancingEngine<WorldProgram> manager = InstancingEngine.builder(Contexts.WORLD)
.build(this.taskEngine); .build();
entityInstanceManager = new EntityInstanceManager(this.taskEngine, manager); entityInstanceManager = new EntityInstanceManager(manager);
tileEntityInstanceManager = new TileInstanceManager(this.taskEngine, manager); tileEntityInstanceManager = new TileInstanceManager(manager);
manager.addListener(entityInstanceManager); manager.addListener(entityInstanceManager);
manager.addListener(tileEntityInstanceManager); manager.addListener(tileEntityInstanceManager);
this.engine = manager; this.engine = manager;
} }
case BATCHING -> { case BATCHING -> {
this.engine = new BatchingEngine(this.taskEngine); this.engine = new BatchingEngine();
entityInstanceManager = new EntityInstanceManager(this.taskEngine, this.engine); entityInstanceManager = new EntityInstanceManager(this.engine);
tileEntityInstanceManager = new TileInstanceManager(this.taskEngine, this.engine); tileEntityInstanceManager = new TileInstanceManager(this.engine);
} }
default -> throw new IllegalArgumentException("Unknown engine type"); default -> throw new IllegalArgumentException("Unknown engine type");
} }
@ -91,8 +94,8 @@ public class InstanceWorld {
taskEngine.syncPoint(); taskEngine.syncPoint();
tileEntityInstanceManager.beginFrame(event.getInfo()); tileEntityInstanceManager.beginFrame(taskEngine, event.getInfo());
entityInstanceManager.beginFrame(event.getInfo()); entityInstanceManager.beginFrame(taskEngine, event.getInfo());
} }
/** /**
@ -107,8 +110,8 @@ public class InstanceWorld {
if (renderViewEntity == null) return; if (renderViewEntity == null) return;
tileEntityInstanceManager.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ()); tileEntityInstanceManager.tick(taskEngine, renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
entityInstanceManager.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ()); entityInstanceManager.tick(taskEngine, renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
} }
/** /**
@ -116,7 +119,10 @@ public class InstanceWorld {
*/ */
public void renderLayer(RenderLayerEvent event) { public void renderLayer(RenderLayerEvent event) {
taskEngine.syncPoint(); taskEngine.syncPoint();
engine.render(event); event.stack.pushPose();
event.stack.translate(-event.camX, -event.camY, -event.camZ);
engine.render(taskEngine, event);
event.stack.popPose();
} }
/** /**

View file

@ -22,7 +22,7 @@ import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber(Dist.CLIENT) @Mod.EventBusSubscriber(Dist.CLIENT)
public class InstancedRenderDispatcher { public class InstancedRenderDispatcher {
private static final WorldAttached<InstanceWorld> instanceWorlds = new WorldAttached<>($ -> new InstanceWorld()); private static final WorldAttached<InstanceWorld> instanceWorlds = new WorldAttached<>(InstanceWorld::new);
/** /**
* Call this when you want to manually run {@link AbstractInstance#update()}. * Call this when you want to manually run {@link AbstractInstance#update()}.

View file

@ -17,7 +17,7 @@ import com.jozufozu.flywheel.backend.instancing.batching.WaitGroup;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
// https://github.com/CaffeineMC/sodium-fabric/blob/5d364ed5ba63f9067fcf72a078ca310bff4db3e9/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/compile/ChunkBuilder.java // https://github.com/CaffeineMC/sodium-fabric/blob/5d364ed5ba63f9067fcf72a078ca310bff4db3e9/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/compile/ChunkBuilder.java
public class BatchExecutor implements TaskEngine { public class ParallelTaskEngine implements TaskEngine {
private static final Logger LOGGER = LogManager.getLogger("BatchExecutor"); private static final Logger LOGGER = LogManager.getLogger("BatchExecutor");
private final AtomicBoolean running = new AtomicBoolean(false); private final AtomicBoolean running = new AtomicBoolean(false);
@ -30,7 +30,10 @@ public class BatchExecutor implements TaskEngine {
private final int threadCount; private final int threadCount;
public BatchExecutor() { private final String name;
public ParallelTaskEngine(String name) {
this.name = name;
threadCount = getOptimalThreadCount(); threadCount = getOptimalThreadCount();
} }
@ -49,7 +52,7 @@ public class BatchExecutor implements TaskEngine {
for (int i = 0; i < this.threadCount; i++) { for (int i = 0; i < this.threadCount; i++) {
Thread thread = new Thread(new WorkerRunnable(), "Engine Executor " + i); Thread thread = new Thread(new WorkerRunnable(), name + " " + i);
thread.setPriority(Math.max(0, Thread.NORM_PRIORITY - 2)); thread.setPriority(Math.max(0, Thread.NORM_PRIORITY - 2));
thread.start(); thread.start();
@ -121,9 +124,9 @@ public class BatchExecutor implements TaskEngine {
Runnable job = this.jobQueue.pollFirst(); Runnable job = this.jobQueue.pollFirst();
if (job == null) { if (job == null) {
synchronized (BatchExecutor.this.jobNotifier) { synchronized (ParallelTaskEngine.this.jobNotifier) {
try { try {
BatchExecutor.this.jobNotifier.wait(); ParallelTaskEngine.this.jobNotifier.wait();
} catch (InterruptedException ignored) { } catch (InterruptedException ignored) {
} }
} }
@ -138,7 +141,7 @@ public class BatchExecutor implements TaskEngine {
} catch (Exception e) { } catch (Exception e) {
Flywheel.log.error(e); Flywheel.log.error(e);
} finally { } finally {
BatchExecutor.this.wg.done(); ParallelTaskEngine.this.wg.done();
} }
} }
@ -155,19 +158,19 @@ public class BatchExecutor implements TaskEngine {
} }
private class WorkerRunnable implements Runnable { private class WorkerRunnable implements Runnable {
private final AtomicBoolean running = BatchExecutor.this.running; private final AtomicBoolean running = ParallelTaskEngine.this.running;
@Override @Override
public void run() { public void run() {
// Run until the chunk builder shuts down // Run until the chunk builder shuts down
while (this.running.get()) { while (this.running.get()) {
Runnable job = BatchExecutor.this.getNextTask(); Runnable job = ParallelTaskEngine.this.getNextTask();
if (job == null) { if (job == null) {
continue; continue;
} }
BatchExecutor.this.processTask(job); ParallelTaskEngine.this.processTask(job);
} }
} }

View file

@ -3,15 +3,15 @@ package com.jozufozu.flywheel.backend.instancing;
import com.jozufozu.flywheel.event.RenderLayerEvent; import com.jozufozu.flywheel.event.RenderLayerEvent;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.renderer.MultiBufferSource;
public interface RenderDispatcher { public interface RenderDispatcher {
/** /**
* Render every model for every material. * Render every model for every material.
* *
* @param taskEngine
* @param event Context for rendering. * @param event Context for rendering.
*/ */
void render(RenderLayerEvent event); void render(TaskEngine taskEngine, RenderLayerEvent event);
/** /**
* Maintain the integer origin coordinate to be within a certain distance from the camera in all directions. * Maintain the integer origin coordinate to be within a certain distance from the camera in all directions.

View file

@ -2,11 +2,11 @@ package com.jozufozu.flywheel.backend.instancing;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class ImmediateExecutor implements TaskEngine { public class SerialTaskEngine implements TaskEngine {
public static final ImmediateExecutor INSTANCE = new ImmediateExecutor(); public static final SerialTaskEngine INSTANCE = new SerialTaskEngine();
private ImmediateExecutor() { private SerialTaskEngine() {
} }
@Override @Override

View file

@ -0,0 +1,79 @@
package com.jozufozu.flywheel.backend.instancing;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import com.jozufozu.flywheel.backend.model.BufferBuilderHack;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.mojang.blaze3d.platform.MemoryTracker;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderType;
public class SuperBufferSource {
protected final Map<RenderType, DrawBuffer> buffers = new HashMap<>();
private final BufferBuilder scratch;
public SuperBufferSource() {
scratch = new BufferBuilder(8);
((BufferBuilderHack) scratch).freeBuffer();
}
public DirectVertexConsumer getBuffer(RenderType renderType, int vertexCount) {
return buffers.computeIfAbsent(renderType, DrawBuffer::new)
.begin(vertexCount);
}
public void endBatch() {
// TODO: when/if this causes trouble with shaders, try to inject our BufferBuilders
// into the RenderBuffers from context.
BufferBuilderHack hack = (BufferBuilderHack) scratch;
for (Map.Entry<RenderType, DrawBuffer> entry : buffers.entrySet()) {
DrawBuffer builder = entry.getValue();
if (builder.expectedVertices > 0) {
RenderType type = entry.getKey();
hack.hackBegin(builder.backingBuffer, type.format(), builder.expectedVertices);
type.end(scratch, 0, 0, 0);
builder.expectedVertices = 0;
}
}
}
private static class DrawBuffer {
private final RenderType type;
private ByteBuffer backingBuffer;
private int expectedVertices;
public DrawBuffer(RenderType type) {
this.type = type;
}
public DirectVertexConsumer begin(int vertexCount) {
this.expectedVertices = vertexCount;
VertexFormat format = type.format();
int byteSize = format.getVertexSize() * vertexCount;
if (backingBuffer == null) {
backingBuffer = MemoryTracker.create(byteSize);
} if (byteSize > backingBuffer.capacity()) {
backingBuffer = MemoryTracker.resize(backingBuffer, byteSize);
}
return new DirectVertexConsumer(backingBuffer, format, vertexCount);
}
}
}

View file

@ -7,13 +7,11 @@ import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.api.struct.Batched; import com.jozufozu.flywheel.api.struct.Batched;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.instancing.SuperBufferSource;
import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.model.DirectBufferBuilder;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
public class BatchedMaterialGroup implements MaterialGroup { public class BatchedMaterialGroup implements MaterialGroup {
@ -36,17 +34,8 @@ public class BatchedMaterialGroup implements MaterialGroup {
} }
} }
public void render(PoseStack stack, MultiBufferSource source, TaskEngine pool) { public void render(PoseStack stack, SuperBufferSource source, TaskEngine pool) {
VertexConsumer buffer = source.getBuffer(state);
if (buffer instanceof DirectBufferBuilder direct) {
renderParallel(stack, pool, direct);
} else {
renderSerial(stack, buffer);
}
}
private void renderParallel(PoseStack stack, TaskEngine pool, DirectBufferBuilder direct) {
int vertexCount = 0; int vertexCount = 0;
for (BatchedMaterial<?> material : materials.values()) { for (BatchedMaterial<?> material : materials.values()) {
for (CPUInstancer<?> instancer : material.models.values()) { for (CPUInstancer<?> instancer : material.models.values()) {
@ -55,7 +44,7 @@ public class BatchedMaterialGroup implements MaterialGroup {
} }
} }
DirectVertexConsumer consumer = direct.intoDirectConsumer(vertexCount); DirectVertexConsumer consumer = source.getBuffer(state, vertexCount);
// avoids rendering garbage, but doesn't fix the issue of some instances not being buffered // avoids rendering garbage, but doesn't fix the issue of some instances not being buffered
consumer.memSetZero(); consumer.memSetZero();
@ -74,12 +63,6 @@ public class BatchedMaterialGroup implements MaterialGroup {
} }
} }
private void renderSerial(PoseStack stack, VertexConsumer consumer) {
for (BatchedMaterial<?> value : materials.values()) {
value.setupAndRenderInto(stack, consumer);
}
}
public void clear() { public void clear() {
materials.values().forEach(BatchedMaterial::clear); materials.values().forEach(BatchedMaterial::clear);
} }

View file

@ -6,6 +6,7 @@ import java.util.Map;
import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.backend.RenderLayer; import com.jozufozu.flywheel.backend.RenderLayer;
import com.jozufozu.flywheel.backend.instancing.SuperBufferSource;
import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.event.RenderLayerEvent; import com.jozufozu.flywheel.event.RenderLayerEvent;
@ -21,19 +22,17 @@ import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
public class BatchingEngine implements Engine, MultiBufferSource { public class BatchingEngine implements Engine {
protected final Map<RenderType, BufferBuilder> buffers = new HashMap<>(); private final Map<RenderLayer, Map<RenderType, BatchedMaterialGroup>> layers;
protected final Map<RenderLayer, Map<RenderType, BatchedMaterialGroup>> layers; private final SuperBufferSource superBufferSource = new SuperBufferSource();
protected final TaskEngine taskEngine;
public BatchingEngine(TaskEngine taskEngine) { public BatchingEngine() {
this.layers = new EnumMap<>(RenderLayer.class); this.layers = new EnumMap<>(RenderLayer.class);
for (RenderLayer value : RenderLayer.values()) { for (RenderLayer value : RenderLayer.values()) {
layers.put(value, new HashMap<>()); layers.put(value, new HashMap<>());
} }
this.taskEngine = taskEngine;
} }
@Override @Override
@ -47,21 +46,15 @@ public class BatchingEngine implements Engine, MultiBufferSource {
} }
@Override @Override
public void render(RenderLayerEvent event) { public void render(TaskEngine taskEngine, RenderLayerEvent event) {
PoseStack stack = event.stack;
stack.pushPose(); Map<RenderType, BatchedMaterialGroup> groups = layers.get(event.getLayer());
for (BatchedMaterialGroup group : groups.values()) {
stack.translate(-event.camX, -event.camY, -event.camZ); group.render(event.stack, superBufferSource, taskEngine);
for (BatchedMaterialGroup group : layers.get(event.getLayer()).values()) {
group.render(stack, this, taskEngine);
} }
taskEngine.syncPoint(); taskEngine.syncPoint();
stack.popPose();
// FIXME: this probably breaks some vanilla stuff but it works much better for flywheel // FIXME: this probably breaks some vanilla stuff but it works much better for flywheel
Matrix4f mat = new Matrix4f(); Matrix4f mat = new Matrix4f();
mat.setIdentity(); mat.setIdentity();
@ -71,20 +64,7 @@ public class BatchingEngine implements Engine, MultiBufferSource {
Lighting.setupLevel(mat); Lighting.setupLevel(mat);
} }
// TODO: when/if this causes trouble with shaders, try to inject our BufferBuilders superBufferSource.endBatch();
// into the RenderBuffers from context.
buffers.forEach((type, builder) -> type.end(builder, 0, 0, 0));
}
@Override
public VertexConsumer getBuffer(RenderType renderType) {
BufferBuilder builder = buffers.computeIfAbsent(renderType, type -> new BufferBuilder(type.bufferSize()));
if (!builder.building()) {
builder.begin(renderType.mode(), renderType.format());
}
return builder;
} }
@Override @Override

View file

@ -3,7 +3,6 @@ package com.jozufozu.flywheel.backend.instancing.entity;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance; import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.backend.instancing.BatchExecutor;
import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.TaskEngine;
@ -15,8 +14,8 @@ import net.minecraft.world.level.Level;
public class EntityInstanceManager extends InstanceManager<Entity> { public class EntityInstanceManager extends InstanceManager<Entity> {
public EntityInstanceManager(TaskEngine executor, MaterialManager materialManager) { public EntityInstanceManager(MaterialManager materialManager) {
super(executor, materialManager); super(materialManager);
} }
@Override @Override

View file

@ -10,7 +10,6 @@ import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.instancing.ImmediateExecutor;
import com.jozufozu.flywheel.backend.RenderLayer; import com.jozufozu.flywheel.backend.RenderLayer;
import com.jozufozu.flywheel.backend.gl.GlVertexArray; import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
@ -22,7 +21,6 @@ import com.jozufozu.flywheel.util.WeakHashSet;
import com.mojang.math.Matrix4f; import com.mojang.math.Matrix4f;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
@ -35,7 +33,6 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
protected BlockPos originCoordinate = BlockPos.ZERO; protected BlockPos originCoordinate = BlockPos.ZERO;
protected final TaskEngine taskEngine;
protected final WorldContext<P> context; protected final WorldContext<P> context;
protected final GroupFactory<P> groupFactory; protected final GroupFactory<P> groupFactory;
protected final boolean ignoreOriginCoordinate; protected final boolean ignoreOriginCoordinate;
@ -45,15 +42,14 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
private final WeakHashSet<OriginShiftListener> listeners; private final WeakHashSet<OriginShiftListener> listeners;
public InstancingEngine(WorldContext<P> context, TaskEngine taskEngine) { public InstancingEngine(WorldContext<P> context, TaskEngine taskEngine) {
this(taskEngine, context, InstancedMaterialGroup::new, false); this(context, InstancedMaterialGroup::new, false);
} }
public static <P extends WorldProgram> Builder<P> builder(WorldContext<P> context) { public static <P extends WorldProgram> Builder<P> builder(WorldContext<P> context) {
return new Builder<>(context); return new Builder<>(context);
} }
public InstancingEngine(TaskEngine taskEngine, WorldContext<P> context, GroupFactory<P> groupFactory, boolean ignoreOriginCoordinate) { public InstancingEngine(WorldContext<P> context, GroupFactory<P> groupFactory, boolean ignoreOriginCoordinate) {
this.taskEngine = taskEngine;
this.context = context; this.context = context;
this.ignoreOriginCoordinate = ignoreOriginCoordinate; this.ignoreOriginCoordinate = ignoreOriginCoordinate;
@ -82,7 +78,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
* Render every model for every material. * Render every model for every material.
*/ */
@Override @Override
public void render(RenderLayerEvent event) { public void render(TaskEngine taskEngine, RenderLayerEvent event) {
double camX; double camX;
double camY; double camY;
double camZ; double camZ;
@ -205,11 +201,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
} }
public InstancingEngine<P> build() { public InstancingEngine<P> build() {
return build(ImmediateExecutor.INSTANCE); return new InstancingEngine<>(context, groupFactory, ignoreOriginCoordinate);
}
public InstancingEngine<P> build(TaskEngine taskEngine) {
return new InstancingEngine<>(taskEngine, context, groupFactory, ignoreOriginCoordinate);
} }
} }
} }

View file

@ -3,7 +3,6 @@ package com.jozufozu.flywheel.backend.instancing.tile;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance; import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.backend.instancing.BatchExecutor;
import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry; import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.TaskEngine;
@ -15,8 +14,8 @@ import net.minecraft.world.level.block.entity.BlockEntity;
public class TileInstanceManager extends InstanceManager<BlockEntity> { public class TileInstanceManager extends InstanceManager<BlockEntity> {
public TileInstanceManager(TaskEngine executor, MaterialManager materialManager) { public TileInstanceManager(MaterialManager materialManager) {
super(executor, materialManager); super(materialManager);
} }
@Override @Override

View file

@ -0,0 +1,16 @@
package com.jozufozu.flywheel.backend.model;
import java.nio.ByteBuffer;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.VertexFormat;
/**
* Duck interface used on {@link BufferBuilder} to provide lower level access to the backing memory.
*/
public interface BufferBuilderHack {
void freeBuffer();
void hackBegin(ByteBuffer buffer, VertexFormat format, int vertexCount);
}

View file

@ -1,20 +0,0 @@
package com.jozufozu.flywheel.backend.model;
import com.mojang.blaze3d.vertex.BufferBuilder;
/**
* Duck interface used on {@link BufferBuilder} to provide lower level access to the backing memory.
*/
public interface DirectBufferBuilder {
/**
* Create a DirectVertexConsumer from this BufferBuilder.
*
* <p>
* After this function returns, the internal state of the BufferBuilder will be as if
* {@link BufferBuilder#endVertex()} was called vertexCount times. It is up to the callee
* to actually populate the BufferBuilder with vertices using the returned value.
* </p>
*/
DirectVertexConsumer intoDirectConsumer(int vertexCount);
}

View file

@ -13,7 +13,7 @@ import com.mojang.blaze3d.vertex.VertexFormatElement;
/** /**
* An unsafe vertex consumer allowing for unchecked writes into a ByteBuffer. * An unsafe vertex consumer allowing for unchecked writes into a ByteBuffer.
* *
* @see DirectBufferBuilder * @see BufferBuilderHack
*/ */
public class DirectVertexConsumer implements VertexConsumer { public class DirectVertexConsumer implements VertexConsumer {
public final VertexFormat format; public final VertexFormat format;

View file

@ -1,7 +1,6 @@
package com.jozufozu.flywheel.core.crumbling; package com.jozufozu.flywheel.core.crumbling;
import com.jozufozu.flywheel.api.MaterialManager; import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.backend.instancing.ImmediateExecutor;
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager; import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -9,7 +8,7 @@ import net.minecraft.core.BlockPos;
public class CrumblingInstanceManager extends TileInstanceManager { public class CrumblingInstanceManager extends TileInstanceManager {
public CrumblingInstanceManager(MaterialManager materialManager) { public CrumblingInstanceManager(MaterialManager materialManager) {
super(ImmediateExecutor.INSTANCE, materialManager); super(materialManager);
} }
@Override @Override

View file

@ -6,6 +6,7 @@ import java.util.SortedSet;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit; import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine;
import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.core.Contexts; import com.jozufozu.flywheel.core.Contexts;
@ -74,9 +75,9 @@ public class CrumblingRenderer {
if (_currentLayer != null) { if (_currentLayer != null) {
stage.getValue().forEach(instanceManager::add); stage.getValue().forEach(instanceManager::add);
instanceManager.beginFrame(info); instanceManager.beginFrame(SerialTaskEngine.INSTANCE, info);
materials.render(event); materials.render(SerialTaskEngine.INSTANCE, event);
instanceManager.invalidate(); instanceManager.invalidate();
} }

View file

@ -5,29 +5,34 @@ import java.nio.ByteBuffer;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.lwjgl.system.MemoryUtil;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import com.jozufozu.flywheel.backend.instancing.SuperBufferSource;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.jozufozu.flywheel.backend.model.DirectBufferBuilder; import com.jozufozu.flywheel.backend.model.BufferBuilderHack;
import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexFormatElement; import com.mojang.blaze3d.vertex.VertexFormatElement;
@Mixin(BufferBuilder.class) @Mixin(BufferBuilder.class)
public abstract class BufferBuilderMixin implements DirectBufferBuilder { public abstract class BufferBuilderMixin implements BufferBuilderHack {
@Shadow @Shadow
private ByteBuffer buffer; private ByteBuffer buffer;
@Shadow
private boolean building;
@Shadow
public abstract void begin(VertexFormat.Mode p_166780_, VertexFormat p_166781_);
@Shadow
private VertexFormat.Mode mode;
@Shadow @Shadow
private VertexFormat format; private VertexFormat format;
@Shadow
protected abstract void ensureCapacity(int p_85723_);
@Shadow
private int vertices;
@Shadow @Shadow
@Nullable @Nullable
private VertexFormatElement currentElement; private VertexFormatElement currentElement;
@ -36,24 +41,26 @@ public abstract class BufferBuilderMixin implements DirectBufferBuilder {
private int elementIndex; private int elementIndex;
@Shadow @Shadow
private int nextElementByte; private int vertices;
@Override @Override
@Nonnull public void freeBuffer() {
public DirectVertexConsumer intoDirectConsumer(int vertexCount) { if (this.buffer != null) {
int bytes = vertexCount * format.getVertexSize(); MemoryUtil.memFree(this.buffer);
// ensure we have capacity for one extra vertex, BufferBuilder does this on #endVertex this.buffer = null;
ensureCapacity(bytes + format.getVertexSize()); }
}
DirectVertexConsumer consumer = new DirectVertexConsumer(this.buffer, this.format, vertexCount); @Override
public void hackBegin(@Nonnull ByteBuffer buffer, @Nonnull VertexFormat format, int vertexCount) {
this.building = true;
this.mode = VertexFormat.Mode.QUADS;
this.vertices += vertexCount; this.buffer = buffer;
this.currentElement = format.getElements() this.format = format;
.get(0); this.vertices = vertexCount;
this.currentElement = this.format.getElements().get(0);
this.elementIndex = 0; this.elementIndex = 0;
this.nextElementByte += bytes;
this.buffer.position(this.buffer.position() + bytes);
return consumer;
} }
} }