Finish porting to 1.19 (mostly)

- Terrible, terrible mixins to ChunkRenderDispatcher and its subclasses
 - Index buffers may break chunk rendering sometimes, needs debugging
This commit is contained in:
Jozufozu 2022-07-16 21:38:22 -04:00
parent b5ea5f561e
commit ae945f6ace
27 changed files with 349 additions and 240 deletions

View file

@ -13,6 +13,17 @@ insert_final_newline = true
[*.json]
indent_style = space
indent_size = 2
max_line_length = 500
ij_json_keep_blank_lines_in_code = 0
ij_json_keep_indents_on_empty_lines = false
ij_json_keep_line_breaks = true
ij_json_space_after_colon = true
ij_json_space_after_comma = true
ij_json_space_before_colon = true
ij_json_space_before_comma = false
ij_json_spaces_within_braces = true
ij_json_spaces_within_brackets = false
ij_json_wrap_long_lines = false
[*.java]
indent_style = tab

View file

@ -39,7 +39,7 @@ public class MappedBuffer extends VecBuffer implements AutoCloseable {
}
@Override
public void close() throws Exception {
public void close() {
flush();
}
}

View file

@ -1,6 +1,7 @@
package com.jozufozu.flywheel.backend.instancing;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -359,4 +360,14 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
LightUpdater.get(value.world).removeListener(value);
}
}
public void queueAddAll(Collection<? extends T> objects) {
if (!Backend.isOn() || objects.isEmpty()) {
return;
}
synchronized (queuedAdditions) {
queuedAdditions.addAll(objects);
}
}
}

View file

@ -2,14 +2,16 @@ package com.jozufozu.flywheel.backend.model;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.mojang.blaze3d.vertex.VertexBuffer;
import com.mojang.blaze3d.vertex.VertexFormat;
public class ElementBuffer {
private final GlBuffer buffer;
public final int elementCount;
public final GlNumericType eboIndexType;
public final VertexFormat.IndexType eboIndexType;
public ElementBuffer(GlBuffer backing, int elementCount, GlNumericType indexType) {
public ElementBuffer(GlBuffer backing, int elementCount, VertexFormat.IndexType indexType) {
this.buffer = backing;
this.eboIndexType = indexType;
this.elementCount = elementCount;

View file

@ -60,7 +60,7 @@ public class IndexedModel implements BufferedModel {
@Override
public void drawCall() {
ebo.bind();
GL20.glDrawElements(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0);
GL20.glDrawElements(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.asGLType, 0);
}
/**
@ -72,7 +72,7 @@ public class IndexedModel implements BufferedModel {
ebo.bind();
GL31.glDrawElementsInstanced(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, instanceCount);
GL31.glDrawElementsInstanced(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.asGLType, 0, instanceCount);
}
public boolean isDeleted() {

View file

@ -189,7 +189,7 @@ public class ModelPool implements ModelAllocator {
@Override
public void drawCall() {
GL32.glDrawElementsBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, first);
GL32.glDrawElementsBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.asGLType, 0, first);
}
@Override
@ -200,7 +200,7 @@ public class ModelPool implements ModelAllocator {
//Backend.log.info(StringUtil.args("drawElementsInstancedBaseVertex", GlPrimitive.TRIANGLES, ebo.elementCount, ebo.eboIndexType, 0, instanceCount, first));
GL32.glDrawElementsInstancedBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, instanceCount, first);
GL32.glDrawElementsInstancedBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.asGLType, 0, instanceCount, first);
}
@Override

View file

@ -3,41 +3,30 @@ package com.jozufozu.flywheel.core;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.EnumMap;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.lwjgl.system.MemoryStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
import com.jozufozu.flywheel.backend.model.ElementBuffer;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import com.mojang.blaze3d.vertex.VertexFormat;
/**
* A class to manage EBOs that index quads as triangles.
*/
@Mod.EventBusSubscriber(Dist.CLIENT)
public class QuadConverter {
public static final int STARTING_CAPACITY = 42; // 255 / 6 = 42
private static QuadConverter INSTANCE;
@Nonnull
@NotNull
public static QuadConverter getInstance() {
if (INSTANCE == null) {
INSTANCE = new QuadConverter(STARTING_CAPACITY);
INSTANCE = new QuadConverter();
}
return INSTANCE;
@ -48,130 +37,80 @@ public class QuadConverter {
return INSTANCE;
}
Map<GlNumericType, GlBuffer> ebos;
int[] capacities;
private final MappedGlBuffer ebo;
private int quadCapacity;
public QuadConverter(int initialCapacity) {
this.ebos = new EnumMap<>(GlNumericType.class);
initCapacities();
fillBuffer(initialCapacity);
public QuadConverter() {
this.ebo = new MappedGlBuffer(GlBufferType.ELEMENT_ARRAY_BUFFER);
this.quadCapacity = 0;
}
public ElementBuffer quads2Tris(int quads) {
int indexCount = quads * 6;
GlNumericType type = getSmallestIndexType(indexCount);
if (quads > getCapacity(type)) {
fillBuffer(quads, indexCount, type);
if (quads > quadCapacity) {
ebo.bind();
ebo.ensureCapacity((long) indexCount * GlNumericType.UINT.getByteWidth());
try (MappedBuffer map = ebo.getBuffer()) {
ByteBuffer indices = map.unwrap();
fillBuffer(indices, quads);
}
ebo.unbind();
this.quadCapacity = quads;
}
return new ElementBuffer(getBuffer(type), indexCount, type);
}
private void initCapacities() {
this.capacities = new int[GlNumericType.values().length];
}
private int getCapacity(GlNumericType type) {
return capacities[type.ordinal()];
}
private void updateCapacity(GlNumericType type, int capacity) {
if (getCapacity(type) < capacity) {
capacities[type.ordinal()] = capacity;
}
return new ElementBuffer(ebo, indexCount, VertexFormat.IndexType.INT);
}
public void delete() {
ebos.values()
.forEach(GlBuffer::delete);
ebos.clear();
initCapacities();
ebo.delete();
this.quadCapacity = 0;
}
private void fillBuffer(int quads) {
int indexCount = quads * 6;
private void fillBuffer(ByteBuffer indices, int quads) {
long addr = MemoryUtil.memAddress(indices);
int numVertices = 4 * quads;
int baseVertex = 0;
while (baseVertex < numVertices) {
// writeQuadIndices(indices, baseVertex);
writeQuadIndicesUnsafe(addr, baseVertex);
fillBuffer(quads, indexCount, getSmallestIndexType(indexCount));
}
private void fillBuffer(int quads, int indexCount, GlNumericType type) {
MemoryStack stack = MemoryStack.stackPush();
int bytes = indexCount * type.getByteWidth();
ByteBuffer indices;
if (bytes > stack.getSize()) {
indices = MemoryUtil.memAlloc(bytes); // not enough space on the preallocated stack
} else {
stack.push();
indices = stack.malloc(bytes);
baseVertex += 4;
addr += 6 * 4;
}
indices.order(ByteOrder.nativeOrder());
fillBuffer(indices, type, quads);
GlBuffer buffer = getBuffer(type);
buffer.bind();
buffer.upload(indices);
buffer.unbind();
if (bytes > stack.getSize()) {
MemoryUtil.memFree(indices);
} else {
stack.pop();
}
updateCapacity(type, quads);
// ((Buffer) indices).flip();
}
private void fillBuffer(ByteBuffer indices, GlNumericType type, int quads) {
for (int i = 0, max = 4 * quads; i < max; i += 4) {
// triangle a
type.castAndBuffer(indices, i);
type.castAndBuffer(indices, i + 1);
type.castAndBuffer(indices, i + 2);
// triangle b
type.castAndBuffer(indices, i);
type.castAndBuffer(indices, i + 2);
type.castAndBuffer(indices, i + 3);
}
((Buffer) indices).flip();
private void writeQuadIndices(ByteBuffer indices, int baseVertex) {
// triangle a
indices.putInt(baseVertex);
indices.putInt(baseVertex + 1);
indices.putInt(baseVertex + 2);
// triangle b
indices.putInt(baseVertex);
indices.putInt(baseVertex + 2);
indices.putInt(baseVertex + 3);
}
private GlBuffer getBuffer(GlNumericType type) {
return ebos.computeIfAbsent(type, $ -> new MappedGlBuffer(GlBufferType.ELEMENT_ARRAY_BUFFER));
}
/**
* Given the needed number of indices, what is the smallest bit width type that can index everything? <br>
*
* <pre>
* | indexCount | type |
* |--------------|-------|
* | [0, 255) | byte |
* | [256, 65536) | short |
* | [65537, ) | int |
* </pre>
*/
private static GlNumericType getSmallestIndexType(int indexCount) {
// indexCount = indexCount >>> 8;
// if (indexCount == 0) {
// return GlNumericType.UBYTE;
// }
// indexCount = indexCount >>> 8;
// if (indexCount == 0) {
// return GlNumericType.USHORT;
// }
return GlNumericType.UINT;
private void writeQuadIndicesUnsafe(long addr, int baseVertex) {
// triangle a
MemoryUtil.memPutInt(addr, baseVertex);
MemoryUtil.memPutInt(addr + 4, baseVertex + 1);
MemoryUtil.memPutInt(addr + 8, baseVertex + 2);
// triangle b
MemoryUtil.memPutInt(addr + 12, baseVertex);
MemoryUtil.memPutInt(addr + 16, baseVertex + 2);
MemoryUtil.memPutInt(addr + 20, baseVertex + 3);
}
// make sure this gets reset first so it has a chance to repopulate
@SubscribeEvent(priority = EventPriority.HIGHEST)
public static void onRendererReload(ReloadRenderersEvent event) {
if (INSTANCE != null) INSTANCE.delete();
if (INSTANCE != null) {
INSTANCE.delete();
INSTANCE = null;
}
}
}

View file

@ -3,7 +3,9 @@ package com.jozufozu.flywheel.core.hardcoded;
import java.util.List;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.backend.model.ElementBuffer;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.QuadConverter;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.vertex.PosTexNormalWriterUnsafe;
import com.mojang.blaze3d.platform.MemoryTracker;
@ -51,4 +53,10 @@ public class ModelPart implements Model {
public VertexList getReader() {
return reader;
}
@Override
public ElementBuffer createEBO() {
return QuadConverter.getInstance()
.quads2Tris(vertices / 4);
}
}

View file

@ -1,8 +1,25 @@
package com.jozufozu.flywheel.core.model;
import java.lang.management.MemoryUsage;
import java.nio.ByteBuffer;
import java.util.function.Supplier;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferUsage;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
import com.jozufozu.flywheel.backend.model.ElementBuffer;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.QuadConverter;
import com.mojang.blaze3d.platform.MemoryTracker;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexBuffer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.model.BakedModel;
@ -12,12 +29,12 @@ import net.minecraft.world.level.block.state.BlockState;
* A model of a single block.
*/
public class BlockModel implements Model {
private static final PoseStack IDENTITY = new PoseStack();
private final VertexList reader;
private final String name;
private final Supplier<ElementBuffer> eboSupplier;
public BlockModel(BlockState state) {
this(Minecraft.getInstance()
.getBlockRenderer()
@ -25,12 +42,49 @@ public class BlockModel implements Model {
}
public BlockModel(BakedModel model, BlockState referenceState) {
this(model, referenceState, IDENTITY);
this(new BakedModelBuilder(model).withReferenceState(referenceState), referenceState.toString());
}
public BlockModel(BakedModel model, BlockState referenceState, PoseStack ms) {
reader = Formats.BLOCK.createReader(ModelUtil.getBufferBuilder(model, referenceState, ms));
name = referenceState.toString();
this(new BakedModelBuilder(model).withReferenceState(referenceState)
.withPoseStack(ms), referenceState.toString());
}
public BlockModel(Bufferable bufferable, String name) {
this(bufferable.build(), name);
}
public BlockModel(ShadeSeparatedBufferBuilder buffer, String name) {
this.name = name;
BufferBuilder.RenderedBuffer renderedBuffer = buffer.endOrDiscardIfEmpty();
if (renderedBuffer == null) {
reader = null;
eboSupplier = () -> null;
return;
}
BufferBuilder.DrawState drawState = renderedBuffer.drawState();
reader = Formats.BLOCK.createReader(renderedBuffer, buffer.getUnshadedStartVertex());
if (drawState.sequentialIndex()) {
ByteBuffer src = renderedBuffer.indexBuffer();
ByteBuffer indexBuffer = MemoryTracker.create(src.capacity());
MemoryUtil.memCopy(src, indexBuffer);
eboSupplier = () -> {
MappedGlBuffer vbo = new MappedGlBuffer(GlBufferType.ELEMENT_ARRAY_BUFFER, GlBufferUsage.STATIC_DRAW);
vbo.upload(indexBuffer);
return new ElementBuffer(vbo, drawState.indexCount(), drawState.indexType());
};
} else {
eboSupplier = () -> QuadConverter.getInstance()
.quads2Tris(vertexCount() / 4);
}
}
@Override
@ -47,4 +101,14 @@ public class BlockModel implements Model {
public VertexList getReader() {
return reader;
}
@Override
public ElementBuffer createEBO() {
return eboSupplier.get();
}
@Override
public VertexType getType() {
return Formats.BLOCK;
}
}

View file

@ -56,10 +56,7 @@ public interface Model {
* </p>
* @return an element buffer object indexing this model's vertices.
*/
default ElementBuffer createEBO() {
return QuadConverter.getInstance()
.quads2Tris(vertexCount() / 4);
}
ElementBuffer createEBO();
/**
* The size in bytes that this model's data takes up.

View file

@ -14,9 +14,16 @@ public class ShadeSeparatedBufferBuilder extends BufferBuilder {
}
public void appendUnshadedVertices(BufferBuilder unshadedBuilder) {
Pair<DrawState, ByteBuffer> data = unshadedBuilder.popNextBuffer();
RenderedBuffer renderedBuffer = unshadedBuilder.endOrDiscardIfEmpty();
if (renderedBuffer == null) {
return;
}
// FIXME: Unshaded indices
ByteBuffer buffer = renderedBuffer.vertexBuffer();
unshadedStartVertex = ((BufferBuilderExtension) this).flywheel$getVertices();
((BufferBuilderExtension) this).flywheel$appendBufferUnsafe(data.getSecond());
((BufferBuilderExtension) this).flywheel$appendBufferUnsafe(buffer);
}
public int getUnshadedStartVertex() {

View file

@ -1,37 +0,0 @@
package com.jozufozu.flywheel.core.model;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.Formats;
import com.mojang.blaze3d.vertex.BufferBuilder;
public class WorldModel implements Model {
private final VertexList reader;
private final String name;
public WorldModel(BufferBuilder bufferBuilder, String name) {
this.reader = Formats.BLOCK.createReader(bufferBuilder);
this.name = name;
}
@Override
public String name() {
return name;
}
@Override
public VertexType getType() {
return Formats.BLOCK;
}
@Override
public int vertexCount() {
return reader.getVertexCount();
}
@Override
public VertexList getReader() {
return reader;
}
}

View file

@ -77,7 +77,7 @@ public final class WorldModelBuilder implements Bufferable {
return this;
}
public WorldModel intoMesh(String name) {
return new WorldModel(ModelUtil.getBufferBuilder(this), name);
public BlockModel intoMesh(String name) {
return new BlockModel(this, name);
}
}

View file

@ -22,15 +22,6 @@ public abstract class AbstractVertexList implements VertexList, AutoCloseable {
init(copyFrom);
}
public AbstractVertexList(BufferBuilder builder) {
var pair = builder.popNextBuffer();
ByteBuffer copyFrom = pair.getSecond();
this.contents = MemoryTracker.create(copyFrom.capacity());
this.vertexCount = pair.getFirst().vertexCount();
this.base = MemoryUtil.memAddress(this.contents);
init(copyFrom);
}
private void init(ByteBuffer copyFrom) {
this.contents.order(copyFrom.order());
this.contents.put(copyFrom);

View file

@ -62,19 +62,20 @@ Vertex FLWCreateVertex() {
return new BlockVertexListUnsafe.Shaded(buffer, vertexCount, unshadedStartVertex);
}
public VertexList createReader(BufferBuilder bufferBuilder) {
public VertexList createReader(BufferBuilder.RenderedBuffer renderedBuffer, int unshadedStartVertex) {
// TODO: try to avoid virtual model rendering
Pair<BufferBuilder.DrawState, ByteBuffer> pair = bufferBuilder.popNextBuffer();
BufferBuilder.DrawState drawState = pair.getFirst();
BufferBuilder.DrawState drawState = renderedBuffer.drawState();
if (drawState.format() != DefaultVertexFormat.BLOCK) {
throw new RuntimeException("Cannot use BufferBuilder with " + drawState.format());
}
ByteBuffer vertexBuffer = renderedBuffer.vertexBuffer();
if (bufferBuilder instanceof ShadeSeparatedBufferBuilder separated) {
return createReader(pair.getSecond(), drawState.vertexCount(), separated.getUnshadedStartVertex());
if (unshadedStartVertex > 0) {
return createReader(vertexBuffer, drawState.vertexCount(), unshadedStartVertex);
} else {
return createReader(pair.getSecond(), drawState.vertexCount());
return createReader(vertexBuffer, drawState.vertexCount());
}
}
}

View file

@ -1,5 +1,7 @@
package com.jozufozu.flywheel.core.vertex;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.vertex.ShadedVertexList;
import com.jozufozu.flywheel.core.model.ShadeSeparatedBufferBuilder;
import com.jozufozu.flywheel.util.RenderMath;
@ -9,10 +11,9 @@ public class BlockVertexList extends AbstractVertexList {
private final int stride;
public BlockVertexList(BufferBuilder builder) {
super(builder);
this.stride = builder.getVertexFormat()
.getVertexSize();
public BlockVertexList(ByteBuffer copyFrom, int vertexCount, int stride) {
super(copyFrom, vertexCount);
this.stride = stride;
}
@Override
@ -93,9 +94,9 @@ public class BlockVertexList extends AbstractVertexList {
private final int unshadedStartVertex;
public Shaded(ShadeSeparatedBufferBuilder builder) {
super(builder);
unshadedStartVertex = builder.getUnshadedStartVertex();
public Shaded(ByteBuffer copyFrom, int vertexCount, int stride, int unshadedStartVertex) {
super(copyFrom, vertexCount, stride);
this.unshadedStartVertex = unshadedStartVertex;
}
@Override

View file

@ -10,6 +10,7 @@ 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.VertexBuffer;
import com.mojang.blaze3d.vertex.VertexFormat;
@Mixin(BufferUploader.class)
@ -17,13 +18,13 @@ public class BufferUploaderMixin {
@Shadow
@Nullable
private static VertexFormat lastFormat;
private static VertexBuffer lastImmediateBuffer;
@Inject(method = "reset", at = @At("HEAD"))
private static void stopBufferUploaderFromClearingBufferStateIfNothingIsBound(CallbackInfo ci) {
// Trust our tracker over BufferUploader's.
if (GlStateTracker.getVertexArray() == 0) {
lastFormat = null;
lastImmediateBuffer = null;
}
}
}

View file

@ -1,33 +0,0 @@
package com.jozufozu.flywheel.mixin;
import java.util.Set;
import org.spongepowered.asm.mixin.Mixin;
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.Backend;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
@Mixin(targets = "net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$RenderChunk$RebuildTask")
public class ChunkRebuildHooksMixin {
@Inject(method = "handleBlockEntity", at = @At("HEAD"), cancellable = true)
private <E extends BlockEntity> void addAndFilterBEs(ChunkRenderDispatcher.CompiledChunk compiledChunk, Set<BlockEntity> set, E be, CallbackInfo ci) {
if (Backend.canUseInstancing(be.getLevel())) {
if (InstancedRenderRegistry.canInstance(be.getType()))
InstancedRenderDispatcher.getBlockEntities(be.getLevel()).queueAdd(be);
if (InstancedRenderRegistry.shouldSkipRender(be))
ci.cancel();
}
}
}

View file

@ -0,0 +1,82 @@
package com.jozufozu.flywheel.mixin.instancemanage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import com.jozufozu.flywheel.util.RenderChunkExtension;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
import net.minecraft.client.renderer.chunk.RenderChunkRegion;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
@Mixin(targets = "net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$RenderChunk$RebuildTask")
public abstract class ChunkRebuildHooksMixin {
@Unique
private Level flywheel$level;
@Inject(method = "<init>(Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;Lnet/minecraft/world/level/ChunkPos;DLnet/minecraft/client/renderer/chunk/RenderChunkRegion;Z)V", at = @At("RETURN"))
private void setLevel(ChunkRenderDispatcher.RenderChunk this$1, ChunkPos pos, double p_194427_, RenderChunkRegion region, boolean p_194429_, CallbackInfo ci) {
flywheel$level = ((RenderChunkExtension) this$1).flywheel$getLevel();
}
@Redirect(method = "doTask", at = @At(value = "INVOKE", target = "Ljava/util/List;addAll(Ljava/util/Collection;)Z"))
private <E extends BlockEntity> boolean addAndFilterBEs(List<BlockEntity> self, Collection<? extends E> es) {
if (!Backend.canUseInstancing(flywheel$level)) {
return self.addAll(es);
}
boolean added = false;
var instanced = new ArrayList<BlockEntity>();
for (E be : es) {
if (InstancedRenderRegistry.canInstance(be.getType())) {
instanced.add(be);
}
if (!InstancedRenderRegistry.shouldSkipRender(be)) {
self.add(be);
added = true;
}
}
InstancedRenderDispatcher.getBlockEntities(flywheel$level).queueAddAll(instanced);
return added;
}
@Redirect(method = "doTask", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;updateGlobalBlockEntities(Ljava/util/Collection;)V"))
private void addAndFilterBEs(ChunkRenderDispatcher.RenderChunk self, Collection<BlockEntity> bes) {
if (!Backend.canUseInstancing(flywheel$level)) {
((RenderChunkAccessor) self).flywheel$updateGlobalBlockEntities(bes);
return;
}
var global = new ArrayList<BlockEntity>();
var instanced = new ArrayList<BlockEntity>();
for (BlockEntity be : bes) {
if (InstancedRenderRegistry.canInstance(be.getType())) {
instanced.add(be);
}
if (!InstancedRenderRegistry.shouldSkipRender(be)) {
global.add(be);
}
}
InstancedRenderDispatcher.getBlockEntities(flywheel$level).queueAddAll(instanced);
((RenderChunkAccessor) self).flywheel$updateGlobalBlockEntities(global);
}
}

View file

@ -0,0 +1,14 @@
package com.jozufozu.flywheel.mixin.instancemanage;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
@Mixin(ChunkRenderDispatcher.class)
public interface ChunkRenderDispatcherAccessor {
@Accessor
ClientLevel getLevel();
}

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.mixin;
package com.jozufozu.flywheel.mixin.instancemanage;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.mixin;
package com.jozufozu.flywheel.mixin.instancemanage;
import javax.annotation.Nullable;

View file

@ -0,0 +1,19 @@
package com.jozufozu.flywheel.mixin.instancemanage;
import java.util.Collection;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
import net.minecraft.world.level.block.entity.BlockEntity;
/**
* For use in {@link ChunkRebuildHooksMixin#addAndFilterBEs(ChunkRenderDispatcher.RenderChunk, Collection)}
*/
@Mixin(ChunkRenderDispatcher.RenderChunk.class)
public interface RenderChunkAccessor {
@Invoker("updateGlobalBlockEntities")
void flywheel$updateGlobalBlockEntities(Collection<BlockEntity> blockEntities);
}

View file

@ -0,0 +1,21 @@
package com.jozufozu.flywheel.mixin.instancemanage;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
@Mixin(ChunkRenderDispatcher.RenderChunk.class)
public class RenderChunkMixin implements com.jozufozu.flywheel.util.RenderChunkExtension {
@Shadow
@Final
private ChunkRenderDispatcher this$0;
@Override
public ClientLevel flywheel$getLevel() {
return ((ChunkRenderDispatcherAccessor) this$0).getLevel();
}
}

View file

@ -0,0 +1,7 @@
package com.jozufozu.flywheel.util;
import net.minecraft.client.multiplayer.ClientLevel;
public interface RenderChunkExtension {
ClientLevel flywheel$getLevel();
}

View file

@ -17,13 +17,13 @@ A modern engine for modded minecraft.'''
[[dependencies.flywheel]]
modId = "forge"
mandatory = true
versionRange = "[40.0.0,)"
versionRange = "[41.0.0,)"
ordering = "NONE"
side = "CLIENT"
[[dependencies.flywheel]]
modId = "minecraft"
mandatory = true
versionRange = "[1.18.2,1.19)"
versionRange = "[1.19,1.20)"
ordering = "NONE"
side = "CLIENT"

View file

@ -10,17 +10,20 @@
"BufferBuilderMixin",
"BufferUploaderMixin",
"CameraMixin",
"instancemanage.ChunkRebuildHooksMixin",
"instancemanage.ChunkRenderDispatcherAccessor",
"ClientLevelMixin",
"ChunkRebuildHooksMixin",
"EntityTypeMixin",
"FixFabulousDepthMixin",
"FrustumMixin",
"GlStateManagerMixin",
"InstanceAddMixin",
"InstanceRemoveMixin",
"instancemanage.InstanceAddMixin",
"instancemanage.InstanceRemoveMixin",
"LevelRendererAccessor",
"LevelRendererMixin",
"PausedPartialTickAccessor",
"instancemanage.RenderChunkAccessor",
"instancemanage.RenderChunkMixin",
"RenderTexturesMixin",
"RenderTypeMixin",
"atlas.AtlasDataMixin",