diff --git a/src/main/java/me/cortex/voxy/client/core/model/ModelBakerySubsystem.java b/src/main/java/me/cortex/voxy/client/core/model/ModelBakerySubsystem.java index ee46f6c6..e0a357f0 100644 --- a/src/main/java/me/cortex/voxy/client/core/model/ModelBakerySubsystem.java +++ b/src/main/java/me/cortex/voxy/client/core/model/ModelBakerySubsystem.java @@ -74,4 +74,8 @@ public class ModelBakerySubsystem { public void addDebugData(List debug) { debug.add("MQ/IF/MC: " + this.blockIdQueue.size() + "/" + this.factory.getInflightCount() + "/" + this.factory.getBakedCount());//Model bake queue/in flight/model baked count } + + public ModelStore getStore() { + return this.storage; + } } diff --git a/src/main/java/me/cortex/voxy/client/core/model/ModelStore.java b/src/main/java/me/cortex/voxy/client/core/model/ModelStore.java index 38196fb5..beaf3615 100644 --- a/src/main/java/me/cortex/voxy/client/core/model/ModelStore.java +++ b/src/main/java/me/cortex/voxy/client/core/model/ModelStore.java @@ -8,9 +8,11 @@ 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.GL30.glBindBufferBase; +import static org.lwjgl.opengl.GL33.*; import static org.lwjgl.opengl.GL33C.glSamplerParameteri; +import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER; +import static org.lwjgl.opengl.GL45.glBindTextureUnit; public class ModelStore { public static final int MODEL_SIZE = 64; @@ -39,4 +41,12 @@ public class ModelStore { this.textures.free(); glDeleteSamplers(this.blockSampler); } + + + public void bind(int modelBindingIndex, int colourBindingIndex, int textureBindingIndex) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, modelBindingIndex, this.modelBuffer.id); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, colourBindingIndex, this.modelColourBuffer.id); + glBindTextureUnit(textureBindingIndex, this.textures.id); + glBindSampler(textureBindingIndex, this.blockSampler); + } } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/LightMapHelper.java b/src/main/java/me/cortex/voxy/client/core/rendering/LightMapHelper.java new file mode 100644 index 00000000..4b9e51be --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/rendering/LightMapHelper.java @@ -0,0 +1,29 @@ +package me.cortex.voxy.client.core.rendering; + +import me.cortex.voxy.client.core.gl.GlBuffer; +import me.cortex.voxy.client.core.rendering.util.UploadStream; +import net.minecraft.client.MinecraftClient; +import org.lwjgl.system.MemoryUtil; + +import static org.lwjgl.opengl.ARBUniformBufferObject.glBindBufferBase; +import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER; + +public class LightMapHelper { + private static final GlBuffer LIGHT_MAP_BUFFER = new GlBuffer(256*4); + public static void tickLightmap() { + long upload = UploadStream.INSTANCE.upload(LIGHT_MAP_BUFFER, 0, 256*4); + var lmt = MinecraftClient.getInstance().gameRenderer.getLightmapTextureManager().texture.getImage(); + for (int light = 0; light < 256; light++) { + int x = light&0xF; + int y = ((light>>4)&0xF); + int sample = lmt.getColor(x,y); + sample = ((sample&0xFF0000)>>16)|(sample&0xFF00)|((sample&0xFF)<<16); + MemoryUtil.memPutInt(upload + (((x<<4)|(15-y))*4), sample|(0xFF<<28));//Skylight is inverted + } + } + + public static void bind(int lightingBufferIndex) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, lightingBufferIndex, LIGHT_MAP_BUFFER.id); + } + +} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java b/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java index 717c25c0..b5485d8d 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/RenderService.java @@ -1,8 +1,8 @@ package me.cortex.voxy.client.core.rendering; import me.cortex.voxy.client.config.VoxyConfig; -import me.cortex.voxy.client.core.gl.shader.PrintfInjector; import me.cortex.voxy.client.core.model.ModelBakerySubsystem; +import me.cortex.voxy.client.core.model.ModelStore; import me.cortex.voxy.client.core.rendering.building.BuiltSection; import me.cortex.voxy.client.core.rendering.building.RenderGenerationService; import me.cortex.voxy.client.core.rendering.hierachical2.HierarchicalNodeManager; @@ -15,15 +15,16 @@ import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.common.world.WorldEngine; import net.minecraft.client.render.Camera; -import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ConcurrentLinkedDeque; -import static org.lwjgl.opengl.ARBDirectStateAccess.glGetNamedFramebufferAttachmentParameteri; import static org.lwjgl.opengl.GL42.*; public class RenderService, J extends Viewport> { - private static AbstractSectionRenderer createSectionRenderer(int maxSectionCount, long geometryCapacity) { - return new MDICSectionRenderer(maxSectionCount, geometryCapacity); + public static final int STATIC_VAO = glGenVertexArrays(); + + private static AbstractSectionRenderer createSectionRenderer(ModelStore store, int maxSectionCount, long geometryCapacity) { + return new MDICSectionRenderer(store, maxSectionCount, geometryCapacity); } private final ViewportSelector viewportSelector; @@ -34,26 +35,28 @@ public class RenderService, J extends Vi private final ModelBakerySubsystem modelService; private final RenderGenerationService renderGen; + private final ConcurrentLinkedDeque sectionBuildResultQueue = new ConcurrentLinkedDeque<>(); + public RenderService(WorldEngine world) { this.modelService = new ModelBakerySubsystem(world.getMapper()); //Max sections: ~500k //Max geometry: 1 gb - this.sectionRenderer = (T) createSectionRenderer(1<<19, 1<<30); + this.sectionRenderer = (T) createSectionRenderer(this.modelService.getStore(),1<<19, (1L<<30)-1024); - this.nodeManager = new HierarchicalNodeManager(1<<21); + this.nodeManager = new HierarchicalNodeManager(1<<21, this.sectionRenderer.getGeometryManager()); this.viewportSelector = new ViewportSelector<>(this.sectionRenderer::createViewport); - this.renderGen = new RenderGenerationService(world, this.modelService, VoxyConfig.CONFIG.renderThreads, this::consumeBuiltSection, this.sectionRenderer.getGeometryManager() instanceof IUsesMeshlets); + this.renderGen = new RenderGenerationService(world, this.modelService, VoxyConfig.CONFIG.renderThreads, this.sectionBuildResultQueue::add, this.sectionRenderer.getGeometryManager() instanceof IUsesMeshlets); this.traversal = new HierarchicalOcclusionTraverser(this.nodeManager, 512); world.setDirtyCallback(this.nodeManager::sectionUpdate); - for(int x = -200; x<=200;x++) { - for (int z = -200; z <= 200; z++) { + for(int x = -10; x<=10;x++) { + for (int z = -10; z <= 10; z++) { for (int y = -3; y <= 3; y++) { this.renderGen.enqueueTask(0, x, y, z); } @@ -61,14 +64,13 @@ public class RenderService, J extends Vi } } - //Cant do a lambda in the constructor cause "this.nodeManager" could be null??? even tho this does the exact same thing, java is stupid - private void consumeBuiltSection(BuiltSection section) {this.nodeManager.processBuildResult(section);} - public void setup(Camera camera) { this.modelService.tick(); } public void renderFarAwayOpaque(J viewport) { + LightMapHelper.tickLightmap(); + //Render previous geometry with the abstract renderer //Execute the hieracial selector // render delta sections @@ -78,9 +80,23 @@ public class RenderService, J extends Vi this.sectionRenderer.renderOpaque(viewport); + //NOTE: need to do the upload and download tick here, after the section renderer renders the world, to ensure "stable" // sections - DownloadStream.INSTANCE.tick(); + + + //FIXME: we only want to tick once per full frame, this is due to how the data of sections is updated + // we basicly need the data to stay stable from one frame to the next, till after renderOpaque + // this is because e.g. shadows, cause this pipeline to be invoked multiple times + // which may cause the geometry to become outdated resulting in corruption rendering in renderOpaque + //TODO: Need to find a proper way to fix this (if there even is one) + if (true /* firstInvocationThisFrame */) { + DownloadStream.INSTANCE.tick(); + //Process the build results here (this is done atomically/on the render thread) + while (!this.sectionBuildResultQueue.isEmpty()) { + this.nodeManager.processBuildResult(this.sectionBuildResultQueue.poll()); + } + } UploadStream.INSTANCE.tick(); glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT|GL_PIXEL_BUFFER_BARRIER_BIT); @@ -98,6 +114,7 @@ public class RenderService, J extends Vi public void addDebugData(List debug) { this.modelService.addDebugData(debug); this.renderGen.addDebugData(debug); + this.sectionRenderer.addDebug(debug); } public void shutdown() { @@ -106,6 +123,9 @@ public class RenderService, J extends Vi this.viewportSelector.free(); this.sectionRenderer.free(); this.traversal.free(); + //Release all the unprocessed built geometry + this.sectionBuildResultQueue.forEach(BuiltSection::free); + this.sectionBuildResultQueue.clear(); } public Viewport getViewport() { 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 c234bd90..30f9dc13 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 @@ -25,6 +25,7 @@ public class SharedIndexBuffer { quadIndexBuff.free(); cubeBuff.free(); + UploadStream.INSTANCE.commit(); } private SharedIndexBuffer(boolean type2) { diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/HierarchicalNodeManager.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/HierarchicalNodeManager.java index 5ad3877d..e0a8021e 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/HierarchicalNodeManager.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierachical2/HierarchicalNodeManager.java @@ -2,6 +2,7 @@ package me.cortex.voxy.client.core.rendering.hierachical2; import me.cortex.voxy.client.core.rendering.building.BuiltSection; +import me.cortex.voxy.client.core.rendering.section.AbstractSectionGeometryManager; import me.cortex.voxy.common.world.WorldSection; import me.jellysquid.mods.sodium.client.util.MathUtil; import org.lwjgl.system.MemoryUtil; @@ -11,8 +12,9 @@ public class HierarchicalNodeManager { public static final int NODE_MSK = ((1<<24)-1); public final int maxNodeCount; private final long[] localNodeData; + private final AbstractSectionGeometryManager geometryManager; - public HierarchicalNodeManager(int maxNodeCount) { + public HierarchicalNodeManager(int maxNodeCount, AbstractSectionGeometryManager geometryManager) { if (!MathUtil.isPowerOfTwo(maxNodeCount)) { throw new IllegalArgumentException("Max node count must be a power of 2"); } @@ -21,6 +23,7 @@ public class HierarchicalNodeManager { } this.maxNodeCount = maxNodeCount; this.localNodeData = new long[maxNodeCount*4]; + this.geometryManager = geometryManager; } public void processRequestQueue(int count, long ptr) { @@ -32,7 +35,11 @@ public class HierarchicalNodeManager { } public void processBuildResult(BuiltSection section) { - section.free(); + if (!section.isEmpty()) { + this.geometryManager.uploadSection(section); + } else { + section.free(); + } } //Called when a section is updated in the world engine diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/section/AbstractSectionGeometryManager.java b/src/main/java/me/cortex/voxy/client/core/rendering/section/AbstractSectionGeometryManager.java index 59c52e0e..f8e95648 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/section/AbstractSectionGeometryManager.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/section/AbstractSectionGeometryManager.java @@ -17,10 +17,11 @@ public abstract class AbstractSectionGeometryManager { this.geometryCapacity = geometryCapacity; } + //Note, calling uploadSection or uploadReplaceSection will free the supplied BuiltSection public int uploadSection(BuiltSection section) {return this.uploadReplaceSection(-1, section);} public abstract int uploadReplaceSection(int oldId, BuiltSection section); public abstract void removeSection(int id); - public void free() { + void tick() {} - } + public void free() {} } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/section/AbstractSectionRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/section/AbstractSectionRenderer.java index dbe731d3..1b885f64 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/section/AbstractSectionRenderer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/section/AbstractSectionRenderer.java @@ -2,14 +2,19 @@ package me.cortex.voxy.client.core.rendering.section; import me.cortex.voxy.client.core.gl.GlBuffer; +import me.cortex.voxy.client.core.model.ModelStore; import me.cortex.voxy.client.core.rendering.Viewport; import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractGeometryManager; +import java.util.List; + //Takes in mesh ids from the hierachical traversal and may perform more culling then renders it public abstract class AbstractSectionRenderer , J extends AbstractSectionGeometryManager> { - private final J geometryManager; - protected AbstractSectionRenderer(J geometryManager) { + protected final J geometryManager; + protected final ModelStore modelStore; + protected AbstractSectionRenderer(ModelStore modelStore, J geometryManager) { this.geometryManager = geometryManager; + this.modelStore = modelStore; } public abstract void renderOpaque(T viewport); @@ -23,4 +28,6 @@ public abstract class AbstractSectionRenderer , J extends public J getGeometryManager() { return this.geometryManager; } + + public void addDebug(List lines) {} } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/section/BasicSectionGeometryManager.java b/src/main/java/me/cortex/voxy/client/core/rendering/section/BasicSectionGeometryManager.java index 10bc841c..957294e1 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/section/BasicSectionGeometryManager.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/section/BasicSectionGeometryManager.java @@ -1,11 +1,13 @@ package me.cortex.voxy.client.core.rendering.section; +import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.rendering.building.BuiltSection; import me.cortex.voxy.client.core.rendering.geometry.OLD.DefaultGeometryManager; import me.cortex.voxy.client.core.rendering.util.BufferArena; +import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.common.util.HierarchicalBitSet; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; @@ -30,6 +32,10 @@ public class BasicSectionGeometryManager extends AbstractSectionGeometryManager @Override public int uploadReplaceSection(int oldId, BuiltSection sectionData) { + if (sectionData.isEmpty()) { + throw new IllegalArgumentException("sectionData is empty, cannot upload nothing"); + } + //Free the old id and replace it with a new one // if oldId is -1, then treat it as not previously existing @@ -43,9 +49,22 @@ public class BasicSectionGeometryManager extends AbstractSectionGeometryManager if (newId == HierarchicalBitSet.SET_FULL) { throw new IllegalStateException("Tried adding section when section count is already at capacity"); } + if (newId > this.sectionMetadata.size()) { + throw new IllegalStateException(); + } + var newMeta = createMeta(sectionData); + //Release the section data as its not needed anymore + sectionData.free(); + if (newId == this.sectionMetadata.size()) { + this.sectionMetadata.add(newMeta); + } else { + this.sectionMetadata.set(newId, newMeta); + } + //Invalidate the section id + this.invalidatedSectionIds.add(newId); return newId; } @@ -83,10 +102,44 @@ public class BasicSectionGeometryManager extends AbstractSectionGeometryManager } } + @Override + void tick() { + //Upload all invalidated bits + if (!this.invalidatedSectionIds.isEmpty()) { + for (int id : this.invalidatedSectionIds) { + var meta = this.sectionMetadata.get(id); + long ptr = UploadStream.INSTANCE.upload(this.sectionMetadataBuffer, (long) id *SECTION_METADATA_SIZE, SECTION_METADATA_SIZE); + if (meta == null) {//We need to clear the gpu side buffer + MemoryUtil.memSet(ptr, 0, SECTION_METADATA_SIZE); + } else { + meta.writeMetadata(ptr); + } + } + this.invalidatedSectionIds.clear(); + UploadStream.INSTANCE.commit(); + } + } + @Override public void free() { super.free(); this.sectionMetadataBuffer.free(); this.geometry.free(); } + + int getSectionCount() { + return this.allocationSet.getCount(); + } + + long getGeometryUsed() { + return this.geometry.getUsedBytes(); + } + + int getGeometryBufferId() { + return this.geometry.id(); + } + + int getMetadataBufferId() { + return this.sectionMetadataBuffer.id; + } } 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 5015a316..e7e60499 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 @@ -2,23 +2,131 @@ package me.cortex.voxy.client.core.rendering.section; 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.model.ModelStore; +import me.cortex.voxy.client.core.rendering.LightMapHelper; +import me.cortex.voxy.client.core.rendering.RenderService; +import me.cortex.voxy.client.core.rendering.SharedIndexBuffer; +import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractFarWorldRenderer; import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractGeometryManager; import me.cortex.voxy.client.core.rendering.geometry.OLD.Gl46HierarchicalViewport; +import me.cortex.voxy.client.core.rendering.util.UploadStream; +import me.cortex.voxy.client.mixin.joml.AccessFrustumIntersection; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.util.math.MathHelper; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.lwjgl.system.MemoryUtil; + +import java.util.List; + +import static org.lwjgl.opengl.ARBIndirectParameters.GL_PARAMETER_BUFFER_ARB; +import static org.lwjgl.opengl.GL11.*; +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.GL32.glDrawElementsInstancedBaseVertex; +import static org.lwjgl.opengl.GL33.glBindSampler; +import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER; +import static org.lwjgl.opengl.GL42.glDrawElementsInstancedBaseVertexBaseInstance; +import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER; +import static org.lwjgl.opengl.GL45.glBindTextureUnit; //Uses MDIC to render the sections public class MDICSectionRenderer extends AbstractSectionRenderer { - public MDICSectionRenderer(int maxSectionCount, long geometryCapacity) { - super(new BasicSectionGeometryManager(maxSectionCount, geometryCapacity)); + private final Shader terrainShader = Shader.make() + .add(ShaderType.VERTEX, "voxy:lod/gl46/quads2.vert") + .add(ShaderType.FRAGMENT, "voxy:lod/gl46/quads.frag") + .compile(); + private final GlBuffer uniform = new GlBuffer(1024).zero(); + + public MDICSectionRenderer(ModelStore modelStore, int maxSectionCount, long geometryCapacity) { + super(modelStore, new BasicSectionGeometryManager(maxSectionCount, geometryCapacity)); + } + + + private void uploadUniformBuffer(BasicViewport viewport) { + long ptr = UploadStream.INSTANCE.upload(this.uniform, 0, 1024); + + int sx = MathHelper.floor(viewport.cameraX)>>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); + 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; + MemoryUtil.memPutInt(ptr, viewport.frameId++); ptr += 4; + innerTranslation.getToAddress(ptr); ptr += 4*3; + + UploadStream.INSTANCE.commit(); + } + + + private void bindRenderingBuffers() { + glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometryManager.getGeometryBufferId()); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.geometryManager.getMetadataBufferId()); + this.modelStore.bind(3, 4, 0); + LightMapHelper.bind(5); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id()); + //glBindBuffer(GL_DRAW_INDIRECT_BUFFER, this.glCommandBuffer.id); + //glBindBuffer(GL_PARAMETER_BUFFER_ARB, this.glCommandCountBuffer.id); + + } + + //Prep the terrain draw calls for this frame, also sets up the + // remaining render pipeline for this frame + private void prepTerrainCallsAndPrep() { + + } + + private void renderTerrain() { + RenderLayer.getCutoutMipped().startDrawing(); + + glDisable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + this.terrainShader.bind(); + glBindVertexArray(RenderService.STATIC_VAO);//Needs to be before binding + this.bindRenderingBuffers(); + + glDrawElementsInstancedBaseVertexBaseInstance(GL_TRIANGLES, 1000*6, GL_UNSIGNED_SHORT, 0,1,0,0); + + + glEnable(GL_CULL_FACE); + + + glBindVertexArray(0); + glBindSampler(0, 0); + glBindTextureUnit(0, 0); + RenderLayer.getCutoutMipped().endDrawing(); } @Override public void renderOpaque(BasicViewport viewport) { - + if (this.geometryManager.getSectionCount() == 0) return; + this.uploadUniformBuffer(viewport); } @Override public void buildDrawCallsAndRenderTemporal(BasicViewport viewport, GlBuffer sectionRenderList) { + //Can do a sneeky trick, since the sectionRenderList is a list to things to render, it invokes the culler + // which only marks visible sections + + + //Tick the geometry manager to upload all invalidated metadata changes to the gpu + this.geometryManager.tick(); + + + this.renderTerrain(); } @Override @@ -26,6 +134,12 @@ public class MDICSectionRenderer extends AbstractSectionRenderer lines) { + super.addDebug(lines); + lines.add("NC/GS: " + this.geometryManager.getSectionCount() + "/" + (this.geometryManager.getGeometryUsed()/(1024*1024)));//Node count/geometry size (MB) + } + @Override public BasicViewport createViewport() { return new BasicViewport(); @@ -34,5 +148,7 @@ public class MDICSectionRenderer extends AbstractSectionRenderer Capabilities.INSTANCE.ssboMaxSize) { - throw new IllegalArgumentException("Buffer is bigger than max ssbo size"); + throw new IllegalArgumentException("Buffer is bigger than max ssbo size (requested " + capacity + " but has max of " + Capabilities.INSTANCE.ssboMaxSize+")"); } this.size = capacity; this.elementSize = elementSize; @@ -58,4 +58,8 @@ public class BufferArena { public float usage() { return (float) ((double)this.used/(this.buffer.size()/this.elementSize)); } + + public long getUsedBytes() { + return this.used*this.elementSize; + } } diff --git a/src/main/java/me/cortex/voxy/common/world/WorldEngine.java b/src/main/java/me/cortex/voxy/common/world/WorldEngine.java index e2214ba9..6ca3a7bd 100644 --- a/src/main/java/me/cortex/voxy/common/world/WorldEngine.java +++ b/src/main/java/me/cortex/voxy/common/world/WorldEngine.java @@ -118,6 +118,7 @@ public class WorldEngine { public void insertUpdate(VoxelizedSection section) {//TODO: add a bitset of levels to update and if it should force update //The >>1 is cause the world sections size is 32x32x32 vs the 16x16x16 of the voxelized section for (int lvl = 0; lvl < this.maxMipLevels; lvl++) { + int nonAirCountDelta = 0; var worldSection = this.acquire(lvl, section.x >> (lvl + 1), section.y >> (lvl + 1), section.z >> (lvl + 1)); int msk = (1<<(lvl+1))-1; int bx = (section.x&msk)<<(4-lvl); @@ -129,11 +130,19 @@ public class WorldEngine { for (int x = bx; x < (16>>lvl)+bx; x++) { long newId = section.get(lvl, x-bx, y-by, z-bz); long oldId = worldSection.set(x, y, z, newId); + nonAirCountDelta += Mapper.isAir(oldId)==Mapper.isAir(newId)?0:(Mapper.isAir(newId)?-1:1 ); didChange |= newId != oldId; } } } + //Branch into 2 paths, if at lod 0, update the atomic count, if that update resulted in a state transition + // then aquire the next lod, lock it, recheck our counter, if it is still ok, then atomically update the parent metadata + //if not lod 0 check that the current occupied state matches the parent lod bit + // if it doesnt, aquire and lock the next lod level + // and do the update propagation + + //Need to release the section after using it if (didChange) { //Mark the section as dirty (enqueuing saving and geometry rebuild) and move to parent mip level diff --git a/src/main/java/me/cortex/voxy/common/world/WorldSection.java b/src/main/java/me/cortex/voxy/common/world/WorldSection.java index 602fc320..615a4b66 100644 --- a/src/main/java/me/cortex/voxy/common/world/WorldSection.java +++ b/src/main/java/me/cortex/voxy/common/world/WorldSection.java @@ -6,6 +6,8 @@ import java.util.Arrays; import java.util.Deque; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; //Represents a loaded world section at a specific detail level // holds a 32x32x32 region of detail @@ -21,7 +23,16 @@ public final class WorldSection { public final int z; public final long key; + + //Serialized states + long metadata; long[] data = null; + + + //Computed on load, updated on insertion + private final AtomicInteger nonAirCount = new AtomicInteger(0); + + private final ActiveSectionTracker tracker; public final AtomicBoolean inSaveQueue = new AtomicBoolean(); diff --git a/src/main/java/me/cortex/voxy/common/world/other/Mapper.java b/src/main/java/me/cortex/voxy/common/world/other/Mapper.java index a5fa762e..11ec768a 100644 --- a/src/main/java/me/cortex/voxy/common/world/other/Mapper.java +++ b/src/main/java/me/cortex/voxy/common/world/other/Mapper.java @@ -52,7 +52,9 @@ public class Mapper { public static boolean isAir(long id) { - return ((id>>27)&((1<<20)-1)) == 0; + int bId = getBlockId(id); + //Note: air can mean void, cave or normal air, as the block state is remapped during ingesting + return bId == 0; } public static int getBlockId(long id) { @@ -185,6 +187,9 @@ public class Mapper { //TODO: replace lambda with a class cached lambda ref (cause doing this:: still does a lambda allocation) public int getIdForBlockState(BlockState state) { + if (state.isAir()) { + return 0; + } return this.block2stateEntry.computeIfAbsent(state, this::registerNewBlockState).id; } diff --git a/src/main/resources/assets/voxy/shaders/lod/gl46/bindings.glsl b/src/main/resources/assets/voxy/shaders/lod/gl46/bindings.glsl index f530bbf5..d3f23a60 100644 --- a/src/main/resources/assets/voxy/shaders/lod/gl46/bindings.glsl +++ b/src/main/resources/assets/voxy/shaders/lod/gl46/bindings.glsl @@ -1,15 +1,10 @@ #line 1 -struct Frustum { - vec4 planes[6]; -}; layout(binding = 0, std140) uniform SceneUniform { mat4 MVP; ivec3 baseSectionPos; - int sectionCount; - Frustum frustum; - vec3 cameraSubPos; uint frameId; + vec3 cameraSubPos; }; struct BlockModel { @@ -39,44 +34,63 @@ struct DrawCommand { uint baseInstance; }; -layout(binding = 0) uniform sampler2D blockModelAtlas; + +#ifdef BLOCK_MODEL_TEXTURE_BINDING +layout(binding = BLOCK_MODEL_TEXTURE_BINDING) uniform sampler2D blockModelAtlas; +#endif + #ifndef Quad #define Quad ivec2 #endif -layout(binding = 1, std430) readonly restrict buffer QuadBuffer { +#ifdef QUAD_BUFFER_BINDING +layout(binding = QUAD_BUFFER_BINDING, std430) readonly restrict buffer QuadBuffer { Quad quadData[]; }; +#endif -layout(binding = 2, std430) writeonly restrict buffer DrawBuffer { +#ifdef DRAW_BUFFER_BINDING +layout(binding = DRAW_BUFFER_BINDING, std430) writeonly restrict buffer DrawBuffer { DrawCommand cmdBuffer[]; }; +#endif -layout(binding = 3, std430) restrict buffer DrawCommandCountBuffer { +#ifdef DRAW_COUNT_BUFFER_BINDING +layout(binding = DRAW_COUNT_BUFFER_BINDING, std430) restrict buffer DrawCommandCountBuffer { uint opaqueDrawCount; uint translucentDrawCount; }; +#endif -layout(binding = 4, std430) readonly restrict buffer SectionBuffer { +#ifdef SECTION_METADA_BUFFER_BINDING +layout(binding = SECTION_METADA_BUFFER_BINDING, std430) readonly restrict buffer SectionBuffer { SectionMeta sectionData[]; }; +#endif #ifndef VISIBILITY_ACCESS #define VISIBILITY_ACCESS readonly #endif -layout(binding = 5, std430) VISIBILITY_ACCESS restrict buffer VisibilityBuffer { +#ifdef VISIBILITY_BUFFER_BINDING +layout(binding = VISIBILITY_BUFFER_BINDING, std430) VISIBILITY_ACCESS restrict buffer VisibilityBuffer { uint visibilityData[]; }; +#endif -layout(binding = 6, std430) readonly restrict buffer ModelBuffer { +#ifdef MODEL_BUFFER_BINDING +layout(binding = MODEL_BUFFER_BINDING, std430) readonly restrict buffer ModelBuffer { BlockModel modelData[]; }; +#endif -layout(binding = 7, std430) readonly restrict buffer ModelColourBuffer { +#ifdef MODEL_COLOUR_BUFFER_BINDING +layout(binding = MODEL_COLOUR_BUFFER_BINDING, std430) readonly restrict buffer ModelColourBuffer { uint colourData[]; }; +#endif -layout(binding = 8, std430) readonly restrict buffer LightingBuffer { +#ifdef LIGHTING_BUFFER_BINDING +layout(binding = LIGHTING_BUFFER_BINDING, std430) readonly restrict buffer LightingBuffer { uint lightData[]; }; @@ -86,5 +100,5 @@ vec4 getLighting(uint index) { arr = arr & uvec4(0xFF); return vec4(arr)*vec4(1.0f/255.0f); } - +#endif diff --git a/src/main/resources/assets/voxy/shaders/lod/gl46/cmdgen.comp b/src/main/resources/assets/voxy/shaders/lod/gl46/cmdgen.comp index 4bfa1479..1c58b73d 100644 --- a/src/main/resources/assets/voxy/shaders/lod/gl46/cmdgen.comp +++ b/src/main/resources/assets/voxy/shaders/lod/gl46/cmdgen.comp @@ -5,7 +5,6 @@ layout(local_size_x = 128) in; #import #import -#import #import #line 11 diff --git a/src/main/resources/assets/voxy/shaders/lod/gl46/frustum.glsl b/src/main/resources/assets/voxy/shaders/lod/gl46/frustum.glsl deleted file mode 100644 index 7bc74b2b..00000000 --- a/src/main/resources/assets/voxy/shaders/lod/gl46/frustum.glsl +++ /dev/null @@ -1,13 +0,0 @@ -bool testFrustumPoint(vec4 plane, vec3 min, vec3 max) { - vec3 point = mix(max, min, lessThan(plane.xyz, vec3(0))) * plane.xyz; - return (point.x + point.y + point.z) >= -plane.w; -} - -bool testFrustum(Frustum frust, vec3 min, vec3 max) { - return testFrustumPoint(frust.planes[0], min, max) && - testFrustumPoint(frust.planes[1], min, max) && - testFrustumPoint(frust.planes[2], min, max) && - testFrustumPoint(frust.planes[3], min, max) && - testFrustumPoint(frust.planes[4], min, max) && - testFrustumPoint(frust.planes[5], min, max); -} \ No newline at end of file diff --git a/src/main/resources/assets/voxy/shaders/lod/gl46/quads2.vert b/src/main/resources/assets/voxy/shaders/lod/gl46/quads2.vert index 5ad88094..643e446f 100644 --- a/src/main/resources/assets/voxy/shaders/lod/gl46/quads2.vert +++ b/src/main/resources/assets/voxy/shaders/lod/gl46/quads2.vert @@ -1,10 +1,16 @@ #version 460 core #extension GL_ARB_gpu_shader_int64 : enable +#define QUAD_BUFFER_BINDING 1 +#define SECTION_METADA_BUFFER_BINDING 2 +#define MODEL_BUFFER_BINDING 3 +#define MODEL_COLOUR_BUFFER_BINDING 4 +#define LIGHTING_BUFFER_BINDING 5 + + #import #import #import -#line 8 //#define DEBUG_RENDER