This commit is contained in:
mcrcortex
2024-07-31 18:58:13 +10:00
parent aa65197749
commit 004bd1e751
11 changed files with 161 additions and 112 deletions

View File

@@ -20,7 +20,7 @@ public class GlPersistentMappedBuffer extends TrackedObject {
@Override
public void free() {
this.free0();
glUnmapBuffer(this.id);
glUnmapNamedBuffer(this.id);
glDeleteBuffers(this.id);
}

View File

@@ -2,6 +2,7 @@ package me.cortex.voxy.client.core.model;
import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
import me.cortex.voxy.client.core.rendering.util.RawDownloadStream;
import me.cortex.voxy.common.world.other.Mapper;
import java.lang.invoke.VarHandle;
@@ -12,20 +13,20 @@ public class ModelBakerySubsystem {
// basicly solves all the render stutter due to the baking
private final RawDownloadStream textureDownStream = new RawDownloadStream(8*1024*1024);//8mb downstream
private final ModelStore storage = new ModelStore();
public final ModelFactory factory;
private final IntLinkedOpenHashSet blockIdQueue = new IntLinkedOpenHashSet();
public ModelBakerySubsystem(Mapper mapper) {
this.factory = new ModelFactory(mapper, this.storage);
this.factory = new ModelFactory(mapper, this.storage, this.textureDownStream);
}
public void tick() {
//There should be a method to access the frame time IIRC, if the user framecap is unlimited lock it to like 60 fps for computation
int BUDGET = 20;//TODO: make this computed based on the remaining free time in a frame (and like div by 2 to reduce overhead) (with a min of 1)
int BUDGET = 50;//TODO: make this computed based on the remaining free time in a frame (and like div by 2 to reduce overhead) (with a min of 1)
for (int i = 0; i < BUDGET; i++) {
if (!this.blockIdQueue.isEmpty()) {
for (int i = 0; i < BUDGET && !this.blockIdQueue.isEmpty(); i++) {
int blockId = -1;
synchronized (this.blockIdQueue) {
if (!this.blockIdQueue.isEmpty()) {
@@ -38,15 +39,19 @@ public class ModelBakerySubsystem {
if (blockId != -1) {
this.factory.addEntry(blockId);
}
} else {
break;
}
}
//Submit is effectively free if nothing is submitted
this.textureDownStream.submit();
//Tick the download stream
this.textureDownStream.tick();
}
public void shutdown() {
this.factory.free();
this.storage.free();
this.textureDownStream.free();
}
public void requestBlockBake(int blockId) {
@@ -58,6 +63,6 @@ public class ModelBakerySubsystem {
}
public void addDebugData(List<String> debug) {
debug.add("MBQ/MBC: " + this.blockIdQueue.size() + "/"+ this.factory.getBakedCount());//Model bake queue/model baked count
debug.add("MQ/IF/MC: " + this.blockIdQueue.size() + "/" + this.factory.getInflightCount() + "/" + this.factory.getBakedCount());//Model bake queue/in flight/model baked count
}
}

View File

@@ -2,12 +2,14 @@ package me.cortex.voxy.client.core.model;
import com.mojang.blaze3d.platform.GlConst;
import com.mojang.blaze3d.platform.GlStateManager;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectSet;
import me.cortex.voxy.client.core.IGetVoxelCore;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.rendering.util.RawDownloadStream;
import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.world.other.Mapper;
import net.minecraft.block.BlockState;
@@ -72,7 +74,6 @@ public class ModelFactory {
private final Biome DEFAULT_BIOME = MinecraftClient.getInstance().world.getRegistryManager().get(RegistryKeys.BIOME).get(BiomeKeys.PLAINS);
public final ModelTextureBakery bakery;
private final int blockSampler = glGenSamplers();
//Model data might also contain a constant colour if the colour resolver produces a constant colour, this saves space in the
@@ -109,6 +110,9 @@ public class ModelFactory {
private final int[] idMappings;
private final Object2IntOpenHashMap<ModelEntry> modelTexture2id = new Object2IntOpenHashMap<>();
//Contains the set of all block ids that are currently inflight/being baked
// this is required due to "async" nature of gpu feedback
private final IntOpenHashSet blockStatesInFlight = new IntOpenHashSet();
private final List<Biome> biomes = new ArrayList<>();
private final List<Pair<Integer, BlockState>> modelsRequiringBiomeColours = new ArrayList<>();
@@ -117,13 +121,15 @@ public class ModelFactory {
private final Mapper mapper;
private final ModelStore storage;
private final RawDownloadStream downstream;
//TODO: NOTE!!! is it worth even uploading as a 16x16 texture, since automatic lod selection... doing 8x8 textures might be perfectly ok!!!
// this _quarters_ the memory requirements for the texture atlas!!! WHICH IS HUGE saving
public ModelFactory(Mapper mapper, ModelStore storage) {
public ModelFactory(Mapper mapper, ModelStore storage, RawDownloadStream downstream) {
this.mapper = mapper;
this.storage = storage;
this.downstream = downstream;
this.bakery = new ModelTextureBakery(MODEL_TEXTURE_SIZE, MODEL_TEXTURE_SIZE);
this.metadataCache = new long[1<<16];
@@ -132,12 +138,6 @@ public class ModelFactory {
Arrays.fill(this.idMappings, -1);
Arrays.fill(this.fluidStateLUT, -1);
glSamplerParameteri(this.blockSampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
glSamplerParameteri(this.blockSampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(this.blockSampler, GL_TEXTURE_MIN_LOD, 0);
glSamplerParameteri(this.blockSampler, GL_TEXTURE_MAX_LOD, 4);
this.modelTexture2id.defaultReturnValue(-1);
}
@@ -148,32 +148,82 @@ public class ModelFactory {
if (this.idMappings[blockId] != -1) {
return;
}
this.addEntry(blockId, this.mapper.getBlockStateFromBlockId(blockId));
//We are (probably) going to be baking the block id
// check that it is currently not inflight, if it is, return as its already being baked
// else add it to the flight as it is going to be baked
if (!this.blockStatesInFlight.add(blockId)) {
//Block baking is already in-flight
return;
}
var blockState = this.mapper.getBlockStateFromBlockId(blockId);
//Before we enqueue the baking of this blockstate, we must check if it has a fluid state associated with it
// if it does, we must ensure that it is (effectivly) baked BEFORE we bake this blockstate
boolean isFluid = blockState.getBlock() instanceof FluidBlock;
if ((!isFluid) && (!blockState.getFluidState().isEmpty())) {
//Insert into the fluid LUT
var fluidState = blockState.getFluidState().getBlockState();
int fluidStateId = this.mapper.getIdForBlockState(fluidState);
if (this.idMappings[fluidStateId] == -1) {
//Dont have to check for inflight as that is done recursively :p
//This is a hack but does work :tm: due to how the download stream is setup
// it should enforce that the fluid state is processed before our blockstate
addEntry(fluidStateId);
}
}
int TOTAL_FACES_TEXTURE_SIZE = MODEL_TEXTURE_SIZE*MODEL_TEXTURE_SIZE*2*4*6;// since both depth and colour are packed together, 6 faces, 4 bytes per pixel
int allocation = this.downstream.download(TOTAL_FACES_TEXTURE_SIZE, ptr->{
ColourDepthTextureData[] textureData = new ColourDepthTextureData[6];
final int FACE_SIZE = MODEL_TEXTURE_SIZE*MODEL_TEXTURE_SIZE;
for (int face = 0; face < 6; face++) {
long faceDataPtr = ptr + (FACE_SIZE*4)*face*2;
int[] colour = new int[FACE_SIZE];
int[] depth = new int[FACE_SIZE];
//TODO: see if there is a memory intrinsic to help here
//Copy out colour
for (int i = 0; i < FACE_SIZE; i++) {
colour[i] = MemoryUtil.memGetInt(faceDataPtr+ (i*4));
}
//Shift ptr and copy out depth
faceDataPtr += FACE_SIZE*4;
for (int i = 0; i < FACE_SIZE; i++) {
depth[i] = MemoryUtil.memGetInt(faceDataPtr+ (i*4));
}
textureData[face] = new ColourDepthTextureData(colour, depth, MODEL_TEXTURE_SIZE, MODEL_TEXTURE_SIZE);
}
processTextureBakeResult(blockId, blockState, textureData);
});
this.bakery.renderFacesToStream(blockState, 123456, isFluid, this.downstream.getBufferId(), allocation);
}
//TODO: what i need to do is seperate out fluid states from blockStates
//Processes the results of the baking, its a seperate function due to the flight
private void processBakingResult() {
}
//TODO: so need a few things, per face sizes and offsets, the sizes should be computed from the pixels and find the minimum bounding pixel
// while the depth is computed from the depth buffer data
public void addEntry(int blockId, BlockState blockState) {
//This is
private void processTextureBakeResult(int blockId, BlockState blockState, ColourDepthTextureData[] textureData) {
if (this.idMappings[blockId] != -1) {
//System.err.println("Block id already added: " + blockId + " for state: " + blockState);
return;
//This should be impossible to reach as it means that multiple bakes for the same blockId happened and where inflight at the same time!
throw new IllegalStateException("Block id already added: " + blockId + " for state: " + blockState);
}
if (!this.blockStatesInFlight.remove(blockId)) {
throw new IllegalStateException("processing a texture bake result but the block state was not in flight!!");
}
boolean isFluid = blockState.getBlock() instanceof FluidBlock;
int modelId = -1;
//TODO: FIRST!! dispatch a face request the fluid state if it doesnt exist!!!
// THEN dispatch this block face request, the ordering should result in a gurentee that the fluid block state is
// computed before this block state
var textureData = this.bakery.renderFaces(blockState, 123456, isFluid);
int clientFluidStateId = -1;
@@ -183,11 +233,9 @@ public class ModelFactory {
int fluidStateId = this.mapper.getIdForBlockState(fluidState);
clientFluidStateId = this.idMappings[fluidStateId];
if (clientFluidStateId == -1) {
this.addEntry(fluidStateId, fluidState);
clientFluidStateId = this.idMappings[fluidStateId];
throw new IllegalStateException("Block has a fluid state but fluid state is not already baked!!!");
}
}
@@ -609,16 +657,15 @@ public class ModelFactory {
}
}
public int getSamplerId() {
return this.blockSampler;
}
public void free() {
this.bakery.free();
glDeleteSamplers(this.blockSampler);
}
public int getBakedCount() {
return this.modelTexture2id.size();
}
public int getInflightCount() {
return this.blockStatesInFlight.size();
}
}

View File

@@ -3,18 +3,33 @@ package me.cortex.voxy.client.core.model;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlTexture;
import static org.lwjgl.opengl.GL11.GL_RGBA8;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL11C.GL_NEAREST;
import static org.lwjgl.opengl.GL11C.GL_NEAREST_MIPMAP_LINEAR;
import static org.lwjgl.opengl.GL12C.GL_TEXTURE_MAX_LOD;
import static org.lwjgl.opengl.GL12C.GL_TEXTURE_MIN_LOD;
import static org.lwjgl.opengl.GL33.glDeleteSamplers;
import static org.lwjgl.opengl.GL33.glGenSamplers;
import static org.lwjgl.opengl.GL33C.glSamplerParameteri;
public class ModelStore {
public static final int MODEL_SIZE = 64;
final GlBuffer modelBuffer;
final GlBuffer modelColourBuffer;
final GlTexture textures;
public final int blockSampler = glGenSamplers();
public ModelStore() {
this.modelBuffer = new GlBuffer(MODEL_SIZE * (1<<16));
this.modelColourBuffer = new GlBuffer(4 * (1<<16));
this.textures = new GlTexture().store(GL_RGBA8, 4, ModelFactory.MODEL_TEXTURE_SIZE*3*256,ModelFactory.MODEL_TEXTURE_SIZE*2*256);
glSamplerParameteri(this.blockSampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
glSamplerParameteri(this.blockSampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(this.blockSampler, GL_TEXTURE_MIN_LOD, 0);
glSamplerParameteri(this.blockSampler, GL_TEXTURE_MAX_LOD, 4);
}
@@ -22,5 +37,6 @@ public class ModelStore {
this.modelBuffer.free();
this.modelColourBuffer.free();
this.textures.free();
glDeleteSamplers(this.blockSampler);
}
}

View File

@@ -4,6 +4,7 @@ import com.mojang.blaze3d.platform.GlConst;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.systems.VertexSorter;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlFramebuffer;
import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.gl.shader.Shader;
@@ -34,17 +35,18 @@ import org.lwjgl.opengl.GL11C;
import java.util.ArrayList;
import java.util.List;
import static org.lwjgl.opengl.ARBFramebufferObject.*;
import static org.lwjgl.opengl.ARBDirectStateAccess.glGetTextureImage;
import static org.lwjgl.opengl.ARBImaging.GL_FUNC_ADD;
import static org.lwjgl.opengl.ARBImaging.glBlendEquation;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_FRAMEBUFFER_BARRIER_BIT;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.glMemoryBarrier;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL13.*;
import static org.lwjgl.opengl.GL11C.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL14C.glBlendFuncSeparate;
import static org.lwjgl.opengl.GL15C.glBindBuffer;
import static org.lwjgl.opengl.GL20C.glUniformMatrix4fv;
import static org.lwjgl.opengl.GL45C.glBlitNamedFramebuffer;
import static org.lwjgl.opengl.GL45C.glGetTextureImage;
import static org.lwjgl.opengl.GL21C.GL_PIXEL_PACK_BUFFER;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.opengl.GL43.glCopyImageSubData;
//Builds a texture for each face of a model
public class ModelTextureBakery {
@@ -69,11 +71,13 @@ public class ModelTextureBakery {
private static final List<MatrixStack> FACE_VIEWS = new ArrayList<>();
//A truly terrible hackfix for nvidia drivers murderizing performance with PBO readout with a depth texture
private static final GlTexture TEMPORARY = new GlTexture().store(GL_R32UI, 1, ModelFactory.MODEL_TEXTURE_SIZE, ModelFactory.MODEL_TEXTURE_SIZE);
public ModelTextureBakery(int width, int height) {
//TODO: Make this run in a seperate opengl context so that it can run in a seperate thread
this.width = width;
this.height = height;
this.colourTex = new GlTexture().store(GL_RGBA8, 1, width, height);
@@ -86,6 +90,8 @@ public class ModelTextureBakery {
}
private static void AddViews() {
//TODO: FIXME: need to bake in the correct orientation, HOWEVER some orientations require a flipped winding order!!!!
addView(-90,0, 0, false);//Direction.DOWN
addView(90,0, 0, false);//Direction.UP
addView(0,180, 0, true);//Direction.NORTH
@@ -109,7 +115,7 @@ public class ModelTextureBakery {
//TODO: For block entities, also somehow attempt to render the default block entity, e.g. chests and stuff
// cause that will result in ok looking micro details in the terrain
public ColourDepthTextureData[] renderFaces(BlockState state, long randomValue, boolean renderFluid) {
public void renderFacesToStream(BlockState state, long randomValue, boolean renderFluid, int streamBuffer, int streamBaseOffset) {
this.glState.capture();
var model = MinecraftClient.getInstance()
.getBakedModelManager()
@@ -174,10 +180,10 @@ public class ModelTextureBakery {
int texId = MinecraftClient.getInstance().getTextureManager().getTexture(Identifier.of("minecraft", "textures/atlas/blocks.png")).getGlId();
GlUniform.uniform1(0, 0);
var faces = new ColourDepthTextureData[FACE_VIEWS.size()];
for (int i = 0; i < faces.length; i++) {
faces[i] = captureView(state, model, entityModel, FACE_VIEWS.get(i), randomValue, i, renderFluid, texId, projection);
//glBlitNamedFramebuffer(this.framebuffer.id, oldFB, 0,0,16,16,300*(i>>1),300*(i&1),300*(i>>1)+256,300*(i&1)+256, GL_COLOR_BUFFER_BIT, GL_NEAREST);
final int TEXTURE_SIZE = this.width*this.height *4;//NOTE! assume here that both depth and colour are 4 bytes in size
for (int i = 0; i < FACE_VIEWS.size(); i++) {
int faceOffset = streamBaseOffset + TEXTURE_SIZE*i*2;
captureViewToStream(state, model, entityModel, FACE_VIEWS.get(i), randomValue, i, renderFluid, texId, projection, streamBuffer, faceOffset, faceOffset + TEXTURE_SIZE);
}
renderLayer.endDrawing();
@@ -190,11 +196,10 @@ public class ModelTextureBakery {
//TODO: FIXME: fully revert the state of opengl
this.glState.restore();
return faces;
}
private final BufferAllocator allocator = new BufferAllocator(786432);
private ColourDepthTextureData captureView(BlockState state, BakedModel model, BakedBlockEntityModel blockEntityModel, MatrixStack stack, long randomValue, int face, boolean renderFluid, int textureId, Matrix4f projection) {
private void captureViewToStream(BlockState state, BakedModel model, BakedBlockEntityModel blockEntityModel, MatrixStack stack, long randomValue, int face, boolean renderFluid, int textureId, Matrix4f projection, int streamBuffer, int streamColourOffset, int streamDepthOffset) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
float[] mat = new float[4*4];
new Matrix4f(projection).mul(stack.peek().getPositionMatrix()).get(mat);
@@ -286,14 +291,28 @@ public class ModelTextureBakery {
}
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT);
int[] colourData = new int[this.width*this.height];
int[] depthData = new int[this.width*this.height];
//TODO: USE A PBO to make a download stream of the textures to a buffer to download then do a download stream
//glGetTextureImage(this.colourTex.id, 0, GL_RGBA, GL_UNSIGNED_BYTE, colourData);
//glGetTextureImage(this.depthTex.id, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, depthData);
GlStateManager._pixelStore(GL_PACK_ROW_LENGTH, 0);
GlStateManager._pixelStore(GL_PACK_SKIP_PIXELS, 0);
GlStateManager._pixelStore(GL_PACK_SKIP_ROWS, 0);
GlStateManager._pixelStore(GL_PACK_ALIGNMENT, 4);
return new ColourDepthTextureData(colourData, depthData, this.width, this.height);
glBindBuffer(GL_PIXEL_PACK_BUFFER, streamBuffer);
{//Copy colour
glGetTextureImage(this.colourTex.id, 0, GL_RGBA, GL_UNSIGNED_BYTE, this.width*this.height*4, streamColourOffset);
}
//TODO: fixme!! only do this dodgy double copy if the driver is nvidia
{//Copy depth
//First copy to the temporary buffer
glCopyImageSubData(textureId, GL_TEXTURE_2D, 0,0,0,0,
TEMPORARY.id, GL_TEXTURE_2D, 0,0,0,0,
ModelFactory.MODEL_TEXTURE_SIZE, ModelFactory.MODEL_TEXTURE_SIZE, 1);
//Then download
glGetTextureImage(TEMPORARY.id, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, this.width*this.height*4, streamDepthOffset);
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}
private static void renderQuads(BufferBuilder builder, BlockState state, BakedModel model, MatrixStack stack, long randomValue) {

View File

@@ -93,6 +93,7 @@ public class TextureUtils {
float depthF = (float) ((double)depth/((1<<24)-1));
//https://registry.khronos.org/OpenGL-Refpages/gl4/html/glDepthRange.xhtml
// due to this and the unsigned bullshit, i believe the depth value needs to get multiplied by 2
depthF *= 2;
if (depthF > 1.00001f) {
System.err.println("Warning: Depth greater than 1");

View File

@@ -114,7 +114,8 @@ public abstract class AbstractFarWorldRenderer <T extends Viewport, J extends Ab
//Do any BlockChanges
while ((!this.blockStateUpdates.isEmpty()) && (maxUpdatesPerFrame-- > 0)) {
var update = this.blockStateUpdates.pop();
this.models.addEntry(update.id, update.state);
//this.models.addEntry(update.id, update.state);
System.err.println("DEFUNKED: " + update);
}
//this.models.bakery.renderFaces(Blocks.ROSE_BUSH.getDefaultState(), 1234, false);
}

View File

@@ -70,7 +70,7 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer<Gl46Viewport,
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id());
//Bind the texture atlas
glBindSampler(0, this.models.getSamplerId());
//glBindSampler(0, this.models.getSamplerId());
//glBindTextureUnit(0, this.models.getTextureId());
}
@@ -190,7 +190,7 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer<Gl46Viewport,
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBindSampler(0, this.models.getSamplerId());
//glBindSampler(0, this.models.getSamplerId());
//glBindTextureUnit(0, this.models.getTextureId());
//RenderSystem.blendFunc(GlStateManager.SrcFactor.ONE, GlStateManager.DstFactor.ONE);

View File

@@ -91,30 +91,6 @@ public class Gl46HierarchicalRenderer {
UploadStream.INSTANCE.tick();
DownloadStream.INSTANCE.tick();
}
{
boolean didHaveBiomeChange = false;
//Do any BiomeChanges
while (!this.biomeUpdates.isEmpty()) {
var update = this.biomeUpdates.pop();
var biomeReg = MinecraftClient.getInstance().world.getRegistryManager().get(RegistryKeys.BIOME);
this.modelManager.addBiome(update.id, biomeReg.get(Identifier.of(update.biome)));
didHaveBiomeChange = true;
}
if (didHaveBiomeChange) {
UploadStream.INSTANCE.commit();
}
int maxUpdatesPerFrame = 40;
//Do any BlockChanges
while ((!this.blockStateUpdates.isEmpty()) && (maxUpdatesPerFrame-- > 0)) {
var update = this.blockStateUpdates.pop();
this.modelManager.addEntry(update.id, update.state);
}
}
}

View File

@@ -1,15 +0,0 @@
package me.cortex.voxy.client.mixin.minecraft;
import com.mojang.blaze3d.systems.RenderSystem;
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;
@Mixin(RenderSystem.class)
public class MixinRenderSystem {
@Inject(method = {"assertOnRenderThread", "assertOnRenderThreadOrInit"}, at = @At("HEAD"), cancellable = true)
private static void cancelAssert(CallbackInfo ci) {
ci.cancel();
}
}

View File

@@ -7,7 +7,6 @@
"minecraft.MixinClientChunkManager",
"minecraft.MixinDebugHud",
"minecraft.MixinMinecraftClient",
"minecraft.MixinRenderSystem",
"minecraft.MixinWorldRenderer",
"nvidium.MixinRenderPipeline",
"sodium.MixinDefaultChunkRenderer",