diff --git a/src/main/java/me/cortex/voxy/client/core/VoxelCore.java b/src/main/java/me/cortex/voxy/client/core/VoxelCore.java index f6d5d2e9..7a896f9f 100644 --- a/src/main/java/me/cortex/voxy/client/core/VoxelCore.java +++ b/src/main/java/me/cortex/voxy/client/core/VoxelCore.java @@ -68,12 +68,17 @@ public class VoxelCore { //Trigger the shared index buffer loading SharedIndexBuffer.INSTANCE.id(); - if (VoxyConfig.CONFIG.useMeshShaders()) { - this.renderer = new NvMeshFarWorldRenderer(VoxyConfig.CONFIG.geometryBufferSize, VoxyConfig.CONFIG.maxSections); - System.out.println("Using NvMeshFarWorldRenderer"); + if (true) { + this.renderer = new Gl46MeshletsFarWorldRenderer(VoxyConfig.CONFIG.geometryBufferSize, VoxyConfig.CONFIG.maxSections); + System.out.println("Using Gl46MeshletFarWorldRendering"); } else { - this.renderer = new Gl46FarWorldRenderer(VoxyConfig.CONFIG.geometryBufferSize, VoxyConfig.CONFIG.maxSections); - System.out.println("Using Gl46FarWorldRenderer"); + if (VoxyConfig.CONFIG.useMeshShaders()) { + this.renderer = new NvMeshFarWorldRenderer(VoxyConfig.CONFIG.geometryBufferSize, VoxyConfig.CONFIG.maxSections); + System.out.println("Using NvMeshFarWorldRenderer"); + } else { + this.renderer = new Gl46FarWorldRenderer(VoxyConfig.CONFIG.geometryBufferSize, VoxyConfig.CONFIG.maxSections); + System.out.println("Using Gl46FarWorldRenderer"); + } } this.viewportSelector = new ViewportSelector<>(this.renderer::createViewport); System.out.println("Renderer initialized"); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/AbstractFarWorldRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/AbstractFarWorldRenderer.java index 488d92bd..977eb7e9 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/AbstractFarWorldRenderer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/AbstractFarWorldRenderer.java @@ -40,11 +40,11 @@ import static org.lwjgl.opengl.GL30.*; //Todo: tinker with having the compute shader where each thread is a position to render? maybe idk -public abstract class AbstractFarWorldRenderer { +public abstract class AbstractFarWorldRenderer { public static final int STATIC_VAO = glGenVertexArrays(); protected final GlBuffer uniformBuffer; - protected final GeometryManager geometry; + protected final J geometry; protected final ModelManager models; protected final GlBuffer lightDataBuffer; @@ -63,11 +63,11 @@ public abstract class AbstractFarWorldRenderer { private final ConcurrentLinkedDeque blockStateUpdates = new ConcurrentLinkedDeque<>(); private final ConcurrentLinkedDeque biomeUpdates = new ConcurrentLinkedDeque<>(); - public AbstractFarWorldRenderer(int geometrySize, int maxSections) { - this.maxSections = maxSections; + public AbstractFarWorldRenderer(J geometry) { + this.maxSections = geometry.getMaxSections(); this.uniformBuffer = new GlBuffer(1024); this.lightDataBuffer = new GlBuffer(256*4);//256 of uint - this.geometry = new GeometryManager(geometrySize*8L, maxSections); + this.geometry = geometry; this.models = new ModelManager(16); } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/AbstractGeometryManager.java b/src/main/java/me/cortex/voxy/client/core/rendering/AbstractGeometryManager.java new file mode 100644 index 00000000..b1adcfc6 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/rendering/AbstractGeometryManager.java @@ -0,0 +1,35 @@ +package me.cortex.voxy.client.core.rendering; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import me.cortex.voxy.client.core.rendering.building.BuiltSection; + +import java.util.concurrent.ConcurrentLinkedDeque; + +public abstract class AbstractGeometryManager { + protected int sectionCount = 0; + protected final int maxSections; + protected final ConcurrentLinkedDeque buildResults = new ConcurrentLinkedDeque<>(); + + protected AbstractGeometryManager(int maxSections) { + this.maxSections = maxSections; + } + + abstract IntArrayList uploadResults(); + + int getMaxSections() { + return this.maxSections; + } + + public void enqueueResult(BuiltSection sectionGeometry) { + this.buildResults.add(sectionGeometry); + } + + public abstract float getGeometryBufferUsage(); + + public int getSectionCount() { + return this.sectionCount; + } + + public abstract void free(); + +} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/GeometryManager.java b/src/main/java/me/cortex/voxy/client/core/rendering/DefaultGeometryManager.java similarity index 92% rename from src/main/java/me/cortex/voxy/client/core/rendering/GeometryManager.java rename to src/main/java/me/cortex/voxy/client/core/rendering/DefaultGeometryManager.java index 9aa33975..fd331661 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/GeometryManager.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/DefaultGeometryManager.java @@ -14,11 +14,8 @@ import org.lwjgl.system.MemoryUtil; import java.util.concurrent.ConcurrentLinkedDeque; -public class GeometryManager { +public class DefaultGeometryManager extends AbstractGeometryManager { private static final int SECTION_METADATA_SIZE = 32; - - private final ConcurrentLinkedDeque buildResults = new ConcurrentLinkedDeque<>(); - private int sectionCount = 0; private final Long2IntOpenHashMap pos2id = new Long2IntOpenHashMap(); private final LongArrayList id2pos = new LongArrayList(); private final ObjectArrayList sectionMetadata = new ObjectArrayList<>(); @@ -27,7 +24,8 @@ public class GeometryManager { private final GlBuffer sectionMetaBuffer; private final BufferArena geometryBuffer; - public GeometryManager(long geometryBufferSize, int maxSections) { + public DefaultGeometryManager(long geometryBufferSize, int maxSections) { + super(maxSections); this.sectionMetaBuffer = new GlBuffer(((long) maxSections) * SECTION_METADATA_SIZE); this.geometryBuffer = new BufferArena(geometryBufferSize, 8); this.pos2id.defaultReturnValue(-1); @@ -120,14 +118,6 @@ public class GeometryManager { return this.markSectionIds; } - public void enqueueResult(BuiltSection sectionGeometry) { - this.buildResults.add(sectionGeometry); - } - - public int getSectionCount() { - return this.sectionCount; - } - public void free() { while (!this.buildResults.isEmpty()) { this.buildResults.pop().free(); @@ -156,7 +146,7 @@ public class GeometryManager { //TODO: pack the offsets of each axis so that implicit face culling can work //Note! the opaquePreDataCount and translucentPreDataCount are never writen to the meta buffer, as they are indexed in reverse relative to the base opaque and translucent geometry - private record SectionMeta(long position, int aabb, int geometryPtr, int size, int[] offsets) { + protected record SectionMeta(long position, int aabb, int geometryPtr, int size, int[] offsets) { public void writeMetadata(long ptr) { //THIS IS DUE TO ENDIANNESS and that we are splitting a long into 2 ints MemoryUtil.memPutInt(ptr, (int) (this.position>>32)); ptr += 4; @@ -171,7 +161,7 @@ public class GeometryManager { } } - private SectionMeta createMeta(BuiltSection geometry) { + protected SectionMeta createMeta(BuiltSection geometry) { int geometryPtr = (int) this.geometryBuffer.upload(geometry.geometryBuffer); if (geometryPtr == -1) { String msg = "Buffer arena out of memory, please increase it in settings or decrease LoD quality"; @@ -182,10 +172,9 @@ public class GeometryManager { return new SectionMeta(geometry.position, geometry.aabb, geometryPtr, (int) (geometry.geometryBuffer.size/8), geometry.offsets); } - private void freeMeta(SectionMeta meta) { + protected void freeMeta(SectionMeta meta) { if (meta.geometryPtr != -1) { this.geometryBuffer.free(meta.geometryPtr); } } - } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/FrustumLoDComputation.java b/src/main/java/me/cortex/voxy/client/core/rendering/FrustumLoDComputation.java deleted file mode 100644 index 29558e5f..00000000 --- a/src/main/java/me/cortex/voxy/client/core/rendering/FrustumLoDComputation.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.cortex.voxy.client.core.rendering; - -public class FrustumLoDComputation { - -} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/Gl46FarWorldRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/Gl46FarWorldRenderer.java index 36a9c3b7..add32e24 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/Gl46FarWorldRenderer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/Gl46FarWorldRenderer.java @@ -34,7 +34,7 @@ import static org.lwjgl.opengl.GL45.glBindTextureUnit; import static org.lwjgl.opengl.GL45.glClearNamedBufferData; import static org.lwjgl.opengl.GL45C.nglClearNamedBufferData; -public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { +public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { private final Shader commandGen = Shader.make() .add(ShaderType.COMPUTE, "voxy:lod/gl46/cmdgen.comp") .compile(); @@ -55,7 +55,7 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer private final GlBuffer glCommandCountBuffer; public Gl46FarWorldRenderer(int geometryBuffer, int maxSections) { - super(geometryBuffer, maxSections); + super(new DefaultGeometryManager(geometryBuffer*8L, maxSections)); this.glCommandBuffer = new GlBuffer(maxSections*5L*4 * 6); this.glCommandCountBuffer = new GlBuffer(4*2); nglClearNamedBufferData(this.glCommandBuffer.id, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, 0); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/Gl46MeshletViewport.java b/src/main/java/me/cortex/voxy/client/core/rendering/Gl46MeshletViewport.java new file mode 100644 index 00000000..6ff23f0f --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/rendering/Gl46MeshletViewport.java @@ -0,0 +1,21 @@ +package me.cortex.voxy.client.core.rendering; + +import me.cortex.voxy.client.core.gl.GlBuffer; + +import static org.lwjgl.opengl.GL30C.GL_R8UI; +import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER; +import static org.lwjgl.opengl.GL42.GL_UNSIGNED_BYTE; +import static org.lwjgl.opengl.GL45C.nglClearNamedBufferData; + +public class Gl46MeshletViewport extends Viewport { + GlBuffer visibilityBuffer; + public Gl46MeshletViewport(Gl46MeshletsFarWorldRenderer renderer) { + super(renderer); + this.visibilityBuffer = new GlBuffer(renderer.maxSections*4L); + nglClearNamedBufferData(this.visibilityBuffer.id, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, 0); + } + + protected void delete0() { + this.visibilityBuffer.free(); + } +} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/Gl46MeshletsFarWorldRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/Gl46MeshletsFarWorldRenderer.java new file mode 100644 index 00000000..7bf7be12 --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/rendering/Gl46MeshletsFarWorldRenderer.java @@ -0,0 +1,164 @@ +package me.cortex.voxy.client.core.rendering; + +import me.cortex.voxy.client.core.gl.GlBuffer; +import me.cortex.voxy.client.core.gl.shader.Shader; +import me.cortex.voxy.client.core.gl.shader.ShaderType; +import me.cortex.voxy.client.core.rendering.util.UploadStream; +import me.cortex.voxy.client.mixin.joml.AccessFrustumIntersection; +import net.minecraft.client.render.RenderLayer; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.lwjgl.system.MemoryUtil; + +import static org.lwjgl.opengl.ARBIndirectParameters.GL_PARAMETER_BUFFER_ARB; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL14C.glBlendFuncSeparate; +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.GL30C.GL_RED_INTEGER; +import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER; +import static org.lwjgl.opengl.GL31.glDrawElementsInstanced; +import static org.lwjgl.opengl.GL33.glBindSampler; +import static org.lwjgl.opengl.GL40.glDrawElementsIndirect; +import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER; +import static org.lwjgl.opengl.GL42.GL_FRAMEBUFFER_BARRIER_BIT; +import static org.lwjgl.opengl.GL42.glMemoryBarrier; +import static org.lwjgl.opengl.GL43.*; +import static org.lwjgl.opengl.GL45.glBindTextureUnit; +import static org.lwjgl.opengl.GL45.nglClearNamedBufferSubData; +import static org.lwjgl.opengl.GL45C.nglClearNamedBufferData; +import static org.lwjgl.opengl.NVMeshShader.glDrawMeshTasksNV; +import static org.lwjgl.opengl.NVRepresentativeFragmentTest.GL_REPRESENTATIVE_FRAGMENT_TEST_NV; + +public class Gl46MeshletsFarWorldRenderer extends AbstractFarWorldRenderer { + private final Shader meshletGenerator = Shader.make() + .add(ShaderType.COMPUTE, "voxy:lod/gl46mesh/cmdgen.comp") + .compile(); + + private final Shader lodShader = Shader.make() + .add(ShaderType.VERTEX, "voxy:lod/gl46mesh/quads.vert") + .add(ShaderType.FRAGMENT, "voxy:lod/gl46mesh/quads.frag") + .compile(); + + private final Shader cullShader = Shader.make() + .add(ShaderType.VERTEX, "voxy:lod/gl46mesh/cull.vert") + .add(ShaderType.FRAGMENT, "voxy:lod/gl46mesh/cull.frag") + .compile(); + + private final GlBuffer glDrawIndirect; + private final GlBuffer meshletBuffer; + + public Gl46MeshletsFarWorldRenderer(int geometrySize, int maxSections) { + super(new DefaultGeometryManager(geometrySize*8L, maxSections)); + this.glDrawIndirect = new GlBuffer(4*5); + this.meshletBuffer = new GlBuffer(4*1000000);//TODO: Make max meshlet count configurable + } + + protected void bindResources(Gl46MeshletViewport viewport) { + glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniformBuffer.id); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometry.geometryId()); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.glDrawIndirect.id); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, this.meshletBuffer.id); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, this.geometry.metaId()); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, viewport.visibilityBuffer.id); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.models.getBufferId()); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, this.models.getColourBufferId()); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 8, this.lightDataBuffer.id);//Lighting LUT + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, this.glDrawIndirect.id); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id()); + + //Bind the texture atlas + glBindSampler(0, this.models.getSamplerId()); + glBindTextureUnit(0, this.models.getTextureId()); + } + + private void updateUniformBuffer(Gl46MeshletViewport viewport) { + long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, this.uniformBuffer.size()); + + var mat = new Matrix4f(viewport.projection).mul(viewport.modelView); + var innerTranslation = new Vector3f((float) (viewport.cameraX-(this.sx<<5)), (float) (viewport.cameraY-(this.sy<<5)), (float) (viewport.cameraZ-(this.sz<<5))); + mat.translate(-innerTranslation.x, -innerTranslation.y, -innerTranslation.z); + mat.getToAddress(ptr); ptr += 4*4*4; + MemoryUtil.memPutInt(ptr, this.sx); ptr += 4; + MemoryUtil.memPutInt(ptr, this.sy); ptr += 4; + MemoryUtil.memPutInt(ptr, this.sz); ptr += 4; + MemoryUtil.memPutInt(ptr, this.geometry.getSectionCount()); ptr += 4; + var planes = ((AccessFrustumIntersection)this.frustum).getPlanes(); + for (var plane : planes) { + plane.getToAddress(ptr); ptr += 4*4; + } + innerTranslation.getToAddress(ptr); ptr += 4*3; + MemoryUtil.memPutInt(ptr, viewport.frameId++); ptr += 4; + } + + @Override + public void renderFarAwayOpaque(Gl46MeshletViewport viewport) { + if (this.geometry.getSectionCount() == 0) { + return; + } + + {//Mark all of the updated sections as being visible from last frame + for (int id : this.updatedSectionIds) { + long ptr = UploadStream.INSTANCE.upload(viewport.visibilityBuffer, id * 4L, 4); + MemoryUtil.memPutInt(ptr, viewport.frameId - 1);//(visible from last frame) + } + } + + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + + this.updateUniformBuffer(viewport); + UploadStream.INSTANCE.commit(); + glBindVertexArray(AbstractFarWorldRenderer.STATIC_VAO); + + nglClearNamedBufferSubData(this.glDrawIndirect.id, GL_R32UI, 4, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, 0); + + this.meshletGenerator.bind(); + this.bindResources(viewport); + glDispatchCompute((this.geometry.getSectionCount()+63)/64, 1, 1); + + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT); + + this.lodShader.bind(); + this.bindResources(viewport); + glDisable(GL_CULL_FACE); + glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, 0); + glEnable(GL_CULL_FACE); + + glMemoryBarrier(GL_PIXEL_BUFFER_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT); + + this.cullShader.bind(); + this.bindResources(viewport); + glDepthMask(false); + glColorMask(false, false, false, false); + glDrawElementsInstanced(GL_TRIANGLES, 6 * 2 * 3, GL_UNSIGNED_BYTE, (1 << 16) * 6 * 2, this.geometry.getSectionCount()); + glColorMask(true, true, true, true); + glDepthMask(true); + + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); + } + + + @Override + public void renderFarAwayTranslucent(Gl46MeshletViewport viewport) { + + } + + @Override + protected Gl46MeshletViewport createViewport0() { + return new Gl46MeshletViewport(this); + } + + @Override + public void shutdown() { + super.shutdown(); + this.meshletGenerator.free(); + this.lodShader.free(); + this.cullShader.free(); + + this.glDrawIndirect.free(); + this.meshletBuffer.free(); + } +} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/Gl46Viewport.java b/src/main/java/me/cortex/voxy/client/core/rendering/Gl46Viewport.java index 761daf26..dbabf9a1 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/Gl46Viewport.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/Gl46Viewport.java @@ -9,7 +9,7 @@ import static org.lwjgl.opengl.GL42.GL_UNSIGNED_BYTE; import static org.lwjgl.opengl.GL45C.glClearNamedBufferData; import static org.lwjgl.opengl.GL45C.nglClearNamedBufferData; -public class Gl46Viewport extends Viewport { +public class Gl46Viewport extends Viewport { GlBuffer visibilityBuffer; public Gl46Viewport(Gl46FarWorldRenderer renderer) { super(renderer); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/NvMeshFarWorldRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/NvMeshFarWorldRenderer.java index 24507d2d..b8547d13 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/NvMeshFarWorldRenderer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/NvMeshFarWorldRenderer.java @@ -34,7 +34,7 @@ import static org.lwjgl.opengl.GL45.glBindTextureUnit; import static org.lwjgl.opengl.NVMeshShader.glDrawMeshTasksNV; import static org.lwjgl.opengl.NVRepresentativeFragmentTest.GL_REPRESENTATIVE_FRAGMENT_TEST_NV; -public class NvMeshFarWorldRenderer extends AbstractFarWorldRenderer { +public class NvMeshFarWorldRenderer extends AbstractFarWorldRenderer { private final Shader terrain = Shader.make() .add(ShaderType.TASK, "voxy:lod/nvmesh/primary.task") .add(ShaderType.MESH, "voxy:lod/nvmesh/primary.mesh") @@ -53,7 +53,7 @@ public class NvMeshFarWorldRenderer extends AbstractFarWorldRenderer { +public class NvMeshViewport extends Viewport { GlBuffer visibilityBuffer; public NvMeshViewport(NvMeshFarWorldRenderer renderer) { super(renderer); 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 32ea7de7..acfa835e 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 @@ -2,8 +2,8 @@ package me.cortex.voxy.client.core.rendering; import org.joml.Matrix4f; -public abstract class Viewport , T extends AbstractFarWorldRenderer> { - private final T renderer; +public abstract class Viewport > { + private final AbstractFarWorldRenderer renderer; int frameId; Matrix4f projection; @@ -12,7 +12,7 @@ public abstract class Viewport , T extends AbstractFarWo double cameraY; double cameraZ; - protected Viewport(T renderer) { + protected Viewport(AbstractFarWorldRenderer renderer) { this.renderer = renderer; } diff --git a/src/main/resources/assets/voxy/shaders/lod/gl46mesh/bindings.glsl b/src/main/resources/assets/voxy/shaders/lod/gl46mesh/bindings.glsl new file mode 100644 index 00000000..212918ab --- /dev/null +++ b/src/main/resources/assets/voxy/shaders/lod/gl46mesh/bindings.glsl @@ -0,0 +1,76 @@ +struct BlockModel { + uint faceData[6]; + uint flagsA; + uint colourTint; + uint _pad[8]; +}; + +struct SectionMeta { + uint posA; + uint posB; + uint AABB; + uint ptr; + uint cntA; + uint cntB; + uint cntC; + uint cntD; +}; + +//TODO: see if making the stride 2*4*4 bytes or something cause you get that 16 byte write +struct DrawCommand { + uint count; + uint instanceCount; + uint firstIndex; + int baseVertex; + uint baseInstance; +}; + +layout(binding = 0) uniform sampler2D blockModelAtlas; + +#ifndef Quad +#define Quad ivec2 +#endif +layout(binding = 1, std430) readonly restrict buffer QuadBuffer { +Quad quadData[]; +}; + +layout(binding = 2, std430) restrict buffer DrawBuffer { + DrawCommand drawCmd; +}; + +#ifndef Quad +#define MESHLET_ACCESS readonly writeonly +#endif +layout(binding = 3, std430) MESHLET_ACCESS restrict buffer MeshletListData { + uint meshlets[]; +}; + +layout(binding = 4, std430) readonly restrict buffer SectionBuffer { + SectionMeta sectionData[]; +}; + +#ifndef VISIBILITY_ACCESS +#define VISIBILITY_ACCESS readonly +#endif +layout(binding = 5, std430) VISIBILITY_ACCESS restrict buffer VisibilityBuffer { + uint visibilityData[]; +}; + +layout(binding = 6, std430) readonly restrict buffer ModelBuffer { + BlockModel modelData[]; +}; + +layout(binding = 7, std430) readonly restrict buffer ModelColourBuffer { + uint colourData[]; +}; + +layout(binding = 8, std430) readonly restrict buffer LightingBuffer { + uint lightData[]; +}; + +vec4 getLighting(uint index) { + uvec4 arr = uvec4(lightData[index]); + arr = arr>>uvec4(16,8,0,24); + arr = arr & uvec4(0xFF); + return vec4(arr)*vec4(1.0f/255.0f); +} \ No newline at end of file diff --git a/src/main/resources/assets/voxy/shaders/lod/gl46mesh/cmdgen.comp b/src/main/resources/assets/voxy/shaders/lod/gl46mesh/cmdgen.comp new file mode 100644 index 00000000..1116463d --- /dev/null +++ b/src/main/resources/assets/voxy/shaders/lod/gl46mesh/cmdgen.comp @@ -0,0 +1,60 @@ +#version 450 +#define MESHLET_ACCESS writeonly +#import +#import +#import +#define extractMeshletStart extractQuadStart +layout(local_size_x = 64) in; +#define QUADS_PER_MESHLET 126 + +void emitMeshlets(inout uint mli, inout uint meshletPtr, uint mskedCnt, uint cnt) { + for (;mskedCnt != 0; mskedCnt--,mli++) { + meshlets[mli] = meshletPtr + (mskedCnt-1); + } + meshletPtr += cnt; +} + +void main() { + if (gl_GlobalInvocationID.x == 0) { + //Setup the remaining state of the drawElementsIndirect command + drawCmd.count = QUADS_PER_MESHLET*6; + drawCmd.firstIndex = 0; + drawCmd.baseVertex = 0; + drawCmd.baseInstance = 0; + } + if (gl_GlobalInvocationID.x >= sectionCount) { + return; + } + + //Check the occlusion data from last frame + bool shouldRender = visibilityData[gl_GlobalInvocationID.x] == frameId - 1; + if (shouldRender) { + SectionMeta meta = sectionData[gl_GlobalInvocationID.x]; + uint detail = extractDetail(meta); + ivec3 ipos = extractPosition(meta); + + ivec3 relative = ipos-(baseSectionPos>>detail); + + uint a = ((meta.cntA>>16)&0xFFFF); + uint u = (meta.cntB &0xFFFF) * uint(relative.y>-1); + uint d = ((meta.cntB>>16)&0xFFFF) * uint(relative.y<1 ); + uint s = (meta.cntC &0xFFFF) * uint(relative.z>-1); + uint n = ((meta.cntC>>16)&0xFFFF) * uint(relative.z<1 ); + uint w = (meta.cntD &0xFFFF) * uint(relative.x>-1); + uint e = ((meta.cntD>>16)&0xFFFF) * uint(relative.x<1 ); + uint total = a + u + d + s + n + w + e; + + uint mli = atomicAdd(drawCmd.instanceCount, total);//meshletListIndex + + uint meshletPtr = extractMeshletStart(meta); + + emitMeshlets(mli, meshletPtr, a, a); + emitMeshlets(mli, meshletPtr, u, (meta.cntB &0xFFFF)); + emitMeshlets(mli, meshletPtr, d, ((meta.cntB>>16)&0xFFFF)); + emitMeshlets(mli, meshletPtr, s, (meta.cntC &0xFFFF)); + emitMeshlets(mli, meshletPtr, n, ((meta.cntC>>16)&0xFFFF)); + emitMeshlets(mli, meshletPtr, w, (meta.cntD &0xFFFF)); + emitMeshlets(mli, meshletPtr, e, ((meta.cntD>>16)&0xFFFF)); + //TODO: also increment a secondary atomic buffer that can be used to do a compute pass over all meshlets (need to basicly divide the meshletCounter by the computes workGroup size) + } +} \ No newline at end of file diff --git a/src/main/resources/assets/voxy/shaders/lod/gl46mesh/cull.frag b/src/main/resources/assets/voxy/shaders/lod/gl46mesh/cull.frag new file mode 100644 index 00000000..542def86 --- /dev/null +++ b/src/main/resources/assets/voxy/shaders/lod/gl46mesh/cull.frag @@ -0,0 +1,3 @@ +#version 450 +void main() { +} \ No newline at end of file diff --git a/src/main/resources/assets/voxy/shaders/lod/gl46mesh/cull.vert b/src/main/resources/assets/voxy/shaders/lod/gl46mesh/cull.vert new file mode 100644 index 00000000..542def86 --- /dev/null +++ b/src/main/resources/assets/voxy/shaders/lod/gl46mesh/cull.vert @@ -0,0 +1,3 @@ +#version 450 +void main() { +} \ No newline at end of file diff --git a/src/main/resources/assets/voxy/shaders/lod/gl46mesh/quads.frag b/src/main/resources/assets/voxy/shaders/lod/gl46mesh/quads.frag new file mode 100644 index 00000000..36836acc --- /dev/null +++ b/src/main/resources/assets/voxy/shaders/lod/gl46mesh/quads.frag @@ -0,0 +1,5 @@ +#version 460 core +layout(location = 0) out vec4 outColour; +void main() { + outColour = vec4(1,0,0,1); +} \ No newline at end of file diff --git a/src/main/resources/assets/voxy/shaders/lod/gl46mesh/quads.vert b/src/main/resources/assets/voxy/shaders/lod/gl46mesh/quads.vert new file mode 100644 index 00000000..2bf78806 --- /dev/null +++ b/src/main/resources/assets/voxy/shaders/lod/gl46mesh/quads.vert @@ -0,0 +1,38 @@ +#version 450 +#define MESHLET_ACCESS readonly +#define QUADS_PER_MESHLET 126 +//There are 16 bytes of metadata at the start of the meshlet +#define MESHLET_SIZE (QUADS_PER_MESHLET+2) +#import +#import +#import + +uvec2 meshletPosition; +Quad quad; +bool setupMeshlet() { + gl_CullDistance[0] = 1; + //TODO: replace with vertexAttribute that has a divisor of 1 + uint data = meshlets[gl_InstanceID]; + if (data == uint(-1)) {//Came across a culled meshlet + gl_CullDistance[0] = -1; + //Since the primative is culled, dont need to do any more work or set any values as the primative is discarded + // we dont need to care about undefined values + return true; + } + + uint baseId = (data*QUADS_PER_MESHLET); + uint quadIndex = baseId + (gl_VertexID>>2) + 2; + meshletPosition = geometryPool[baseId]; + quad = geometryPool[quadIndex]; + if (isQuadEmpty(quad)) { + gl_CullDistance[0] = -1; + return true; + } + return false; +} + +void main() { + if (setupMeshlet()) { + return; + } +} \ No newline at end of file diff --git a/src/main/resources/assets/voxy/shaders/lod/quad_format.glsl b/src/main/resources/assets/voxy/shaders/lod/quad_format.glsl index 7423afff..62046852 100644 --- a/src/main/resources/assets/voxy/shaders/lod/quad_format.glsl +++ b/src/main/resources/assets/voxy/shaders/lod/quad_format.glsl @@ -29,6 +29,10 @@ uint extractLightId(uint64_t quad) { return Eu32(quad, 8, 55); } +bool isQuadEmpty(uint64_t quad) { + return quad == uint64_t(0); +} + #else //TODO: FIXME, ivec2 swaps around the data of the x and y cause its written in little endian @@ -69,4 +73,8 @@ uint extractBiomeId(ivec2 quad) { uint extractLightId(ivec2 quad) { return Eu32v(quad, 8, 55); } + +bool isQuadEmpty(ivec2 quad) { + return all(equal(quad, ivec2(0))); +} #endif \ No newline at end of file