mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-07 12:56:31 +01:00
Add batching stage support
This commit is contained in:
parent
d3c280d298
commit
d7613fc5e4
7 changed files with 151 additions and 63 deletions
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue