From b526d5a51aa2b4b6cac2c9d5bf1ea2ab1dee011c Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Wed, 30 Apr 2025 13:19:08 +1000 Subject: [PATCH] Wip fix overdraw --- .../core/rendering/ChunkBoundRenderer.java | 97 +++++++++++++++++++ .../core/rendering/SharedIndexBuffer.java | 1 + .../voxy/client/core/rendering/Viewport.java | 29 +++++- .../core/rendering/VoxyRenderSystem.java | 8 +- .../HierarchicalOcclusionTraverser.java | 12 +-- .../section/MDICSectionRenderer.java | 17 ++-- .../sodium/MixinRenderSectionManager.java | 10 ++ 7 files changed, 142 insertions(+), 32 deletions(-) create mode 100644 src/main/java/me/cortex/voxy/client/core/rendering/ChunkBoundRenderer.java diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/ChunkBoundRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/ChunkBoundRenderer.java new file mode 100644 index 00000000..3381a106 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/rendering/ChunkBoundRenderer.java @@ -0,0 +1,97 @@ +package me.cortex.voxy.client.core.rendering; + +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; +import me.cortex.voxy.client.core.gl.GlBuffer; +import me.cortex.voxy.client.core.rendering.util.UploadStream; +import org.lwjgl.system.MemoryUtil; + +import static org.lwjgl.opengl.GL11.GL_TRIANGLES; +import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE; +import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER; +import static org.lwjgl.opengl.GL15.glBindBuffer; +import static org.lwjgl.opengl.GL30.glBindBufferBase; +import static org.lwjgl.opengl.GL30.glBindVertexArray; +import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER; +import static org.lwjgl.opengl.GL31.glDrawElementsInstanced; +import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER; +import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER; + +//This is a render subsystem, its very simple in what it does +// it renders an AABB around loaded chunks, thats it +public class ChunkBoundRenderer { + public static final int MAX_CHUNK_COUNT = 10_000; + private final GlBuffer chunkPosBuffer = new GlBuffer(MAX_CHUNK_COUNT*8);//Stored as ivec2 + private final GlBuffer uniformBuffer = new GlBuffer(128); + private final Long2IntOpenHashMap chunk2idx = new Long2IntOpenHashMap(MAX_CHUNK_COUNT); + private final long[] idx2chunk = new long[MAX_CHUNK_COUNT]; + + public ChunkBoundRenderer() { + this.chunk2idx.defaultReturnValue(-1); + } + + public void addChunk(long pos) { + if (this.chunk2idx.containsKey(pos)) { + throw new IllegalArgumentException("Chunk already in map"); + } + int idx = this.chunk2idx.size(); + this.chunk2idx.put(pos, idx); + this.idx2chunk[idx] = pos; + + long ptr = UploadStream.INSTANCE.upload(this.chunkPosBuffer, 8L*idx, 8); + //Need to do it in 2 parts because ivec2 is 2 parts + MemoryUtil.memPutInt(ptr, (int)(pos&0xFFFFFFFFL)); ptr += 4; + MemoryUtil.memPutInt(ptr, (int)((pos>>>32)&0xFFFFFFFFL)); + UploadStream.INSTANCE.commit(); + } + + public void removeChunk(long pos) { + int idx = this.chunk2idx.remove(pos); + if (idx == -1) { + throw new IllegalArgumentException("Chunk pos not in map"); + } + if (idx == this.chunk2idx.size()-1) { + //Dont need to do anything as heap is already compact + return; + } + if (this.idx2chunk[idx] != pos) { + throw new IllegalStateException(); + } + + //Move last entry on heap to this index + long ePos = this.idx2chunk[this.chunk2idx.size()];// since is already removed size is correct end idx + if (this.chunk2idx.put(ePos, idx) == -1) { + throw new IllegalStateException(); + } + this.idx2chunk[idx] = ePos; + + //Put the end pos into the new idx + long ptr = UploadStream.INSTANCE.upload(this.chunkPosBuffer, 8L*idx, 8); + //Need to do it in 2 parts because ivec2 is 2 parts + MemoryUtil.memPutInt(ptr, (int)(ePos&0xFFFFFFFFL)); ptr += 4; + MemoryUtil.memPutInt(ptr, (int)((ePos>>>32)&0xFFFFFFFFL)); + UploadStream.INSTANCE.commit(); + } + + //Bind and render, changing as little gl state as possible so that the caller may configure how it wants to render + public void render(Viewport viewport) { + long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, 128); + viewport.MVP.getToAddress(ptr); ptr += 4*4*4; + viewport.section.getToAddress(ptr); ptr += 4*4; + viewport.innerTranslation.getToAddress(ptr); ptr += 4*4; + UploadStream.INSTANCE.commit(); + + //TODO: NOTE: need to reverse the winding order since we want the back faces of the AABB, not the front + //this.cullShader.bind(); + glBindVertexArray(RenderService.STATIC_VAO); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniformBuffer.id); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.chunkPosBuffer.id); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id()); + glDrawElementsInstanced(GL_TRIANGLES, 6*2*3, GL_UNSIGNED_BYTE, SharedIndexBuffer.CUBE_INDEX_OFFSET, this.chunk2idx.size()); + } + + public void free() { + this.uniformBuffer.free(); + this.chunkPosBuffer.free(); + } +} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/SharedIndexBuffer.java b/src/main/java/me/cortex/voxy/client/core/rendering/SharedIndexBuffer.java index 30f9dc13..b583c52a 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/SharedIndexBuffer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/SharedIndexBuffer.java @@ -9,6 +9,7 @@ import org.lwjgl.system.MemoryUtil; //Has a base index buffer of 16380 quads, and also a 1 cube byte index buffer at the end public class SharedIndexBuffer { + public static final int CUBE_INDEX_OFFSET = (1<<16)*6*2; public static final SharedIndexBuffer INSTANCE = new SharedIndexBuffer(); public static final SharedIndexBuffer INSTANCE_BYTE = new SharedIndexBuffer(true); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/Viewport.java b/src/main/java/me/cortex/voxy/client/core/rendering/Viewport.java index 9e3432b2..054d449b 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/Viewport.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/Viewport.java @@ -1,8 +1,7 @@ package me.cortex.voxy.client.core.rendering; -import org.joml.FrustumIntersection; -import org.joml.Matrix4f; -import org.joml.Vector4f; +import net.minecraft.util.math.MathHelper; +import org.joml.*; import java.lang.reflect.Field; @@ -28,6 +27,10 @@ public abstract class Viewport > { public double cameraY; public double cameraZ; + public final Matrix4f MVP = new Matrix4f(); + public final Vector3i section = new Vector3i(); + public final Vector3f innerTranslation = new Vector3f(); + protected Viewport() { Vector4f[] planes = null; try { @@ -67,8 +70,24 @@ public abstract class Viewport > { return (A) this; } - public A updateFrustum() { - this.frustum.set(new Matrix4f(this.projection).mul(this.modelView), false); + public A update() { + //MVP + this.projection.mul(this.modelView, this.MVP); + + //Update the frustum + this.frustum.set(this.MVP, false); + + //Translation vectors + int sx = MathHelper.floor(this.cameraX)>>5; + int sy = MathHelper.floor(this.cameraY)>>5; + int sz = MathHelper.floor(this.cameraZ)>>5; + this.section.set(sx, sy, sz); + + this.innerTranslation.set( + (float) (this.cameraX-(sx<<5)), + (float) (this.cameraY-(sy<<5)), + (float) (this.cameraZ-(sz<<5))); + return (A) this; } } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/VoxyRenderSystem.java b/src/main/java/me/cortex/voxy/client/core/rendering/VoxyRenderSystem.java index 0ba3a7a8..a47f37e5 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/VoxyRenderSystem.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/VoxyRenderSystem.java @@ -6,14 +6,11 @@ import me.cortex.voxy.client.TimingStatistics; import me.cortex.voxy.client.config.VoxyConfig; import me.cortex.voxy.client.core.gl.Capabilities; import me.cortex.voxy.client.core.gl.GlBuffer; -import me.cortex.voxy.client.core.model.ColourDepthTextureData; import me.cortex.voxy.client.core.model.ModelBakerySubsystem; -import me.cortex.voxy.client.core.model.bakery.ModelTextureBakery; import me.cortex.voxy.client.core.rendering.building.RenderDataFactory45; import me.cortex.voxy.client.core.rendering.building.RenderGenerationService; import me.cortex.voxy.client.core.rendering.post.PostProcessing; import me.cortex.voxy.client.core.rendering.util.DownloadStream; -import me.cortex.voxy.client.core.rendering.util.RawDownloadStream; import me.cortex.voxy.client.core.util.IrisUtil; import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.thread.ServiceThreadPool; @@ -22,7 +19,6 @@ import me.cortex.voxy.common.world.WorldSection; import me.cortex.voxy.common.world.other.Mapper; import me.cortex.voxy.commonImpl.VoxyCommon; import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses; -import net.minecraft.block.Blocks; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gl.GlBackend; import net.minecraft.client.render.Camera; @@ -30,9 +26,7 @@ import net.minecraft.client.render.Frustum; import net.minecraft.client.util.math.MatrixStack; import org.joml.Matrix4f; import org.lwjgl.opengl.GL11; -import org.lwjgl.system.MemoryUtil; -import java.lang.invoke.VarHandle; import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -180,7 +174,7 @@ public class VoxyRenderSystem { .setModelView(matrices.peek().getPositionMatrix()) .setCamera(cameraX, cameraY, cameraZ) .setScreenSize(MinecraftClient.getInstance().getFramebuffer().textureWidth, MinecraftClient.getInstance().getFramebuffer().textureHeight) - .updateFrustum(); + .update(); viewport.frameId++; diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical/HierarchicalOcclusionTraverser.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical/HierarchicalOcclusionTraverser.java index b898efc0..b4f9733d 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical/HierarchicalOcclusionTraverser.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical/HierarchicalOcclusionTraverser.java @@ -174,20 +174,14 @@ public class HierarchicalOcclusionTraverser { private void uploadUniform(Viewport viewport) { long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, 1024); - int sx = MathHelper.floor(viewport.cameraX)>>5; - int sy = MathHelper.floor(viewport.cameraY)>>5; - int sz = MathHelper.floor(viewport.cameraZ)>>5; - new Matrix4f(viewport.projection).mul(viewport.modelView).getToAddress(ptr); ptr += 4*4*4; + viewport.MVP.getToAddress(ptr); ptr += 4*4*4; - MemoryUtil.memPutInt(ptr, sx); ptr += 4; - MemoryUtil.memPutInt(ptr, sy); ptr += 4; - MemoryUtil.memPutInt(ptr, sz); ptr += 4; + viewport.section.getToAddress(ptr); ptr += 4*3; MemoryUtil.memPutFloat(ptr, viewport.width); ptr += 4; - var innerTranslation = new Vector3f((float) (viewport.cameraX-(sx<<5)), (float) (viewport.cameraY-(sy<<5)), (float) (viewport.cameraZ-(sz<<5))); - innerTranslation.getToAddress(ptr); ptr += 4*3; + viewport.innerTranslation.getToAddress(ptr); ptr += 4*3; MemoryUtil.memPutFloat(ptr, viewport.height); ptr += 4; diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/section/MDICSectionRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/section/MDICSectionRenderer.java index 2a37c605..4947478a 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/section/MDICSectionRenderer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/section/MDICSectionRenderer.java @@ -84,24 +84,19 @@ public class MDICSectionRenderer extends AbstractSectionRenderer>5; - int sy = MathHelper.floor(viewport.cameraY)>>5; - int sz = MathHelper.floor(viewport.cameraZ)>>5; - var mat = new Matrix4f(viewport.projection).mul(viewport.modelView); - var innerTranslation = new Vector3f((float) (viewport.cameraX-(sx<<5)), (float) (viewport.cameraY-(sy<<5)), (float) (viewport.cameraZ-(sz<<5))); - mat.translate(-innerTranslation.x, -innerTranslation.y, -innerTranslation.z); + var mat = new Matrix4f(viewport.MVP); + mat.translate(-viewport.innerTranslation.x, -viewport.innerTranslation.y, -viewport.innerTranslation.z); mat.getToAddress(ptr); ptr += 4*4*4; - MemoryUtil.memPutInt(ptr, sx); ptr += 4; - MemoryUtil.memPutInt(ptr, sy); ptr += 4; - MemoryUtil.memPutInt(ptr, sz); ptr += 4; + + viewport.section.getToAddress(ptr); ptr += 4*3; + if (viewport.frameId<0) { Logger.error("Frame ID negative, this will cause things to break, wrapping around"); viewport.frameId &= 0x7fffffff; } MemoryUtil.memPutInt(ptr, viewport.frameId&0x7fffffff); ptr += 4; - innerTranslation.getToAddress(ptr); ptr += 4*3; + viewport.innerTranslation.getToAddress(ptr); ptr += 4*3; UploadStream.INSTANCE.commit(); } diff --git a/src/main/java/me/cortex/voxy/client/mixin/sodium/MixinRenderSectionManager.java b/src/main/java/me/cortex/voxy/client/mixin/sodium/MixinRenderSectionManager.java index 2d5e4b04..bcda2c46 100644 --- a/src/main/java/me/cortex/voxy/client/mixin/sodium/MixinRenderSectionManager.java +++ b/src/main/java/me/cortex/voxy/client/mixin/sodium/MixinRenderSectionManager.java @@ -16,6 +16,16 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; public class MixinRenderSectionManager { @Shadow @Final private ClientWorld level; + @Inject(method = "onChunkAdded", at = @At("HEAD")) + private void voxy$trackChunkAdd(int x, int z, CallbackInfo ci) { + + } + + @Inject(method = "onChunkRemoved", at = @At("HEAD")) + private void voxy$trackChunkRemove(int x, int z, CallbackInfo ci) { + + } + @Inject(method = "onChunkRemoved", at = @At("HEAD")) private void injectIngest(int x, int z, CallbackInfo ci) { //TODO: Am not quite sure if this is right