mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-07 12:56:31 +01:00
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:
commit
d3d5797fd3
46 changed files with 605 additions and 321 deletions
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@ public class BlockVertexList extends AbstractVertexList {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shiftPtr(int vertices) {
|
public int vertexStride() {
|
||||||
ptr += vertices * STRIDE;
|
return STRIDE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,7 +148,7 @@ public class PosTexNormalVertexList extends AbstractVertexList {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shiftPtr(int vertices) {
|
public int vertexStride() {
|
||||||
ptr += vertices * STRIDE;
|
return STRIDE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<>();
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
"BlockEntityRenderDispatcherAccessor",
|
"BlockEntityRenderDispatcherAccessor",
|
||||||
"BlockEntityTypeMixin",
|
"BlockEntityTypeMixin",
|
||||||
"BufferBuilderMixin",
|
"BufferBuilderMixin",
|
||||||
"BufferUploaderMixin",
|
|
||||||
"ChunkRebuildHooksMixin",
|
"ChunkRebuildHooksMixin",
|
||||||
"ClientLevelMixin",
|
"ClientLevelMixin",
|
||||||
"ClientMainMixin",
|
"ClientMainMixin",
|
||||||
|
|
Loading…
Reference in a new issue