Merge branch '1.18/batching-stage-support' into 1.18/next

Conflicts:
	src/main/java/com/jozufozu/flywheel/backend/Backend.java
	src/main/java/com/jozufozu/flywheel/backend/instancing/batching/DrawBuffer.java
	src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java
	src/main/java/com/jozufozu/flywheel/config/FlwConfig.java
	src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java
	src/main/java/com/jozufozu/flywheel/core/model/SimpleMesh.java
This commit is contained in:
PepperCode1 2022-09-16 13:54:45 -07:00
commit d3d5797fd3
46 changed files with 605 additions and 321 deletions

View file

@ -97,7 +97,7 @@ public class Flywheel {
forgeEventBus.addListener(ForgeEvents::unloadWorld); forgeEventBus.addListener(ForgeEvents::unloadWorld);
forgeEventBus.addListener(ForgeEvents::tickLight); forgeEventBus.addListener(ForgeEvents::tickLight);
forgeEventBus.addListener(EventPriority.LOWEST, RenderWork::onRenderWorldLast); forgeEventBus.addListener(EventPriority.LOWEST, RenderWork::onRenderLevelLast);
modEventBus.addListener(PartialModel::onModelRegistry); modEventBus.addListener(PartialModel::onModelRegistry);
modEventBus.addListener(PartialModel::onModelBake); modEventBus.addListener(PartialModel::onModelBake);

View file

@ -1,13 +1,13 @@
package com.jozufozu.flywheel.api; package com.jozufozu.flywheel.api;
/** /**
* A marker interface custom worlds can override to indicate * A marker interface custom levels can override to indicate
* that block entities and entities inside the world should * that block entities and entities inside the level should
* render with Flywheel. * render with Flywheel.
* *
* {@link net.minecraft.client.Minecraft#level Minecraft#level} is special cased and will support Flywheel by default. * {@link net.minecraft.client.Minecraft#level Minecraft#level} is special cased and will support Flywheel by default.
*/ */
public interface FlywheelWorld { public interface FlywheelLevel {
default boolean supportsFlywheel() { default boolean supportsFlywheel() {
return true; return true;
} }

View file

@ -5,7 +5,7 @@ public interface ReusableVertexList extends MutableVertexList {
void ptr(long ptr); void ptr(long ptr);
void shiftPtr(int vertices); int vertexStride();
void setVertexCount(int vertexCount); void vertexCount(int vertexCount);
} }

View file

@ -72,18 +72,18 @@ public interface VertexList extends PointSet {
} }
default void writeAll(MutableVertexList dst) { default void writeAll(MutableVertexList dst) {
write(dst, 0, 0, getVertexCount()); write(dst, 0, 0, vertexCount());
} }
int getVertexCount(); int vertexCount();
default boolean isEmpty() { default boolean isEmpty() {
return getVertexCount() == 0; return vertexCount() == 0;
} }
@Override @Override
default int size() { default int size() {
return getVertexCount(); return vertexCount();
} }
@Override @Override

View file

@ -3,7 +3,7 @@ package com.jozufozu.flywheel.backend;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import com.jozufozu.flywheel.api.FlywheelWorld; import com.jozufozu.flywheel.api.FlywheelLevel;
import com.jozufozu.flywheel.backend.instancing.ParallelTaskEngine; import com.jozufozu.flywheel.backend.instancing.ParallelTaskEngine;
import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.core.BackendTypes; import com.jozufozu.flywheel.core.BackendTypes;
@ -60,21 +60,21 @@ public class Backend {
return TYPE != BackendTypes.OFF; return TYPE != BackendTypes.OFF;
} }
public static boolean canUseInstancing(@Nullable Level world) { public static boolean canUseInstancing(@Nullable Level level) {
return isOn() && isFlywheelWorld(world); return isOn() && isFlywheelLevel(level);
} }
/** /**
* Used to avoid calling Flywheel functions on (fake) worlds that don't specifically support it. * Used to avoid calling Flywheel functions on (fake) levels that don't specifically support it.
*/ */
public static boolean isFlywheelWorld(@Nullable LevelAccessor world) { public static boolean isFlywheelLevel(@Nullable LevelAccessor level) {
if (world == null) return false; if (level == null) return false;
if (!world.isClientSide()) return false; if (!level.isClientSide()) return false;
if (world instanceof FlywheelWorld && ((FlywheelWorld) world).supportsFlywheel()) return true; if (level instanceof FlywheelLevel && ((FlywheelLevel) level).supportsFlywheel()) return true;
return world == Minecraft.getInstance().level; return level == Minecraft.getInstance().level;
} }
public static boolean isGameActive() { public static boolean isGameActive() {

View file

@ -109,9 +109,9 @@ public class Loader implements ResourceManagerReloadListener {
throw new ShaderLoadingException("Compilation failed"); throw new ShaderLoadingException("Compilation failed");
} }
ClientLevel world = Minecraft.getInstance().level; ClientLevel level = Minecraft.getInstance().level;
if (Backend.canUseInstancing(world)) { if (Backend.canUseInstancing(level)) {
InstancedRenderDispatcher.resetInstanceWorld(world); InstancedRenderDispatcher.resetInstanceLevel(level);
} }
} }

View file

@ -6,12 +6,11 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import net.minecraftforge.client.event.RenderLevelLastEvent; import net.minecraftforge.client.event.RenderLevelLastEvent;
public class RenderWork { public class RenderWork {
private static final Queue<Runnable> runs = new ConcurrentLinkedQueue<>(); private static final Queue<Runnable> RUNS = new ConcurrentLinkedQueue<>();
public static void onRenderLevelLast(RenderLevelLastEvent event) {
public static void onRenderWorldLast(RenderLevelLastEvent event) { while (!RUNS.isEmpty()) {
while (!runs.isEmpty()) { RUNS.remove()
runs.remove()
.run(); .run();
} }
} }
@ -20,6 +19,6 @@ public class RenderWork {
* Queue work to be executed at the end of a frame * Queue work to be executed at the end of a frame
*/ */
public static void enqueue(Runnable run) { public static void enqueue(Runnable run) {
runs.add(run); RUNS.add(run);
} }
} }

View file

@ -7,13 +7,12 @@ import com.mojang.blaze3d.platform.GlStateManager;
* Tracks bound buffers/vbos because GlStateManager doesn't do that for us. * Tracks bound buffers/vbos because GlStateManager doesn't do that for us.
*/ */
public class GlStateTracker { public class GlStateTracker {
private static final int[] BUFFERS = new int[GlBufferType.values().length];
private static final int[] buffers = new int[GlBufferType.values().length];
private static int vao; private static int vao;
private static int program; private static int program;
public static int getBuffer(GlBufferType type) { public static int getBuffer(GlBufferType type) {
return buffers[type.ordinal()]; return BUFFERS[type.ordinal()];
} }
public static int getVertexArray() { public static int getVertexArray() {
@ -25,7 +24,7 @@ public class GlStateTracker {
} }
public static void _setBuffer(GlBufferType type, int buffer) { public static void _setBuffer(GlBufferType type, int buffer) {
buffers[type.ordinal()] = buffer; BUFFERS[type.ordinal()] = buffer;
} }
public static void _setProgram(int id) { public static void _setProgram(int id) {
@ -37,7 +36,7 @@ public class GlStateTracker {
} }
public static State getRestoreState() { public static State getRestoreState() {
return new State(buffers.clone(), vao, program); return new State(BUFFERS.clone(), vao, program);
} }
public record State(int[] buffers, int vao, int program) implements AutoCloseable { public record State(int[] buffers, int vao, int program) implements AutoCloseable {
@ -53,7 +52,7 @@ public class GlStateTracker {
GlBufferType[] values = GlBufferType.values(); GlBufferType[] values = GlBufferType.values();
for (int i = 0; i < values.length; i++) { for (int i = 0; i < values.length; i++) {
if (buffers[i] != GlStateTracker.buffers[i]) { if (buffers[i] != GlStateTracker.BUFFERS[i]) {
GlStateManager._glBindBuffer(values[i].glEnum, buffers[i]); GlStateManager._glBindBuffer(values[i].glEnum, buffers[i]);
} }
} }

View file

@ -12,25 +12,27 @@ import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;
public enum GlBufferType { public enum GlBufferType {
ARRAY_BUFFER(GL15C.GL_ARRAY_BUFFER), ARRAY_BUFFER(GL15C.GL_ARRAY_BUFFER, GL15C.GL_ARRAY_BUFFER_BINDING),
ELEMENT_ARRAY_BUFFER(GL15C.GL_ELEMENT_ARRAY_BUFFER), ELEMENT_ARRAY_BUFFER(GL15C.GL_ELEMENT_ARRAY_BUFFER, GL15C.GL_ELEMENT_ARRAY_BUFFER_BINDING),
PIXEL_PACK_BUFFER(GL21.GL_PIXEL_PACK_BUFFER), PIXEL_PACK_BUFFER(GL21.GL_PIXEL_PACK_BUFFER, GL21.GL_PIXEL_PACK_BUFFER_BINDING),
PIXEL_UNPACK_BUFFER(GL21.GL_PIXEL_UNPACK_BUFFER), PIXEL_UNPACK_BUFFER(GL21.GL_PIXEL_UNPACK_BUFFER, GL21.GL_PIXEL_UNPACK_BUFFER_BINDING),
TRANSFORM_FEEDBACK_BUFFER(GL30.GL_TRANSFORM_FEEDBACK_BUFFER), TRANSFORM_FEEDBACK_BUFFER(GL30.GL_TRANSFORM_FEEDBACK_BUFFER, GL30.GL_TRANSFORM_FEEDBACK_BUFFER_BINDING),
UNIFORM_BUFFER(GL31.GL_UNIFORM_BUFFER), UNIFORM_BUFFER(GL31.GL_UNIFORM_BUFFER, GL31.GL_UNIFORM_BUFFER_BINDING),
TEXTURE_BUFFER(GL31.GL_TEXTURE_BUFFER), TEXTURE_BUFFER(GL31.GL_TEXTURE_BUFFER, GL31.GL_TEXTURE_BUFFER),
COPY_READ_BUFFER(GL31.GL_COPY_READ_BUFFER), COPY_READ_BUFFER(GL31.GL_COPY_READ_BUFFER, GL31.GL_COPY_READ_BUFFER),
COPY_WRITE_BUFFER(GL31.GL_COPY_WRITE_BUFFER), COPY_WRITE_BUFFER(GL31.GL_COPY_WRITE_BUFFER, GL31.GL_COPY_WRITE_BUFFER),
DRAW_INDIRECT_BUFFER(GL40.GL_DRAW_INDIRECT_BUFFER), DRAW_INDIRECT_BUFFER(GL40.GL_DRAW_INDIRECT_BUFFER, GL40.GL_DRAW_INDIRECT_BUFFER_BINDING),
ATOMIC_COUNTER_BUFFER(GL42.GL_ATOMIC_COUNTER_BUFFER), ATOMIC_COUNTER_BUFFER(GL42.GL_ATOMIC_COUNTER_BUFFER, GL42.GL_ATOMIC_COUNTER_BUFFER_BINDING),
DISPATCH_INDIRECT_BUFFER(GL43.GL_DISPATCH_INDIRECT_BUFFER), DISPATCH_INDIRECT_BUFFER(GL43.GL_DISPATCH_INDIRECT_BUFFER, GL43.GL_DISPATCH_INDIRECT_BUFFER_BINDING),
SHADER_STORAGE_BUFFER(GL43.GL_SHADER_STORAGE_BUFFER), SHADER_STORAGE_BUFFER(GL43.GL_SHADER_STORAGE_BUFFER, GL43.GL_SHADER_STORAGE_BUFFER_BINDING),
; ;
public final int glEnum; public final int glEnum;
public final int glBindingEnum;
GlBufferType(int glEnum) { GlBufferType(int glEnum, int glBindingEnum) {
this.glEnum = glEnum; this.glEnum = glEnum;
this.glBindingEnum = glBindingEnum;
} }
public static GlBufferType fromTarget(int pTarget) { public static GlBufferType fromTarget(int pTarget) {

View file

@ -133,10 +133,10 @@ public class InstanceWorld implements AutoCloseable {
/** /**
* Instantiate all the necessary instances to render the given world. * Instantiate all the necessary instances to render the given world.
*/ */
public void loadEntities(ClientLevel world) { public void loadEntities(ClientLevel level) {
// Block entities are loaded while chunks are baked. // Block entities are loaded while chunks are baked.
// Entities are loaded with the world, so when chunks are reloaded they need to be re-added. // Entities are loaded with the level, so when chunks are reloaded they need to be re-added.
ClientLevelExtension.getAllLoadedEntities(world) ClientLevelExtension.getAllLoadedEntities(level)
.forEach(entities::add); .forEach(entities::add);
} }

View file

@ -3,6 +3,7 @@ package com.jozufozu.flywheel.backend.instancing;
import java.util.List; import java.util.List;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.instancing.effect.Effect; import com.jozufozu.flywheel.backend.instancing.effect.Effect;
import com.jozufozu.flywheel.config.FlwCommands; import com.jozufozu.flywheel.config.FlwCommands;
import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.config.FlwConfig;
@ -77,11 +78,11 @@ public class InstancedRenderDispatcher {
return; return;
} }
Minecraft mc = Minecraft.getInstance(); Minecraft mc = Minecraft.getInstance();
ClientLevel world = mc.level; ClientLevel level = mc.level;
AnimationTickHolder.tick(); AnimationTickHolder.tick();
if (Backend.isOn()) { if (Backend.isOn()) {
instanceWorlds.get(world) instanceWorlds.get(level)
.tick(); .tick();
} }
} }
@ -94,22 +95,22 @@ public class InstancedRenderDispatcher {
} }
public static void onRenderStage(RenderStageEvent event) { public static void onRenderStage(RenderStageEvent event) {
ClientLevel world = event.getContext().level(); ClientLevel level = event.getContext().level();
if (!Backend.canUseInstancing(world)) return; if (!Backend.canUseInstancing(level)) return;
instanceWorlds.get(world).renderStage(event.getContext(), event.getStage()); instanceWorlds.get(level).renderStage(event.getContext(), event.getStage());
} }
public static void onReloadRenderers(ReloadRenderersEvent event) { public static void onReloadRenderers(ReloadRenderersEvent event) {
ClientLevel world = event.getWorld(); ClientLevel level = event.getLevel();
if (Backend.isOn() && world != null) { if (Backend.isOn() && level != null) {
resetInstanceWorld(world); resetInstanceLevel(level);
} }
} }
public static void resetInstanceWorld(ClientLevel world) { public static void resetInstanceLevel(ClientLevel level) {
instanceWorlds.replace(world, InstanceWorld::delete) instanceWorlds.replace(level, InstanceWorld::delete)
.loadEntities(world); .loadEntities(level);
} }
public static void getDebugString(List<String> debug) { public static void getDebugString(List<String> debug) {

View file

@ -0,0 +1,208 @@
package com.jozufozu.flywheel.backend.instancing.batching;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
import com.jozufozu.flywheel.backend.memory.MemoryBlock;
import com.jozufozu.flywheel.core.model.Mesh;
import com.mojang.blaze3d.vertex.VertexFormat;
public class BatchedMeshPool {
private final VertexFormat vertexFormat;
private final ReusableVertexList vertexList;
private final int growthMargin;
private final Map<Mesh, BufferedMesh> meshes = new HashMap<>();
private final List<BufferedMesh> allBuffered = new ArrayList<>();
private final List<BufferedMesh> pendingUpload = new ArrayList<>();
private MemoryBlock memory;
private long byteSize;
private boolean dirty;
private boolean anyToRemove;
/**
* Create a new mesh pool.
*/
public BatchedMeshPool(VertexFormat vertexFormat) {
this.vertexFormat = vertexFormat;
vertexList = VertexListProvider.get(vertexFormat).createVertexList();
growthMargin = vertexList.vertexStride() * 32;
}
/**
* Allocate a mesh in the arena.
*
* @param mesh The mesh to allocate.
* @return A handle to the allocated mesh.
*/
public BufferedMesh alloc(Mesh mesh) {
return meshes.computeIfAbsent(mesh, m -> {
BufferedMesh bufferedMesh = new BufferedMesh(m, byteSize, m.getVertexCount() * vertexList.vertexStride());
byteSize += bufferedMesh.size();
allBuffered.add(bufferedMesh);
pendingUpload.add(bufferedMesh);
dirty = true;
return bufferedMesh;
});
}
@Nullable
public BufferedMesh get(Mesh mesh) {
return meshes.get(mesh);
}
public void flush() {
if (dirty) {
if (anyToRemove) {
processDeletions();
}
realloc();
uploadPending();
dirty = false;
pendingUpload.clear();
}
}
private void processDeletions() {
// remove deleted meshes
allBuffered.removeIf(bufferedMesh -> {
boolean deleted = bufferedMesh.isDeleted();
if (deleted) {
meshes.remove(bufferedMesh.mesh);
}
return deleted;
});
// re-evaluate first vertex for each mesh
int byteIndex = 0;
for (BufferedMesh mesh : allBuffered) {
if (mesh.byteIndex != byteIndex) {
pendingUpload.add(mesh);
}
mesh.byteIndex = byteIndex;
byteIndex += mesh.size();
}
this.byteSize = byteIndex;
this.anyToRemove = false;
}
private void realloc() {
if (byteSize < 0) {
throw new IllegalArgumentException("Size " + byteSize + " < 0");
}
if (byteSize == 0) {
return;
}
if (memory == null) {
memory = MemoryBlock.malloc(byteSize);
} else if (byteSize > memory.size()) {
memory = memory.realloc(byteSize + growthMargin);
}
}
private void uploadPending() {
try {
for (BufferedMesh mesh : pendingUpload) {
mesh.buffer(vertexList);
}
pendingUpload.clear();
} catch (Exception e) {
Flywheel.LOGGER.error("Error uploading pooled meshes:", e);
}
}
public void delete() {
if (memory != null) {
memory.free();
}
meshes.clear();
allBuffered.clear();
pendingUpload.clear();
}
public VertexFormat getVertexFormat() {
return vertexFormat;
}
@Override
public String toString() {
return "BatchedMeshPool{" + "vertexFormat=" + vertexFormat + ", byteSize=" + byteSize + ", meshCount=" + meshes.size() + '}';
}
public class BufferedMesh {
private final Mesh mesh;
private long byteIndex;
private int byteSize;
private boolean deleted;
private BufferedMesh(Mesh mesh, long byteIndex, int byteSize) {
this.mesh = mesh;
this.byteIndex = byteIndex;
this.byteSize = byteSize;
}
private void buffer(ReusableVertexList vertexList) {
vertexList.ptr(ptr());
vertexList.vertexCount(mesh.getVertexCount());
mesh.write(vertexList);
}
public void copyTo(long ptr) {
if (isEmpty()) {
return;
}
MemoryUtil.memCopy(ptr(), ptr, byteSize);
}
private boolean isEmpty() {
return mesh.isEmpty() || isDeleted();
}
private long ptr() {
return BatchedMeshPool.this.memory.ptr() + byteIndex;
}
public void delete() {
deleted = true;
BatchedMeshPool.this.dirty = true;
BatchedMeshPool.this.anyToRemove = true;
}
public Mesh getMesh() {
return mesh;
}
public int size() {
return byteSize;
}
public VertexFormat getVertexFormat() {
return vertexFormat;
}
public boolean isDeleted() {
return deleted;
}
}
}

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

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -14,16 +15,18 @@ import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ListMultimap; import com.google.common.collect.ListMultimap;
import com.jozufozu.flywheel.api.RenderStage; import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.Model; import com.jozufozu.flywheel.core.model.Model;
import com.mojang.blaze3d.vertex.VertexFormat;
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);
private final Map<RenderStage, TransformSet> transformSetsView = Collections.unmodifiableMap(transformSets); private final Map<RenderStage, TransformSet> transformSetsView = Collections.unmodifiableMap(transformSets);
private final Map<VertexFormat, BatchedMeshPool> meshPools = new HashMap<>();
public TransformSet get(RenderStage stage) { public TransformSet get(RenderStage stage) {
return transformSets.getOrDefault(stage, TransformSet.EMPTY); return transformSets.getOrDefault(stage, TransformSet.EMPTY);
@ -42,9 +45,17 @@ public class BatchingTransformManager {
add(model.instancer(), model.model()); add(model.instancer(), model.model());
} }
uninitializedModels.clear(); uninitializedModels.clear();
for (var pool : meshPools.values()) {
pool.flush();
}
} }
public void delete() { public void delete() {
meshPools.values()
.forEach(BatchedMeshPool::delete);
meshPools.clear();
allInstancers.forEach(CPUInstancer::delete); allInstancers.forEach(CPUInstancer::delete);
allInstancers.clear(); allInstancers.clear();
} }
@ -56,19 +67,22 @@ public class BatchingTransformManager {
private void add(CPUInstancer<?> instancer, Model model) { private void add(CPUInstancer<?> instancer, Model model) {
var meshes = model.getMeshes(); var meshes = model.getMeshes();
for (var entry : meshes.entrySet()) { for (var entry : meshes.entrySet()) {
TransformCall<?> transformCall = new TransformCall<>(instancer, entry.getKey(), entry.getValue()); var material = entry.getKey();
var material = transformCall.getMaterial();
var renderType = material.getBatchingRenderType(); var renderType = material.getBatchingRenderType();
TransformCall<?> transformCall = new TransformCall<>(instancer, material, alloc(entry.getValue(), renderType.format()));
// 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<?>>>> { private BatchedMeshPool.BufferedMesh alloc(Mesh mesh, VertexFormat format) {
return meshPools.computeIfAbsent(format, BatchedMeshPool::new)
.alloc(mesh);
}
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);
} }
@ -75,9 +72,8 @@ public class DrawBuffer {
} }
ReusableVertexList vertexList = provider.createVertexList(); ReusableVertexList vertexList = provider.createVertexList();
vertexList.ptr(memory.ptr()); vertexList.ptr(memory.ptr() + startVertex * vertexList.vertexStride());
vertexList.shiftPtr(startVertex); vertexList.vertexCount(vertexCount);
vertexList.setVertexCount(vertexCount);
return vertexList; return vertexList;
} }
@ -94,6 +90,10 @@ public class DrawBuffer {
bufferBuilder.flywheel$injectForRender(buffer, format, vertexCount); bufferBuilder.flywheel$injectForRender(buffer, format, vertexCount);
} }
public VertexFormat getVertexFormat() {
return format;
}
public boolean isPrepared() { public boolean isPrepared() {
return prepared; return prepared;
} }

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

@ -8,8 +8,8 @@ import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.vertex.MutableVertexList; import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import com.jozufozu.flywheel.api.vertex.ReusableVertexList; import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.core.model.Mesh;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.math.Matrix3f; import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f; import com.mojang.math.Matrix4f;
import com.mojang.math.Vector3f; import com.mojang.math.Vector3f;
@ -18,23 +18,22 @@ import com.mojang.math.Vector4f;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
public class TransformCall<D extends InstancedPart> { public class TransformCall<D extends InstancedPart> {
private final CPUInstancer<D> instancer; private final CPUInstancer<D> instancer;
private final Material material; private final Material material;
private final Mesh mesh; private final BatchedMeshPool.BufferedMesh bufferedMesh;
public TransformCall(CPUInstancer<D> instancer, Material material, Mesh mesh) { public TransformCall(CPUInstancer<D> instancer, Material material, BatchedMeshPool.BufferedMesh mesh) {
this.instancer = instancer; this.instancer = instancer;
this.material = material; this.material = material;
this.mesh = mesh; this.bufferedMesh = mesh;
} }
public Material getMaterial() { public Material getMaterial() {
return material; return material;
} }
public Mesh getMesh() { public VertexFormat getVertexFormat() {
return mesh; return bufferedMesh.getVertexFormat();
} }
void submitTasks(TaskEngine pool, DrawBuffer buffer, int startVertex, PoseStack stack, ClientLevel level) { void submitTasks(TaskEngine pool, DrawBuffer buffer, int startVertex, PoseStack stack, ClientLevel level) {
@ -47,7 +46,7 @@ public class TransformCall<D extends InstancedPart> {
instances -= 512; instances -= 512;
int start = Math.max(instances, 0); int start = Math.max(instances, 0);
int vertexCount = mesh.getVertexCount() * (end - start); int vertexCount = bufferedMesh.getMesh().getVertexCount() * (end - start);
ReusableVertexList sub = buffer.slice(startVertex, vertexCount); ReusableVertexList sub = buffer.slice(startVertex, vertexCount);
startVertex += vertexCount; startVertex += vertexCount;
@ -65,23 +64,24 @@ public class TransformCall<D extends InstancedPart> {
private void transformList(ReusableVertexList vertexList, List<D> parts, PoseStack stack, ClientLevel level) { private void transformList(ReusableVertexList vertexList, List<D> parts, PoseStack stack, ClientLevel level) {
long anchorPtr = vertexList.ptr(); long anchorPtr = vertexList.ptr();
int totalVertexCount = vertexList.getVertexCount(); int totalVertexCount = vertexList.vertexCount();
int meshVertexCount = mesh.getVertexCount(); int meshVertexCount = bufferedMesh.getMesh().getVertexCount();
vertexList.setVertexCount(meshVertexCount); int meshByteSize = bufferedMesh.size();
vertexList.vertexCount(meshVertexCount);
StructType.VertexTransformer<D> structVertexTransformer = instancer.type.getVertexTransformer(); StructType.VertexTransformer<D> structVertexTransformer = instancer.type.getVertexTransformer();
for (D d : parts) { for (D d : parts) {
mesh.write(vertexList); bufferedMesh.copyTo(vertexList.ptr());
structVertexTransformer.transform(vertexList, d, level); structVertexTransformer.transform(vertexList, d, level);
vertexList.shiftPtr(meshVertexCount); vertexList.ptr(vertexList.ptr() + meshByteSize);
} }
vertexList.ptr(anchorPtr); vertexList.ptr(anchorPtr);
vertexList.setVertexCount(totalVertexCount); vertexList.vertexCount(totalVertexCount);
material.getVertexTransformer().transform(vertexList, level); material.getVertexTransformer().transform(vertexList, level);
applyPoseStack(vertexList, stack); applyPoseStack(vertexList, stack);
} }
@ -93,7 +93,7 @@ public class TransformCall<D extends InstancedPart> {
Matrix4f modelMatrix = stack.last().pose(); Matrix4f modelMatrix = stack.last().pose();
Matrix3f normalMatrix = stack.last().normal(); Matrix3f normalMatrix = stack.last().normal();
for (int i = 0; i < vertexList.getVertexCount(); i++) { for (int i = 0; i < vertexList.vertexCount(); i++) {
pos.set( pos.set(
vertexList.x(i), vertexList.x(i),
vertexList.y(i), vertexList.y(i),
@ -119,6 +119,6 @@ public class TransformCall<D extends InstancedPart> {
} }
public int getTotalVertexCount() { public int getTotalVertexCount() {
return mesh.getVertexCount() * instancer.getInstanceCount(); return bufferedMesh.getMesh().getVertexCount() * instancer.getInstanceCount();
} }
} }

View file

@ -47,20 +47,20 @@ public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
return false; return false;
} }
Level world = blockEntity.getLevel(); Level level = blockEntity.getLevel();
if (world == null) { if (level == null) {
return false; return false;
} }
if (world.isEmptyBlock(blockEntity.getBlockPos())) { if (level.isEmptyBlock(blockEntity.getBlockPos())) {
return false; return false;
} }
if (Backend.isFlywheelWorld(world)) { if (Backend.isFlywheelLevel(level)) {
BlockPos pos = blockEntity.getBlockPos(); BlockPos pos = blockEntity.getBlockPos();
BlockGetter existingChunk = world.getChunkForCollisions(pos.getX() >> 4, pos.getZ() >> 4); BlockGetter existingChunk = level.getChunkForCollisions(pos.getX() >> 4, pos.getZ() >> 4);
return existingChunk != null; return existingChunk != null;
} }

View file

@ -42,12 +42,12 @@ public class EntityInstanceManager extends InstanceManager<Entity> {
return false; return false;
} }
Level world = entity.level; Level level = entity.level;
if (Backend.isFlywheelWorld(world)) { if (Backend.isFlywheelLevel(level)) {
BlockPos pos = entity.blockPosition(); BlockPos pos = entity.blockPosition();
BlockGetter existingChunk = world.getChunkForCollisions(pos.getX() >> 4, pos.getZ() >> 4); BlockGetter existingChunk = level.getChunkForCollisions(pos.getX() >> 4, pos.getZ() >> 4);
return existingChunk != null; return existingChunk != null;
} }

View file

@ -12,6 +12,7 @@ import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.api.context.ContextShader; import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit; import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstanceManager;
@ -65,10 +66,12 @@ public class IndirectEngine implements Engine {
@Override @Override
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) { public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
setup(); try (var restoreState = GlStateTracker.getRestoreState()) {
setup();
for (var list : indirectDrawManager.lists.values()) {
list.submit(stage); for (var list : indirectDrawManager.lists.values()) {
list.submit(stage);
}
} }
} }
@ -121,13 +124,15 @@ public class IndirectEngine implements Engine {
@Override @Override
public void beginFrame(TaskEngine taskEngine, RenderContext context) { public void beginFrame(TaskEngine taskEngine, RenderContext context) {
for (var model : uninitializedModels) { try (var restoreState = GlStateTracker.getRestoreState()) {
model.init(indirectDrawManager); for (var model : uninitializedModels) {
} model.init(indirectDrawManager);
uninitializedModels.clear(); }
uninitializedModels.clear();
for (IndirectCullingGroup<?> value : indirectDrawManager.lists.values()) {
value.beginFrame(); for (IndirectCullingGroup<?> value : indirectDrawManager.lists.values()) {
value.beginFrame();
}
} }
} }

View file

@ -10,10 +10,10 @@ public class DrawCall {
final GPUInstancer<?> instancer; final GPUInstancer<?> instancer;
final Material material; final Material material;
private final int meshAttributes; private final int meshAttributes;
MeshPool.BufferedMesh bufferedMesh; InstancedMeshPool.BufferedMesh bufferedMesh;
GlVertexArray vao; GlVertexArray vao;
DrawCall(GPUInstancer<?> instancer, Material material, MeshPool.BufferedMesh mesh) { DrawCall(GPUInstancer<?> instancer, Material material, InstancedMeshPool.BufferedMesh mesh) {
this.instancer = instancer; this.instancer = instancer;
this.material = material; this.material = material;
this.vao = new GlVertexArray(); this.vao = new GlVertexArray();

View file

@ -19,16 +19,14 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.Mesh;
public class MeshPool { public class InstancedMeshPool {
private final VertexType vertexType; private final VertexType vertexType;
private final Map<Mesh, BufferedMesh> meshes = new HashMap<>(); private final Map<Mesh, BufferedMesh> meshes = new HashMap<>();
private final List<BufferedMesh> allBuffered = new ArrayList<>(); private final List<BufferedMesh> allBuffered = new ArrayList<>();
private final List<BufferedMesh> pendingUpload = new ArrayList<>(); private final List<BufferedMesh> pendingUpload = new ArrayList<>();
private final GlBuffer vbo; private final GlBuffer vbo;
private long byteSize; private long byteSize;
private boolean dirty; private boolean dirty;
@ -37,7 +35,7 @@ public class MeshPool {
/** /**
* Create a new mesh pool. * Create a new mesh pool.
*/ */
public MeshPool(VertexType vertexType) { public InstancedMeshPool(VertexType vertexType) {
this.vertexType = vertexType; this.vertexType = vertexType;
int stride = vertexType.getStride(); int stride = vertexType.getStride();
this.vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER); this.vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER);
@ -46,10 +44,10 @@ public class MeshPool {
} }
/** /**
* Allocate a model in the arena. * Allocate a mesh in the arena.
* *
* @param mesh The model to allocate. * @param mesh The mesh to allocate.
* @return A handle to the allocated model. * @return A handle to the allocated mesh.
*/ */
public BufferedMesh alloc(Mesh mesh) { public BufferedMesh alloc(Mesh mesh) {
return meshes.computeIfAbsent(mesh, m -> { return meshes.computeIfAbsent(mesh, m -> {
@ -57,13 +55,13 @@ public class MeshPool {
throw new IllegalArgumentException("Mesh has wrong vertex type"); throw new IllegalArgumentException("Mesh has wrong vertex type");
} }
BufferedMesh bufferedModel = new BufferedMesh(m, byteSize); BufferedMesh bufferedMesh = new BufferedMesh(m, byteSize);
byteSize += m.size(); byteSize += bufferedMesh.size();
allBuffered.add(bufferedModel); allBuffered.add(bufferedMesh);
pendingUpload.add(bufferedModel); pendingUpload.add(bufferedMesh);
dirty = true; dirty = true;
return bufferedModel; return bufferedMesh;
}); });
} }
@ -90,7 +88,6 @@ public class MeshPool {
} }
private void processDeletions() { private void processDeletions() {
// remove deleted meshes // remove deleted meshes
allBuffered.removeIf(bufferedMesh -> { allBuffered.removeIf(bufferedMesh -> {
boolean deleted = bufferedMesh.isDeleted(); boolean deleted = bufferedMesh.isDeleted();
@ -100,16 +97,16 @@ public class MeshPool {
return deleted; return deleted;
}); });
// re-evaluate first vertex for each model // re-evaluate first vertex for each mesh
int byteIndex = 0; int byteIndex = 0;
for (BufferedMesh model : allBuffered) { for (BufferedMesh mesh : allBuffered) {
if (model.byteIndex != byteIndex) { if (mesh.byteIndex != byteIndex) {
pendingUpload.add(model); pendingUpload.add(mesh);
} }
model.byteIndex = byteIndex; mesh.byteIndex = byteIndex;
byteIndex += model.mesh.size(); byteIndex += mesh.size();
} }
this.byteSize = byteIndex; this.byteSize = byteIndex;
@ -130,14 +127,13 @@ public class MeshPool {
long ptr = mapped.getPtr(); long ptr = mapped.getPtr();
int byteIndex = 0; int byteIndex = 0;
for (BufferedMesh model : allBuffered) { for (BufferedMesh mesh : allBuffered) {
model.byteIndex = byteIndex; mesh.byteIndex = byteIndex;
model.buffer(ptr); mesh.buffer(ptr);
byteIndex += model.mesh.size(); byteIndex += mesh.size();
} }
} catch (Exception e) { } catch (Exception e) {
Flywheel.LOGGER.error("Error uploading pooled meshes:", e); Flywheel.LOGGER.error("Error uploading pooled meshes:", e);
} }
@ -145,10 +141,12 @@ public class MeshPool {
private void uploadPending() { private void uploadPending() {
try (MappedBuffer mapped = vbo.map()) { try (MappedBuffer mapped = vbo.map()) {
long buffer = mapped.getPtr(); long ptr = mapped.getPtr();
for (BufferedMesh model : pendingUpload) {
model.buffer(buffer); for (BufferedMesh mesh : pendingUpload) {
mesh.buffer(ptr);
} }
pendingUpload.clear(); pendingUpload.clear();
} catch (Exception e) { } catch (Exception e) {
Flywheel.LOGGER.error("Error uploading pooled meshes:", e); Flywheel.LOGGER.error("Error uploading pooled meshes:", e);
@ -162,32 +160,41 @@ public class MeshPool {
pendingUpload.clear(); pendingUpload.clear();
} }
public VertexType getVertexType() {
return vertexType;
}
@Override @Override
public String toString() { public String toString() {
return "MeshPool{" + "vertexType=" + vertexType + ", byteSize=" + byteSize + ", meshCount=" + meshes.size() + '}'; return "InstancedMeshPool{" + "vertexType=" + vertexType + ", byteSize=" + byteSize + ", meshCount=" + meshes.size() + '}';
} }
public class BufferedMesh { public class BufferedMesh {
private final ElementBuffer ebo;
private final Mesh mesh; private final Mesh mesh;
private final ElementBuffer ebo;
private long byteIndex; private long byteIndex;
private boolean deleted; private boolean deleted;
private final Set<GlVertexArray> boundTo = new HashSet<>(); private final Set<GlVertexArray> boundTo = new HashSet<>();
public BufferedMesh(Mesh mesh, long byteIndex) { private BufferedMesh(Mesh mesh, long byteIndex) {
this.mesh = mesh; this.mesh = mesh;
this.byteIndex = byteIndex; this.byteIndex = byteIndex;
this.ebo = mesh.createEBO(); this.ebo = mesh.createEBO();
} }
private void buffer(long ptr) {
mesh.write(ptr + byteIndex);
boundTo.clear();
}
public void drawCall(GlVertexArray vao) { public void drawCall(GlVertexArray vao) {
drawInstances(vao, 1); drawInstances(vao, 1);
} }
public void drawInstances(GlVertexArray vao, int instanceCount) { public void drawInstances(GlVertexArray vao, int instanceCount) {
if (hasAnythingToRender()) { if (isEmpty()) {
return; return;
} }
@ -196,50 +203,51 @@ public class MeshPool {
draw(instanceCount); draw(instanceCount);
} }
private boolean hasAnythingToRender() { private boolean isEmpty() {
return mesh.isEmpty() || isDeleted(); return mesh.isEmpty() || isDeleted();
} }
private void setup(GlVertexArray vao) {
if (boundTo.add(vao)) {
vao.enableArrays(getAttributeCount());
vao.bindAttributes(InstancedMeshPool.this.vbo, 0, vertexType.getLayout(), byteIndex);
}
vao.bindElementArray(ebo.buffer);
vao.bind();
}
private void draw(int instanceCount) { private void draw(int instanceCount) {
if (instanceCount > 1) { if (instanceCount > 1) {
GL32.glDrawElementsInstanced(GlPrimitive.TRIANGLES.glEnum, this.ebo.elementCount, this.ebo.eboIndexType.getGlEnum(), 0, instanceCount); GL32.glDrawElementsInstanced(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, instanceCount);
} else { } else {
GL32.glDrawElements(GlPrimitive.TRIANGLES.glEnum, this.ebo.elementCount, this.ebo.eboIndexType.getGlEnum(), 0); GL32.glDrawElements(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0);
} }
} }
private void setup(GlVertexArray vao) {
if (this.boundTo.add(vao)) {
vao.enableArrays(getAttributeCount());
vao.bindAttributes(MeshPool.this.vbo, 0, vertexType.getLayout(), this.byteIndex);
}
vao.bindElementArray(this.ebo.buffer);
vao.bind();
}
public boolean isDeleted() {
return this.deleted;
}
public void delete() { public void delete() {
MeshPool.this.dirty = true; deleted = true;
MeshPool.this.anyToRemove = true; InstancedMeshPool.this.dirty = true;
this.deleted = true; InstancedMeshPool.this.anyToRemove = true;
} }
private void buffer(long ptr) { public Mesh getMesh() {
this.mesh.write(ptr + byteIndex); return mesh;
}
this.boundTo.clear(); public int size() {
return mesh.size();
}
public VertexType getVertexType() {
return vertexType;
} }
public int getAttributeCount() { public int getAttributeCount() {
return vertexType.getLayout().getAttributeCount(); return vertexType.getLayout().getAttributeCount();
} }
public VertexType getVertexType() { public boolean isDeleted() {
return vertexType; return deleted;
} }
} }
} }

View file

@ -23,7 +23,7 @@ public class InstancingDrawManager {
private final List<UninitializedModel> uninitializedModels = new ArrayList<>(); private final List<UninitializedModel> uninitializedModels = new ArrayList<>();
private final List<GPUInstancer<?>> allInstancers = new ArrayList<>(); private final List<GPUInstancer<?>> allInstancers = new ArrayList<>();
private final Map<RenderStage, DrawSet> renderLists = new EnumMap<>(RenderStage.class); private final Map<RenderStage, DrawSet> renderLists = new EnumMap<>(RenderStage.class);
private final Map<VertexType, MeshPool> meshPools = new HashMap<>(); private final Map<VertexType, InstancedMeshPool> meshPools = new HashMap<>();
public DrawSet get(RenderStage stage) { public DrawSet get(RenderStage stage) {
return renderLists.getOrDefault(stage, DrawSet.EMPTY); return renderLists.getOrDefault(stage, DrawSet.EMPTY);
@ -49,7 +49,7 @@ public class InstancingDrawManager {
public void delete() { public void delete() {
meshPools.values() meshPools.values()
.forEach(MeshPool::delete); .forEach(InstancedMeshPool::delete);
meshPools.clear(); meshPools.clear();
renderLists.values() renderLists.values()
@ -77,8 +77,8 @@ public class InstancingDrawManager {
allInstancers.add(instancer); allInstancers.add(instancer);
} }
private MeshPool.BufferedMesh alloc(Mesh mesh) { private InstancedMeshPool.BufferedMesh alloc(Mesh mesh) {
return meshPools.computeIfAbsent(mesh.getVertexType(), MeshPool::new) return meshPools.computeIfAbsent(mesh.getVertexType(), InstancedMeshPool::new)
.alloc(mesh); .alloc(mesh);
} }

View file

@ -12,6 +12,7 @@ import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.api.context.ContextShader; import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit; import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstanceManager;
@ -64,7 +65,9 @@ public class InstancingEngine implements Engine {
@Override @Override
public void beginFrame(TaskEngine taskEngine, RenderContext context) { public void beginFrame(TaskEngine taskEngine, RenderContext context) {
drawManager.flush(); try (var restoreState = GlStateTracker.getRestoreState()) {
drawManager.flush();
}
} }
@Override @Override
@ -75,9 +78,11 @@ public class InstancingEngine implements Engine {
return; return;
} }
setup(); try (var restoreState = GlStateTracker.getRestoreState()) {
setup();
render(drawSet);
render(drawSet);
}
} }
protected void setup() { protected void setup() {

View file

@ -59,23 +59,6 @@ public class FlwCommands {
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
}))); })));
commandBuilder.addValue(config.client.debugNormals, "debugNormals", (builder, value) -> booleanValueCommand(builder, value,
(source, bool) -> {
LocalPlayer player = Minecraft.getInstance().player;
if (player == null) return;
Component text = new TextComponent("Normal debug mode is currently: ").append(boolToText(bool));
player.displayClientMessage(text, false);
},
(source, bool) -> {
LocalPlayer player = Minecraft.getInstance().player;
if (player == null) return;
Component text = boolToText(bool).append(new TextComponent(" normal debug mode").withStyle(ChatFormatting.WHITE));
player.displayClientMessage(text, false);
}
));
commandBuilder.addValue(config.client.limitUpdates, "limitUpdates", (builder, value) -> booleanValueCommand(builder, value, commandBuilder.addValue(config.client.limitUpdates, "limitUpdates", (builder, value) -> booleanValueCommand(builder, value,
(source, bool) -> { (source, bool) -> {
LocalPlayer player = Minecraft.getInstance().player; LocalPlayer player = Minecraft.getInstance().player;
@ -95,7 +78,18 @@ public class FlwCommands {
} }
)); ));
commandBuilder.command.then(Commands.literal("debugCrumble") // TODO
commandBuilder.command.then(Commands.literal("debugNormals"))
.executes(context -> {
LocalPlayer player = Minecraft.getInstance().player;
if (player == null) return 0;
player.displayClientMessage(new TextComponent("This command is not yet implemented."), false);
return Command.SINGLE_SUCCESS;
});
commandBuilder.command.then(Commands.literal("debugCrumbling")
.then(Commands.argument("pos", BlockPosArgument.blockPos()) .then(Commands.argument("pos", BlockPosArgument.blockPos())
.then(Commands.argument("stage", IntegerArgumentType.integer(0, 9)) .then(Commands.argument("stage", IntegerArgumentType.integer(0, 9))
.executes(context -> { .executes(context -> {
@ -111,7 +105,7 @@ public class FlwCommands {
executor.level.destroyBlockProgress(executor.getId(), pos, value); executor.level.destroyBlockProgress(executor.getId(), pos, value);
return 1; return Command.SINGLE_SUCCESS;
})))); }))));
commandBuilder.command.then(Commands.literal("debugFrustum") commandBuilder.command.then(Commands.literal("debugFrustum")

View file

@ -35,10 +35,6 @@ public class FlwConfig {
return BackendTypes.getBackendType(client.backend.get()); return BackendTypes.getBackendType(client.backend.get());
} }
public boolean debugNormals() {
return client.debugNormals.get();
}
public boolean limitUpdates() { public boolean limitUpdates() {
return client.limitUpdates.get(); return client.limitUpdates.get();
} }
@ -48,7 +44,6 @@ public class FlwConfig {
public static class ClientConfig { public static class ClientConfig {
public final ForgeConfigSpec.ConfigValue<String> backend; public final ForgeConfigSpec.ConfigValue<String> backend;
public final BooleanValue debugNormals;
public final BooleanValue limitUpdates; public final BooleanValue limitUpdates;
public ClientConfig(ForgeConfigSpec.Builder builder) { public ClientConfig(ForgeConfigSpec.Builder builder) {
@ -56,9 +51,6 @@ public class FlwConfig {
.define("backend", BackendTypes.defaultForCurrentPC() .define("backend", BackendTypes.defaultForCurrentPC()
.getShortName()); .getShortName());
debugNormals = builder.comment("Enable or disable a debug overlay that colors pixels by their normal.")
.define("debugNormals", false);
limitUpdates = builder.comment("Enable or disable instance update limiting with distance.") limitUpdates = builder.comment("Enable or disable instance update limiting with distance.")
.define("limitUpdates", true); .define("limitUpdates", true);
} }

View file

@ -25,7 +25,7 @@ public final class Materials {
} }
DiffuseLightCalculator diffuseCalc = DiffuseLightCalculator.forLevel(level); DiffuseLightCalculator diffuseCalc = DiffuseLightCalculator.forLevel(level);
for (int i = 0; i < vertexList.getVertexCount(); i++) { for (int i = 0; i < vertexList.vertexCount(); i++) {
float diffuse = diffuseCalc.getDiffuse(vertexList.normalX(i), vertexList.normalY(i), vertexList.normalZ(i), true); float diffuse = diffuseCalc.getDiffuse(vertexList.normalX(i), vertexList.normalY(i), vertexList.normalZ(i), true);
vertexList.r(i, (byte) Mth.clamp((int) (Byte.toUnsignedInt(vertexList.r(i)) * diffuse), 0, 255)); vertexList.r(i, (byte) Mth.clamp((int) (Byte.toUnsignedInt(vertexList.r(i)) * diffuse), 0, 255));
vertexList.g(i, (byte) Mth.clamp((int) (Byte.toUnsignedInt(vertexList.g(i)) * diffuse), 0, 255)); vertexList.g(i, (byte) Mth.clamp((int) (Byte.toUnsignedInt(vertexList.g(i)) * diffuse), 0, 255));

View file

@ -33,7 +33,7 @@ public class ModelPart implements Mesh {
vertexList = getVertexType().createVertexList(); vertexList = getVertexType().createVertexList();
vertexList.ptr(ptr); vertexList.ptr(ptr);
vertexList.setVertexCount(vertexCount); vertexList.vertexCount(vertexCount);
boundingSphere = ModelUtil.computeBoundingSphere(vertexList); boundingSphere = ModelUtil.computeBoundingSphere(vertexList);
} }

View file

@ -66,8 +66,8 @@ public class ModelUtil {
ReusableVertexList dstList = dstVertexType.createVertexList(); ReusableVertexList dstList = dstVertexType.createVertexList();
srcList.ptr(srcPtr); srcList.ptr(srcPtr);
dstList.ptr(dstPtr); dstList.ptr(dstPtr);
srcList.setVertexCount(vertexCount); srcList.vertexCount(vertexCount);
dstList.setVertexCount(vertexCount); dstList.vertexCount(vertexCount);
srcList.writeAll(dstList); srcList.writeAll(dstList);

View file

@ -29,7 +29,7 @@ public class SimpleMesh implements Mesh {
vertexList = getVertexType().createVertexList(); vertexList = getVertexType().createVertexList();
vertexList.ptr(contents.ptr()); vertexList.ptr(contents.ptr());
vertexList.setVertexCount(vertexCount); vertexList.vertexCount(vertexCount);
boundingSphere = ModelUtil.computeBoundingSphere(vertexList); boundingSphere = ModelUtil.computeBoundingSphere(vertexList);
} }

View file

@ -59,7 +59,7 @@ public class OrientedType implements StructType<OrientedPart> {
Matrix3f normalMatrix = new Matrix3f(q); Matrix3f normalMatrix = new Matrix3f(q);
int light = struct.getPackedLight(); int light = struct.getPackedLight();
for (int i = 0; i < vertexList.getVertexCount(); i++) { for (int i = 0; i < vertexList.vertexCount(); i++) {
pos.set( pos.set(
vertexList.x(i), vertexList.x(i),
vertexList.y(i), vertexList.y(i),

View file

@ -45,7 +45,7 @@ public class TransformedType implements StructType<TransformedPart> {
Vector3f normal = new Vector3f(); Vector3f normal = new Vector3f();
int light = struct.getPackedLight(); int light = struct.getPackedLight();
for (int i = 0; i < vertexList.getVertexCount(); i++) { for (int i = 0; i < vertexList.vertexCount(); i++) {
pos.set( pos.set(
vertexList.x(i), vertexList.x(i),
vertexList.y(i), vertexList.y(i),

View file

@ -17,12 +17,12 @@ public abstract class AbstractVertexList implements ReusableVertexList {
} }
@Override @Override
public int getVertexCount() { public int vertexCount() {
return vertexCount; return vertexCount;
} }
@Override @Override
public void setVertexCount(int vertexCount) { public void vertexCount(int vertexCount) {
this.vertexCount = vertexCount; this.vertexCount = vertexCount;
} }
} }

View file

@ -153,7 +153,7 @@ public class BlockVertexList extends AbstractVertexList {
} }
@Override @Override
public void shiftPtr(int vertices) { public int vertexStride() {
ptr += vertices * STRIDE; return STRIDE;
} }
} }

View file

@ -203,7 +203,7 @@ public class InferredVertexListImpl extends AbstractVertexList implements Reusab
} }
@Override @Override
public void shiftPtr(int vertices) { public int vertexStride() {
ptr += vertices * stride; return stride;
} }
} }

View file

@ -148,7 +148,7 @@ public class PosTexNormalVertexList extends AbstractVertexList {
} }
@Override @Override
public void shiftPtr(int vertices) { public int vertexStride() {
ptr += vertices * STRIDE; return STRIDE;
} }
} }

View file

@ -11,7 +11,7 @@ import java.util.function.Predicate;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.FlywheelWorld; import com.jozufozu.flywheel.api.FlywheelLevel;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
@ -43,7 +43,7 @@ import net.minecraft.world.level.storage.WritableLevelData;
import net.minecraft.world.scores.Scoreboard; import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.ticks.LevelTickAccess; import net.minecraft.world.ticks.LevelTickAccess;
public class VirtualRenderWorld extends Level implements FlywheelWorld { public class VirtualRenderWorld extends Level implements FlywheelLevel {
public final Map<BlockPos, BlockState> blocksAdded = new HashMap<>(); public final Map<BlockPos, BlockState> blocksAdded = new HashMap<>();
public final Map<BlockPos, BlockEntity> besAdded = new HashMap<>(); public final Map<BlockPos, BlockEntity> besAdded = new HashMap<>();
public final Set<SectionPos> spannedSections = new HashSet<>(); public final Set<SectionPos> spannedSections = new HashSet<>();

View file

@ -6,14 +6,14 @@ import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.eventbus.api.Event;
public class ReloadRenderersEvent extends Event { public class ReloadRenderersEvent extends Event {
private final ClientLevel world; private final ClientLevel level;
public ReloadRenderersEvent(ClientLevel world) { public ReloadRenderersEvent(ClientLevel level) {
this.world = world; this.level = level;
} }
@Nullable @Nullable
public ClientLevel getWorld() { public ClientLevel getLevel() {
return world; return level;
} }
} }

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

@ -1,28 +0,0 @@
package com.jozufozu.flywheel.mixin;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.mojang.blaze3d.vertex.BufferUploader;
import com.mojang.blaze3d.vertex.VertexFormat;
@Mixin(BufferUploader.class)
public class BufferUploaderMixin {
@Shadow
@Nullable
private static VertexFormat lastFormat;
// Stop BufferUploader from clearing buffer state if nothing is bound
@Inject(method = "reset", at = @At("HEAD"))
private static void flywheel$onReset(CallbackInfo ci) {
// Trust our tracker over BufferUploader's.
if (GlStateTracker.getVertexArray() == 0) {
lastFormat = null;
}
}
}

View file

@ -12,7 +12,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.api.RenderStage; import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.core.RenderContext; import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent;
@ -48,9 +47,7 @@ public class LevelRendererMixin {
var culler = RenderContext.createCuller(viewProjection, (float) -cameraPos.x, (float) -cameraPos.y, (float) -cameraPos.z); var culler = RenderContext.createCuller(viewProjection, (float) -cameraPos.x, (float) -cameraPos.y, (float) -cameraPos.z);
flywheel$renderContext = new RenderContext((LevelRenderer) (Object) this, level, pPoseStack, viewProjection, pProjectionMatrix, renderBuffers, pCamera, culler); flywheel$renderContext = new RenderContext((LevelRenderer) (Object) this, level, pPoseStack, viewProjection, pProjectionMatrix, renderBuffers, pCamera, culler);
try (var restoreState = GlStateTracker.getRestoreState()) { MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(flywheel$renderContext));
MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(flywheel$renderContext));
}
} }
@Inject(at = @At("TAIL"), method = "renderLevel") @Inject(at = @At("TAIL"), method = "renderLevel")
@ -78,9 +75,7 @@ public class LevelRendererMixin {
@Unique @Unique
private void flywheel$dispatch(RenderStage stage) { private void flywheel$dispatch(RenderStage stage) {
if (flywheel$renderContext != null) { if (flywheel$renderContext != null) {
try (var restoreState = GlStateTracker.getRestoreState()) { MinecraftForge.EVENT_BUS.post(new RenderStageEvent(flywheel$renderContext, stage));
MinecraftForge.EVENT_BUS.post(new RenderStageEvent(flywheel$renderContext, stage));
}
} }
} }

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;
} }
} }

View file

@ -18,14 +18,14 @@ public class NetworkLightUpdateMixin {
@Inject(at = @At("TAIL"), method = "handleLightUpdatePacket") @Inject(at = @At("TAIL"), method = "handleLightUpdatePacket")
private void flywheel$onLightPacket(ClientboundLightUpdatePacket packet, CallbackInfo ci) { private void flywheel$onLightPacket(ClientboundLightUpdatePacket packet, CallbackInfo ci) {
RenderWork.enqueue(() -> { RenderWork.enqueue(() -> {
ClientLevel world = Minecraft.getInstance().level; ClientLevel level = Minecraft.getInstance().level;
if (world == null) return; if (level == null) return;
int chunkX = packet.getX(); int chunkX = packet.getX();
int chunkZ = packet.getZ(); int chunkZ = packet.getZ();
LightUpdater.get(world) LightUpdater.get(level)
.onLightPacket(chunkX, chunkZ); .onLightPacket(chunkX, chunkZ);
}); });
} }

View file

@ -8,7 +8,6 @@
"BlockEntityRenderDispatcherAccessor", "BlockEntityRenderDispatcherAccessor",
"BlockEntityTypeMixin", "BlockEntityTypeMixin",
"BufferBuilderMixin", "BufferBuilderMixin",
"BufferUploaderMixin",
"ChunkRebuildHooksMixin", "ChunkRebuildHooksMixin",
"ClientLevelMixin", "ClientLevelMixin",
"ClientMainMixin", "ClientMainMixin",