Add batching stage support

This commit is contained in:
PepperCode1 2022-08-22 20:00:21 -07:00
parent d3c280d298
commit d7613fc5e4
7 changed files with 151 additions and 63 deletions

View file

@ -1,8 +1,10 @@
package com.jozufozu.flywheel.backend.instancing.batching; package com.jozufozu.flywheel.backend.instancing.batching;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.Set; import java.util.Set;
import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.extension.BufferBuilderExtension; import com.jozufozu.flywheel.extension.BufferBuilderExtension;
import com.jozufozu.flywheel.extension.RenderTypeExtension; import com.jozufozu.flywheel.extension.RenderTypeExtension;
import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.BufferBuilder;
@ -10,7 +12,6 @@ import com.mojang.blaze3d.vertex.BufferBuilder;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
public class BatchingDrawTracker { public class BatchingDrawTracker {
private final Set<RenderType> activeTypes = new HashSet<>(); private final Set<RenderType> activeTypes = new HashSet<>();
private final BufferBuilder scratch; private final BufferBuilder scratch;
@ -20,13 +21,17 @@ public class BatchingDrawTracker {
((BufferBuilderExtension) scratch).flywheel$freeBuffer(); ((BufferBuilderExtension) scratch).flywheel$freeBuffer();
} }
public DrawBuffer getBuffer(RenderType renderType) { public DrawBuffer getBuffer(RenderType renderType, RenderStage stage) {
return getBufferSet(renderType).getBuffer(stage);
}
public DrawBufferSet getBufferSet(RenderType renderType) {
activeTypes.add(renderType); activeTypes.add(renderType);
return RenderTypeExtension.getDrawBuffer(renderType); return RenderTypeExtension.getDrawBufferSet(renderType);
} }
/** /**
* Draw and reset the DrawBuffer for the given RenderType. * Draw and reset all DrawBuffers for the given RenderType.
* @param renderType The RenderType to draw. * @param renderType The RenderType to draw.
*/ */
public void draw(RenderType renderType) { public void draw(RenderType renderType) {
@ -36,7 +41,27 @@ public class BatchingDrawTracker {
} }
/** /**
* Draws all active DrawBuffers and reset them. * Draw and reset all DrawBuffers for the given RenderStage.
* @param stage The RenderStage to draw.
*/
public void draw(RenderStage stage) {
Iterator<RenderType> iterator = activeTypes.iterator();
while (iterator.hasNext()) {
RenderType renderType = iterator.next();
DrawBufferSet bufferSet = RenderTypeExtension.getDrawBufferSet(renderType);
DrawBuffer buffer = bufferSet.deactivateBuffer(stage);
if (buffer == null) {
continue;
}
if (bufferSet.getActiveStagesView().isEmpty()) {
iterator.remove();
}
_draw(buffer, renderType);
}
}
/**
* Draw and reset all active DrawBuffers.
*/ */
public void drawAll() { public void drawAll() {
for (RenderType renderType : activeTypes) { for (RenderType renderType : activeTypes) {
@ -47,8 +72,14 @@ public class BatchingDrawTracker {
} }
private void _draw(RenderType renderType) { private void _draw(RenderType renderType) {
DrawBuffer buffer = RenderTypeExtension.getDrawBuffer(renderType); DrawBufferSet bufferSet = RenderTypeExtension.getDrawBufferSet(renderType);
for (RenderStage stage : bufferSet.getActiveStagesView()) {
DrawBuffer buffer = bufferSet.deactivateBuffer(stage);
_draw(buffer, renderType);
}
}
private void _draw(DrawBuffer buffer, RenderType renderType) {
if (buffer.hasVertices()) { if (buffer.hasVertices()) {
BufferBuilderExtension scratch = (BufferBuilderExtension) this.scratch; BufferBuilderExtension scratch = (BufferBuilderExtension) this.scratch;
buffer.inject(scratch); buffer.inject(scratch);
@ -59,15 +90,14 @@ public class BatchingDrawTracker {
} }
/** /**
* Resets all DrawBuffers to 0 vertices. * Reset all active DrawBuffers.
*/ */
public void reset() { public void reset() {
for (RenderType type : activeTypes) { for (RenderType type : activeTypes) {
RenderTypeExtension.getDrawBuffer(type) RenderTypeExtension.getDrawBufferSet(type)
.reset(); .reset();
} }
activeTypes.clear(); activeTypes.clear();
} }
} }

View file

@ -23,7 +23,6 @@ import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
public class BatchingEngine implements Engine { public class BatchingEngine implements Engine {
protected final BatchingTransformManager transformManager = new BatchingTransformManager(); protected final BatchingTransformManager transformManager = new BatchingTransformManager();
protected final BatchingDrawTracker drawTracker = new BatchingDrawTracker(); protected final BatchingDrawTracker drawTracker = new BatchingDrawTracker();
protected final Map<StructType<?>, CPUInstancerFactory<?>> factories = new HashMap<>(); protected final Map<StructType<?>, CPUInstancerFactory<?>> factories = new HashMap<>();
@ -54,41 +53,38 @@ public class BatchingEngine implements Engine {
} }
public void submitTasks(TaskEngine taskEngine, PoseStack stack, ClientLevel level) { public void submitTasks(TaskEngine taskEngine, PoseStack stack, ClientLevel level) {
BatchingTransformManager.TransformSet transformSet = transformManager.get(RenderStage.AFTER_FINAL_END_BATCH); for (var transformSetEntry : transformManager.getTransformSetsView().entrySet()) {
for (var entry : transformSet) { var stage = transformSetEntry.getKey();
var renderType = entry.getKey(); var transformSet = transformSetEntry.getValue();
var transformCalls = entry.getValue();
int vertices = 0; for (var entry : transformSet) {
for (var transformCall : transformCalls) { var renderType = entry.getKey();
vertices += transformCall.getTotalVertexCount(); var transformCalls = entry.getValue();
}
if (vertices == 0) { int vertices = 0;
continue; for (var transformCall : transformCalls) {
} vertices += transformCall.getTotalVertexCount();
}
DrawBuffer buffer = drawTracker.getBuffer(renderType); if (vertices == 0) {
buffer.prepare(vertices); continue;
}
int startVertex = 0; DrawBuffer buffer = drawTracker.getBuffer(renderType, stage);
for (var transformCall : transformCalls) { buffer.prepare(vertices);
transformCall.submitTasks(taskEngine, buffer, startVertex, stack, level);
startVertex += transformCall.getTotalVertexCount(); int startVertex = 0;
for (var transformCall : transformCalls) {
transformCall.submitTasks(taskEngine, buffer, startVertex, stack, level);
startVertex += transformCall.getTotalVertexCount();
}
} }
} }
} }
@Override @Override
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) { public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
// FIXME: properly support material stages drawTracker.draw(stage);
// This also breaks block outlines on batched block entities
// and makes translucent blocks occlude everything Flywheel renders
if (stage != RenderStage.AFTER_FINAL_END_BATCH) {
return;
}
drawTracker.drawAll();
} }
@Override @Override

View file

@ -19,7 +19,6 @@ import com.jozufozu.flywheel.core.model.Model;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
public class BatchingTransformManager { public class BatchingTransformManager {
private final List<UninitializedModel> uninitializedModels = new ArrayList<>(); private final List<UninitializedModel> uninitializedModels = new ArrayList<>();
private final List<CPUInstancer<?>> allInstancers = new ArrayList<>(); private final List<CPUInstancer<?>> allInstancers = new ArrayList<>();
private final Map<RenderStage, TransformSet> transformSets = new EnumMap<>(RenderStage.class); private final Map<RenderStage, TransformSet> transformSets = new EnumMap<>(RenderStage.class);
@ -60,15 +59,13 @@ public class BatchingTransformManager {
var material = transformCall.getMaterial(); var material = transformCall.getMaterial();
var renderType = material.getBatchingRenderType(); var renderType = material.getBatchingRenderType();
// transformSets.computeIfAbsent(material.getRenderStage(), TransformSet::new) transformSets.computeIfAbsent(material.getRenderStage(), TransformSet::new)
transformSets.computeIfAbsent(RenderStage.AFTER_FINAL_END_BATCH, TransformSet::new)
.put(renderType, transformCall); .put(renderType, transformCall);
} }
allInstancers.add(instancer); allInstancers.add(instancer);
} }
public static class TransformSet implements Iterable<Map.Entry<RenderType, Collection<TransformCall<?>>>> { public static class TransformSet implements Iterable<Map.Entry<RenderType, Collection<TransformCall<?>>>> {
public static final TransformSet EMPTY = new TransformSet(ImmutableListMultimap.of()); public static final TransformSet EMPTY = new TransformSet(ImmutableListMultimap.of());
final ListMultimap<RenderType, TransformCall<?>> transformCalls; final ListMultimap<RenderType, TransformCall<?>> transformCalls;

View file

@ -4,8 +4,6 @@ import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import com.jozufozu.flywheel.api.vertex.ReusableVertexList; import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import com.jozufozu.flywheel.api.vertex.VertexListProvider; import com.jozufozu.flywheel.api.vertex.VertexListProvider;
import com.jozufozu.flywheel.backend.memory.MemoryBlock; import com.jozufozu.flywheel.backend.memory.MemoryBlock;
@ -31,11 +29,10 @@ public class DrawBuffer {
private boolean prepared; private boolean prepared;
private int vertexCount; private int vertexCount;
@ApiStatus.Internal DrawBuffer(VertexFormat format, int stride, VertexListProvider provider) {
public DrawBuffer(VertexFormat format) {
this.format = format; this.format = format;
stride = format.getVertexSize(); this.stride = stride;
provider = VertexListProvider.get(format); this.provider = provider;
ALL.add(this); ALL.add(this);
} }
@ -120,6 +117,9 @@ public class DrawBuffer {
} }
public void free() { public void free() {
if (memory == null) {
return;
}
memory.free(); memory.free();
memory = null; memory = null;
buffer = null; buffer = null;

View file

@ -0,0 +1,66 @@
package com.jozufozu.flywheel.backend.instancing.batching;
import java.util.Collections;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
import com.mojang.blaze3d.vertex.VertexFormat;
public class DrawBufferSet {
private final VertexFormat format;
private final int stride;
private final VertexListProvider provider;
private final Map<RenderStage, DrawBuffer> buffers = new EnumMap<>(RenderStage.class);
private final Set<RenderStage> activeStages = EnumSet.noneOf(RenderStage.class);
private final Set<RenderStage> activeStagesView = Collections.unmodifiableSet(activeStages);
@ApiStatus.Internal
public DrawBufferSet(VertexFormat format) {
this.format = format;
stride = format.getVertexSize();
provider = VertexListProvider.get(format);
}
public Set<RenderStage> getActiveStagesView() {
return activeStagesView;
}
public DrawBuffer getBuffer(RenderStage stage) {
activeStages.add(stage);
return buffers.computeIfAbsent(stage, $ -> createBuffer());
}
@Nullable
public DrawBuffer deactivateBuffer(RenderStage stage) {
if (activeStages.remove(stage)) {
return buffers.get(stage);
}
return null;
}
public void reset(RenderStage stage) {
if (activeStages.remove(stage)) {
buffers.get(stage).reset();
}
}
public void reset() {
for (RenderStage stage : activeStages) {
buffers.get(stage).reset();
}
activeStages.clear();
}
private DrawBuffer createBuffer() {
return new DrawBuffer(format, stride, provider);
}
}

View file

@ -1,27 +1,26 @@
package com.jozufozu.flywheel.extension; package com.jozufozu.flywheel.extension;
import com.jozufozu.flywheel.backend.instancing.batching.DrawBuffer; import com.jozufozu.flywheel.backend.instancing.batching.DrawBufferSet;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
/** /**
* Duck interface to make RenderType store a DrawBuffer. * Duck interface to make RenderType store a DrawBufferSet.
* *
* @see RenderType * @see RenderType
*/ */
public interface RenderTypeExtension { public interface RenderTypeExtension {
/**
* @return The DrawBufferSet associated with this RenderType.
*/
DrawBufferSet flywheel$getDrawBufferSet();
/** /**
* @return The DrawBuffer associated with this RenderType. * Helper function to cast a RenderType to a RenderTypeExtension and get its DrawBufferSet.
* @param type The RenderType to get the DrawBufferSet from.
* @return The DrawBufferSet associated with the given RenderType.
*/ */
DrawBuffer flywheel$getDrawBuffer(); static DrawBufferSet getDrawBufferSet(RenderType type) {
return ((RenderTypeExtension) type).flywheel$getDrawBufferSet();
/**
* Helper function to cast a RenderType to a RenderTypeExtension and get its DrawBuffer.
* @param type The RenderType to get the DrawBuffer from.
* @return The DrawBuffer associated with the given RenderType.
*/
static DrawBuffer getDrawBuffer(RenderType type) {
return ((RenderTypeExtension) type).flywheel$getDrawBuffer();
} }
} }

View file

@ -4,7 +4,7 @@ import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import com.jozufozu.flywheel.backend.instancing.batching.DrawBuffer; import com.jozufozu.flywheel.backend.instancing.batching.DrawBufferSet;
import com.jozufozu.flywheel.extension.RenderTypeExtension; import com.jozufozu.flywheel.extension.RenderTypeExtension;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
@ -12,14 +12,14 @@ import net.minecraft.client.renderer.RenderType;
@Mixin(RenderType.class) @Mixin(RenderType.class)
public class RenderTypeMixin implements RenderTypeExtension { public class RenderTypeMixin implements RenderTypeExtension {
@Unique @Unique
private DrawBuffer flywheel$drawBuffer; private DrawBufferSet flywheel$drawBufferSet;
@Override @Override
@NotNull @NotNull
public DrawBuffer flywheel$getDrawBuffer() { public DrawBufferSet flywheel$getDrawBufferSet() {
if (flywheel$drawBuffer == null) { if (flywheel$drawBufferSet == null) {
flywheel$drawBuffer = new DrawBuffer(((RenderType) (Object) this).format()); flywheel$drawBufferSet = new DrawBufferSet(((RenderType) (Object) this).format());
} }
return flywheel$drawBuffer; return flywheel$drawBufferSet;
} }
} }