From c8b0df6ff9534a68f31206a15ba1eb59c0a4ae03 Mon Sep 17 00:00:00 2001 From: mcrcortex <18544518+MCRcortex@users.noreply.github.com> Date: Thu, 7 Aug 2025 10:44:08 +1000 Subject: [PATCH] Part A --- .../cortex/voxy/client/RenderStatistics.java | 24 ++ .../client/core/AbstractRenderPipeline.java | 202 +++++++++ .../client/core/NormalRenderPipeline.java | 143 +++++++ .../voxy/client/core/VoxyRenderSystem.java | 404 ++++++++++++------ .../cortex/voxy/client/core/gl/GlBuffer.java | 7 +- .../voxy/client/core/gl/shader/Shader.java | 7 + .../core/model/bakery/GlViewCapture.java | 12 +- .../core/rendering/ChunkBoundRenderer.java | 23 +- .../client/core/rendering/RenderService.java | 37 +- .../voxy/client/core/rendering/Viewport.java | 6 + .../HierarchicalOcclusionTraverser.java | 11 +- .../core/rendering/post/FullscreenBlit.java | 11 +- .../core/rendering/post/PostProcessing.java | 10 +- .../section/AbstractSectionRenderer.java | 6 +- .../section/MDICSectionRenderer.java | 64 ++- .../core/rendering/util/DepthFramebuffer.java | 63 +++ .../client/mixin/minecraft/MixinDebugHud.java | 2 +- .../post/blit_texture_depth_cutout.frag | 34 +- .../assets/voxy/shaders/post/fullscreen2.vert | 7 + 19 files changed, 842 insertions(+), 231 deletions(-) create mode 100644 src/main/java/me/cortex/voxy/client/core/AbstractRenderPipeline.java create mode 100644 src/main/java/me/cortex/voxy/client/core/NormalRenderPipeline.java create mode 100644 src/main/java/me/cortex/voxy/client/core/rendering/util/DepthFramebuffer.java create mode 100644 src/main/resources/assets/voxy/shaders/post/fullscreen2.vert diff --git a/src/main/java/me/cortex/voxy/client/RenderStatistics.java b/src/main/java/me/cortex/voxy/client/RenderStatistics.java index e666c016..e8823eed 100644 --- a/src/main/java/me/cortex/voxy/client/RenderStatistics.java +++ b/src/main/java/me/cortex/voxy/client/RenderStatistics.java @@ -2,6 +2,10 @@ package me.cortex.voxy.client; import me.cortex.voxy.common.world.WorldEngine; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + public class RenderStatistics { public static boolean enabled = false; @@ -9,4 +13,24 @@ public class RenderStatistics { public static final int[] hierarchicalRenderSections = new int[WorldEngine.MAX_LOD_LAYER+1]; public static final int[] visibleSections = new int[WorldEngine.MAX_LOD_LAYER+1]; public static final int[] quadCount = new int[WorldEngine.MAX_LOD_LAYER+1]; + + + public static void addDebug(List debug) { + if (!enabled) { + return; + } + debug.add("HTC: [" + Arrays.stream(flipCopy(RenderStatistics.hierarchicalTraversalCounts)).mapToObj(Integer::toString).collect(Collectors.joining(", "))+"]"); + debug.add("HRS: [" + Arrays.stream(flipCopy(RenderStatistics.hierarchicalRenderSections)).mapToObj(Integer::toString).collect(Collectors.joining(", "))+"]"); + debug.add("VS: [" + Arrays.stream(flipCopy(RenderStatistics.visibleSections)).mapToObj(Integer::toString).collect(Collectors.joining(", "))+"]"); + debug.add("QC: [" + Arrays.stream(flipCopy(RenderStatistics.quadCount)).mapToObj(Integer::toString).collect(Collectors.joining(", "))+"]"); + } + + private static int[] flipCopy(int[] array) { + int[] ret = new int[array.length]; + int i = ret.length; + for (int j : array) { + ret[--i] = j; + } + return ret; + } } diff --git a/src/main/java/me/cortex/voxy/client/core/AbstractRenderPipeline.java b/src/main/java/me/cortex/voxy/client/core/AbstractRenderPipeline.java new file mode 100644 index 00000000..7d766e3c --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/AbstractRenderPipeline.java @@ -0,0 +1,202 @@ +package me.cortex.voxy.client.core; + +import me.cortex.voxy.client.RenderStatistics; +import me.cortex.voxy.client.TimingStatistics; +import me.cortex.voxy.client.core.rendering.Viewport; +import me.cortex.voxy.client.core.rendering.hierachical.AsyncNodeManager; +import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser; +import me.cortex.voxy.client.core.rendering.hierachical.NodeCleaner; +import me.cortex.voxy.client.core.rendering.post.FullscreenBlit; +import me.cortex.voxy.client.core.rendering.section.AbstractSectionRenderer; +import me.cortex.voxy.client.core.rendering.util.DownloadStream; +import me.cortex.voxy.common.util.TrackedObject; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; +import org.lwjgl.opengl.GL30; +import org.lwjgl.system.MemoryUtil; + +import java.util.List; +import java.util.function.BooleanSupplier; + +import static org.lwjgl.opengl.GL11C.GL_ALWAYS; +import static org.lwjgl.opengl.GL11C.GL_DEPTH_TEST; +import static org.lwjgl.opengl.GL11C.GL_EQUAL; +import static org.lwjgl.opengl.GL11C.GL_KEEP; +import static org.lwjgl.opengl.GL11C.GL_NEAREST; +import static org.lwjgl.opengl.GL11C.GL_REPLACE; +import static org.lwjgl.opengl.GL11C.GL_STENCIL_TEST; +import static org.lwjgl.opengl.GL11C.glColorMask; +import static org.lwjgl.opengl.GL11C.glDisable; +import static org.lwjgl.opengl.GL11C.glEnable; +import static org.lwjgl.opengl.GL11C.glStencilFunc; +import static org.lwjgl.opengl.GL11C.glStencilMask; +import static org.lwjgl.opengl.GL11C.glStencilOp; +import static org.lwjgl.opengl.GL30C.GL_FRAMEBUFFER; +import static org.lwjgl.opengl.GL30C.glBindFramebuffer; +import static org.lwjgl.opengl.GL42.GL_DEPTH_BUFFER_BIT; +import static org.lwjgl.opengl.GL42.GL_LEQUAL; +import static org.lwjgl.opengl.GL42.GL_NOTEQUAL; +import static org.lwjgl.opengl.GL42.glDepthFunc; +import static org.lwjgl.opengl.GL42.*; +import static org.lwjgl.opengl.GL45.glClearNamedFramebufferfi; +import static org.lwjgl.opengl.GL45C.glBindTextureUnit; +import static org.lwjgl.opengl.GL45C.glBlitNamedFramebuffer; + +public abstract class AbstractRenderPipeline extends TrackedObject { + private final BooleanSupplier frexStillHasWork; + + private final AsyncNodeManager nodeManager; + private final NodeCleaner nodeCleaner; + private final HierarchicalOcclusionTraverser traversal; + + protected AbstractSectionRenderer sectionRenderer; + + private final FullscreenBlit depthMaskBlit = new FullscreenBlit("voxy:post/fullscreen2.vert", "voxy:post/noop.frag"); + private final FullscreenBlit depthSetBlit = new FullscreenBlit("voxy:post/fullscreen2.vert", "voxy:post/depth0.frag"); + protected AbstractRenderPipeline(AsyncNodeManager nodeManager, NodeCleaner nodeCleaner, HierarchicalOcclusionTraverser traversal, BooleanSupplier frexSupplier) { + this.frexStillHasWork = frexSupplier; + this.nodeManager = nodeManager; + this.nodeCleaner = nodeCleaner; + this.traversal = traversal; + } + + public final void setSectionRenderer(AbstractSectionRenderer sectionRenderer) {//Stupid java ordering not allowing something pre super + if (this.sectionRenderer != null) throw new IllegalStateException(); + this.sectionRenderer = sectionRenderer; + } + + protected abstract int setup(Viewport viewport, int sourceFramebuffer); + protected abstract void postOpaquePreTranslucent(Viewport viewport); + protected void finish(Viewport viewport, Matrix4fc mcProjection, int sourceFrameBuffer) { + glDisable(GL_STENCIL_TEST); + glBindFramebuffer(GL_FRAMEBUFFER, sourceFrameBuffer); + } + + public void runPipeline(Viewport viewport, Matrix4fc mcProjection, int sourceFrameBuffer) { + int depthTexture = this.setup(viewport, sourceFrameBuffer); + + var rs = ((AbstractSectionRenderer)this.sectionRenderer); + rs.renderOpaque(viewport); + this.innerPrimaryWork(viewport, depthTexture); + rs.buildDrawCalls(viewport); + rs.renderTemporal(viewport); + + this.postOpaquePreTranslucent(viewport); + + rs.renderTranslucent(viewport); + + this.finish(viewport, mcProjection, sourceFrameBuffer); + glBindFramebuffer(GL_FRAMEBUFFER, sourceFrameBuffer); + } + + protected void initDepthStencil(int sourceFrameBuffer, int targetFb, int width, int height) { + + glClearNamedFramebufferfi(targetFb, GL_DEPTH_STENCIL, 0, 1.0f, 1); + glBlitNamedFramebuffer(sourceFrameBuffer, targetFb, 0,0, width, height, 0,0, width, height, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + + glBindFramebuffer(GL30.GL_FRAMEBUFFER, targetFb); + + //This whole thing is hell, we basicly want to create a mask stenicel/depth mask specificiclly + // in theory we could do this in a single pass by passing in the depth buffer from the sourceFrambuffer + // but the current implmentation does a 2 pass system + glEnable(GL_STENCIL_TEST); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilFunc(GL_ALWAYS, 0, 0xFF); + glStencilMask(0xFF); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_NOTEQUAL);//If != 1 pass + glColorMask(false,false,false,false); + //We do here + this.depthMaskBlit.blit(); + glDisable(GL_DEPTH_TEST); + + //Blit depth 0 where stencil is 0 + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, 0, 0xFF); + + this.depthSetBlit.blit(); + + glDepthFunc(GL_LEQUAL); + glColorMask(true,true,true,true); + + //Make voxy terrain render only where there isnt mc terrain + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, 1, 0xFF); + } + + private static final long SCRATCH = MemoryUtil.nmemAlloc(4*4*4); + protected static void transformBlitDepth(FullscreenBlit blitShader, int srcDepthTex, int dstFB, Viewport viewport, Matrix4f targetTransform) { + glBindFramebuffer(GL30.GL_FRAMEBUFFER, dstFB); + + blitShader.bind(); + glBindTextureUnit(0, srcDepthTex); + new Matrix4f(viewport.MVP).invert().getToAddress(SCRATCH); + nglUniformMatrix4fv(1, 1, false, SCRATCH);//inverse fromProjection + targetTransform.getToAddress(SCRATCH);//new Matrix4f(tooProjection).mul(vp.modelView).get(data); + nglUniformMatrix4fv(2, 1, false, SCRATCH);//tooProjection + + glEnable(GL_DEPTH_TEST); + //We keep the stencil test on with the emitting, only to where non terrain is rendered + blitShader.blit(); + glDisable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + } + + protected void innerPrimaryWork(Viewport viewport, int depthBuffer) { + + //Compute the mip chain + viewport.hiZBuffer.buildMipChain(depthBuffer, viewport.width, viewport.height); + + do { + TimingStatistics.main.stop(); + TimingStatistics.dynamic.start(); + + TimingStatistics.D.start(); + //Tick download stream + DownloadStream.INSTANCE.tick(); + TimingStatistics.D.stop(); + + this.nodeManager.tick(this.traversal.getNodeBuffer(), this.nodeCleaner); + //glFlush(); + + this.nodeCleaner.tick(this.traversal.getNodeBuffer());//Probably do this here?? + + TimingStatistics.dynamic.stop(); + TimingStatistics.main.start(); + + glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_PIXEL_BUFFER_BARRIER_BIT); + + TimingStatistics.I.start(); + this.traversal.doTraversal(viewport); + TimingStatistics.I.stop(); + } while (this.frexStillHasWork.getAsBoolean()); + } + + @Override + protected void free0() { + this.sectionRenderer.free(); + this.depthMaskBlit.delete(); + this.depthSetBlit.delete(); + super.free0(); + } + + public void addDebug(List debug) { + this.sectionRenderer.addDebug(debug); + RenderStatistics.addDebug(debug); + } + + public abstract void bindOpaqueFramebuffer(); + public abstract void bindTranslucentFramebuffer(); + + + //null means dont transform the shader + public String patchOpaqueShader(AbstractSectionRenderer renderer, String input) { + return null; + } + + //Returning null means apply the same patch as the opaque + public String patchTranslucentShader(AbstractSectionRenderer renderer, String input) { + return null; + } +} diff --git a/src/main/java/me/cortex/voxy/client/core/NormalRenderPipeline.java b/src/main/java/me/cortex/voxy/client/core/NormalRenderPipeline.java new file mode 100644 index 00000000..bc6ec93f --- /dev/null +++ b/src/main/java/me/cortex/voxy/client/core/NormalRenderPipeline.java @@ -0,0 +1,143 @@ +package me.cortex.voxy.client.core; + +import me.cortex.voxy.client.config.VoxyConfig; +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; +import me.cortex.voxy.client.core.gl.shader.ShaderType; +import me.cortex.voxy.client.core.rendering.Viewport; +import me.cortex.voxy.client.core.rendering.hierachical.AsyncNodeManager; +import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser; +import me.cortex.voxy.client.core.rendering.hierachical.NodeCleaner; +import me.cortex.voxy.client.core.rendering.post.FullscreenBlit; +import me.cortex.voxy.client.core.rendering.util.DepthFramebuffer; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; +import org.lwjgl.opengl.GL30; +import org.lwjgl.system.MemoryStack; + +import java.util.function.BooleanSupplier; + +import static org.lwjgl.opengl.ARBComputeShader.glDispatchCompute; +import static org.lwjgl.opengl.ARBShaderImageLoadStore.glBindImageTexture; +import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; +import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; +import static org.lwjgl.opengl.GL11C.GL_NEAREST; +import static org.lwjgl.opengl.GL11C.GL_RGBA8; +import static org.lwjgl.opengl.GL15.GL_READ_WRITE; +import static org.lwjgl.opengl.GL20.glUniformMatrix4fv; +import static org.lwjgl.opengl.GL30C.*; +import static org.lwjgl.opengl.GL30C.GL_FRAMEBUFFER; +import static org.lwjgl.opengl.GL43.GL_DEPTH_STENCIL_TEXTURE_MODE; +import static org.lwjgl.opengl.GL45C.*; +import static org.lwjgl.opengl.GL45C.glTextureParameterf; + +public class NormalRenderPipeline extends AbstractRenderPipeline { + private GlTexture colourTex; + private GlTexture colourSSAOTex; + private final GlFramebuffer fbSSAO = new GlFramebuffer(); + private final DepthFramebuffer fb = new DepthFramebuffer(GL_DEPTH24_STENCIL8); + + private final boolean useEnvFog; + private final FullscreenBlit finalBlit; + + private final Shader ssaoCompute = Shader.make() + .add(ShaderType.COMPUTE, "voxy:post/ssao.comp") + .compile(); + + protected NormalRenderPipeline(AsyncNodeManager nodeManager, NodeCleaner nodeCleaner, HierarchicalOcclusionTraverser traversal, BooleanSupplier frexSupplier) { + super(nodeManager, nodeCleaner, traversal, frexSupplier); + this.useEnvFog = VoxyConfig.CONFIG.useEnvironmentalFog; + this.finalBlit = new FullscreenBlit("voxy:post/blit_texture_depth_cutout.frag", + a->a.defineIf("USE_ENV_FOG", this.useEnvFog).define("EMIT_COLOUR")); + } + + @Override + protected int setup(Viewport viewport, int sourceFB) { + if (this.colourTex == null || this.colourTex.getHeight() != viewport.height || this.colourTex.getWidth() != viewport.width) { + if (this.colourTex != null) { + this.colourTex.free(); + this.colourSSAOTex.free(); + } + this.fb.resize(viewport.width, viewport.height); + + this.colourTex = new GlTexture().store(GL_RGBA8, 1, viewport.width, viewport.height); + this.colourSSAOTex = new GlTexture().store(GL_RGBA8, 1, viewport.width, viewport.height); + + this.fb.framebuffer.bind(GL_COLOR_ATTACHMENT0, this.colourTex).verify(); + this.fbSSAO.bind(GL_DEPTH_STENCIL_ATTACHMENT, this.fb.getDepthTex()).bind(GL_COLOR_ATTACHMENT0, this.colourSSAOTex).verify(); + + + glTextureParameterf(this.colourTex.id, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTextureParameterf(this.colourTex.id, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTextureParameterf(this.colourSSAOTex.id, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTextureParameterf(this.colourSSAOTex.id, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTextureParameterf(this.fb.getDepthTex().id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT); + } + + this.initDepthStencil(sourceFB, this.fb.framebuffer.id, viewport.width, viewport.height); + + return this.fb.getDepthTex().id; + } + + @Override + protected void postOpaquePreTranslucent(Viewport viewport) { + this.ssaoCompute.bind(); + try (var stack = MemoryStack.stackPush()) { + long ptr = stack.nmalloc(4*4*4); + viewport.MVP.getToAddress(ptr); + nglUniformMatrix4fv(3, 1, false, ptr);//MVP + viewport.MVP.invert(new Matrix4f()).getToAddress(ptr); + nglUniformMatrix4fv(4, 1, false, ptr);//invMVP + } + + + glBindImageTexture(0, this.colourSSAOTex.id, 0, false,0, GL_READ_WRITE, GL_RGBA8); + glBindTextureUnit(1, this.fb.getDepthTex().id); + glBindTextureUnit(2, this.colourTex.id); + + glDispatchCompute((viewport.width+31)/32, (viewport.height+31)/32, 1); + + glBindFramebuffer(GL_FRAMEBUFFER, this.fbSSAO.id); + } + + @Override + protected void finish(Viewport viewport, Matrix4fc mcProjection, int sourceFrameBuffer) { + this.finalBlit.bind(); + if (this.useEnvFog) { + float start = viewport.fogParameters.environmentalStart(); + float end = viewport.fogParameters.environmentalEnd(); + float invEndFogDelta = 1f/(end-start); + glUniform3f(4, viewport.fogParameters.environmentalEnd(), invEndFogDelta, start*invEndFogDelta); + glUniform3f(5, viewport.fogParameters.red(), viewport.fogParameters.green(), viewport.fogParameters.blue()); + } + + glBindTextureUnit(3, this.colourSSAOTex.id); + AbstractRenderPipeline.transformBlitDepth(this.finalBlit, this.fb.getDepthTex().id, sourceFrameBuffer, viewport, new Matrix4f(mcProjection).mul(viewport.modelView)); + + //glBlitNamedFramebuffer(this.fbSSAO.id, sourceFrameBuffer, 0,0, viewport.width, viewport.height, 0,0, viewport.width, viewport.height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + + @Override + public void bindOpaqueFramebuffer() { + this.fb.bind(); + } + + @Override + public void bindTranslucentFramebuffer() { + glBindFramebuffer(GL_FRAMEBUFFER, this.fbSSAO.id); + } + + @Override + public void free() { + this.finalBlit.delete(); + this.ssaoCompute.free(); + this.fb.free(); + this.fbSSAO.free(); + if (this.colourTex != null) { + this.colourTex.free(); + this.colourSSAOTex.free(); + } + super.free0(); + } +} diff --git a/src/main/java/me/cortex/voxy/client/core/VoxyRenderSystem.java b/src/main/java/me/cortex/voxy/client/core/VoxyRenderSystem.java index b698ddd8..d46c91cd 100644 --- a/src/main/java/me/cortex/voxy/client/core/VoxyRenderSystem.java +++ b/src/main/java/me/cortex/voxy/client/core/VoxyRenderSystem.java @@ -5,12 +5,20 @@ import com.mojang.blaze3d.opengl.GlStateManager; import me.cortex.voxy.client.TimingStatistics; import me.cortex.voxy.client.VoxyClient; 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.gl.GlTexture; -import me.cortex.voxy.client.core.rendering.ChunkBoundRenderer; -import me.cortex.voxy.client.core.rendering.RenderDistanceTracker; -import me.cortex.voxy.client.core.rendering.RenderService; -import me.cortex.voxy.client.core.rendering.post.PostProcessing; +import me.cortex.voxy.client.core.model.ModelBakerySubsystem; +import me.cortex.voxy.client.core.model.ModelStore; +import me.cortex.voxy.client.core.rendering.*; +import me.cortex.voxy.client.core.rendering.building.RenderGenerationService; +import me.cortex.voxy.client.core.rendering.hierachical.AsyncNodeManager; +import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser; +import me.cortex.voxy.client.core.rendering.hierachical.NodeCleaner; +import me.cortex.voxy.client.core.rendering.section.AbstractSectionRenderer; +import me.cortex.voxy.client.core.rendering.section.MDICSectionRenderer; +import me.cortex.voxy.client.core.rendering.section.geometry.BasicSectionGeometryData; +import me.cortex.voxy.client.core.rendering.section.geometry.IGeometryData; import me.cortex.voxy.client.core.rendering.util.DownloadStream; import me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil; import me.cortex.voxy.client.core.rendering.util.UploadStream; @@ -25,22 +33,40 @@ import org.joml.Matrix4f; import org.joml.Matrix4fc; import org.lwjgl.opengl.GL11; +import java.util.Arrays; import java.util.List; import static org.lwjgl.opengl.GL11.GL_VIEWPORT; import static org.lwjgl.opengl.GL11.glGetIntegerv; -import static org.lwjgl.opengl.GL11C.glFinish; +import static org.lwjgl.opengl.GL11C.*; import static org.lwjgl.opengl.GL30C.GL_DRAW_FRAMEBUFFER_BINDING; import static org.lwjgl.opengl.GL30C.glBindFramebuffer; import static org.lwjgl.opengl.GL33.glBindSampler; public class VoxyRenderSystem { - private final RenderService renderer; - private final PostProcessing postProcessing; private final WorldEngine worldIn; + + + private final ModelBakerySubsystem modelService; + private final RenderGenerationService renderGen; + private final IGeometryData geometryData; + private final AsyncNodeManager nodeManager; + private final NodeCleaner nodeCleaner; + private final HierarchicalOcclusionTraverser traversal; + + private final RenderDistanceTracker renderDistanceTracker; public final ChunkBoundRenderer chunkBoundRenderer; + private final ViewportSelector viewportSelector; + + private final AbstractRenderPipeline pipeline; + + private static AbstractSectionRenderer createSectionRenderer(AbstractRenderPipeline pipeline, ModelStore modelStore, IGeometryData geometryData) { + //TODO: need todo a thing where selects optimal section render based on if supports the pipeline and geometry data type + return new MDICSectionRenderer(pipeline, modelStore, (BasicSectionGeometryData) geometryData);//We only have MDIC backend... for now + } + public VoxyRenderSystem(WorldEngine world, ServiceThreadPool threadPool) { //Keep the world loaded, NOTE: this is done FIRST, to keep and ensure that even if the rest of loading takes more // than timeout, we keep the world acquired @@ -51,41 +77,185 @@ public class VoxyRenderSystem { glFinish(); this.worldIn = world; - this.renderer = new RenderService(world, threadPool); - this.postProcessing = new PostProcessing(); - int minSec = MinecraftClient.getInstance().world.getBottomSectionCoord() >> 5; - int maxSec = (MinecraftClient.getInstance().world.getTopSectionCoord() - 1) >> 5; - //Do some very cheeky stuff for MiB - if (false) { - minSec = -8; - maxSec = 7; + long geometryCapacity = getGeometryBufferSize(); + { + + + this.modelService = new ModelBakerySubsystem(world.getMapper()); + this.renderGen = new RenderGenerationService(world, this.modelService, threadPool, false, () -> true); + + this.geometryData = new BasicSectionGeometryData(1 << 20, geometryCapacity); + + this.nodeManager = new AsyncNodeManager(1 << 21, this.geometryData, this.renderGen); + this.nodeCleaner = new NodeCleaner(this.nodeManager); + this.traversal = new HierarchicalOcclusionTraverser(this.nodeManager, this.nodeCleaner, this.renderGen); + + world.setDirtyCallback(this.nodeManager::worldEvent); + + Arrays.stream(world.getMapper().getBiomeEntries()).forEach(this.modelService::addBiome); + world.getMapper().setBiomeCallback(this.modelService::addBiome); + + this.nodeManager.start(); } - this.renderDistanceTracker = new RenderDistanceTracker(20, - minSec, - maxSec, - this.renderer::addTopLevelNode, - this.renderer::removeTopLevelNode); + this.pipeline = RenderPipelineFactory.createPipeline(this.nodeManager, this.nodeCleaner, this.traversal, this::frexStillHasWork); + var sectionRenderer = createSectionRenderer(this.pipeline, this.modelService.getStore(), this.geometryData); + this.pipeline.setSectionRenderer(sectionRenderer); + this.viewportSelector = new ViewportSelector<>(sectionRenderer::createViewport); - this.renderDistanceTracker.setRenderDistance(VoxyConfig.CONFIG.sectionRenderDistance); + { + int minSec = MinecraftClient.getInstance().world.getBottomSectionCoord() >> 5; + int maxSec = (MinecraftClient.getInstance().world.getTopSectionCoord() - 1) >> 5; + + //Do some very cheeky stuff for MiB + if (false) { + minSec = -8; + maxSec = 7; + } + + this.renderDistanceTracker = new RenderDistanceTracker(20, + minSec, + maxSec, + this.nodeManager::addTopLevel, + this.nodeManager::removeTopLevel); + + this.renderDistanceTracker.setRenderDistance(VoxyConfig.CONFIG.sectionRenderDistance); + } this.chunkBoundRenderer = new ChunkBoundRenderer(); + + + Logger.info("Voxy render system created with " + geometryCapacity + " geometry capacity, using pipeline '" + this.pipeline.getClass().getSimpleName() + "' with renderer '" + sectionRenderer.getClass().getSimpleName() + "'"); } catch (RuntimeException e) { world.releaseRef();//If something goes wrong, we must release the world first throw e; } } - public void setRenderDistance(int renderDistance) { - this.renderDistanceTracker.setRenderDistance(renderDistance); + public void renderOpaque(ChunkRenderMatrices matrices, FogParameters fogParameters, double cameraX, double cameraY, double cameraZ) { + if (IrisUtil.irisShadowActive()) { + return; + } + TimingStatistics.resetSamplers(); + + + //Do some very cheeky stuff for MiB + if (false) { + int sector = (((int)Math.floor(cameraX)>>4)+512)>>10; + cameraX -= sector<<14;//10+4 + cameraY += (16+(256-32-sector*30))*16; + } + + long startTime = System.nanoTime(); + TimingStatistics.all.start(); + TimingStatistics.main.start(); + + int oldFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING); + int boundFB = oldFB; + + //var target = DefaultTerrainRenderPasses.CUTOUT.getTarget(); + //boundFB = ((net.minecraft.client.texture.GlTexture) target.getColorAttachment()).getOrCreateFramebuffer(((GlBackend) RenderSystem.getDevice()).getFramebufferManager(), target.getDepthAttachment()); + if (boundFB == 0) { + throw new IllegalStateException("Cannot use the default framebuffer as cannot source from it"); + } + + //this.autoBalanceSubDivSize(); + + var projection = computeProjectionMat(matrices.projection());//RenderSystem.getProjectionMatrix(); + //var projection = new Matrix4f(matrices.projection()); + + int[] dims = new int[4]; + glGetIntegerv(GL_VIEWPORT, dims); + + var viewport = this.getViewport(); + viewport + .setProjection(projection) + .setModelView(new Matrix4f(matrices.modelView())) + .setCamera(cameraX, cameraY, cameraZ) + .setScreenSize(dims[2], dims[3]) + .setFogParameters(fogParameters) + .update(); + viewport.frameId++; + + TimingStatistics.E.start(); + this.chunkBoundRenderer.render(viewport); + TimingStatistics.E.stop(); + + + //The entire rendering pipeline (excluding the chunkbound thing) + this.pipeline.runPipeline(viewport, matrices.projection(), boundFB); + + + TimingStatistics.main.stop(); + TimingStatistics.postDynamic.start(); + + PrintfDebugUtil.tick(); + + //As much dynamic runtime stuff here + { + //Tick upload stream (this is ok to do here as upload ticking is just memory management) + UploadStream.INSTANCE.tick(); + + while (this.renderDistanceTracker.setCenterAndProcess(cameraX, cameraZ) && VoxyClient.isFrexActive());//While FF is active, run until everything is processed + + //Done here as is allows less gl state resetup + this.modelService.tick(Math.max(3_000_000-(System.nanoTime()-startTime), 500_000)); + } + TimingStatistics.postDynamic.stop(); + + glBindFramebuffer(GlConst.GL_FRAMEBUFFER, oldFB); + + {//Reset state manager stuffs + glEnable(GL_DEPTH_TEST); + + GlStateManager._glBindVertexArray(0);//Clear binding + + GlStateManager._activeTexture(GlConst.GL_TEXTURE0); + GlStateManager._bindTexture(0); + glBindSampler(0, 0); + + GlStateManager._activeTexture(GlConst.GL_TEXTURE1); + GlStateManager._bindTexture(0); + glBindSampler(1, 0); + + GlStateManager._activeTexture(GlConst.GL_TEXTURE2); + GlStateManager._bindTexture(0); + glBindSampler(2, 0); + } + TimingStatistics.all.stop(); + + /* + TimingStatistics.F.start(); + this.postProcessing.setup(viewport.width, viewport.height, boundFB); + TimingStatistics.F.stop(); + + this.renderer.renderFarAwayOpaque(viewport, this.chunkBoundRenderer.getDepthBoundTexture()); + + + TimingStatistics.F.start(); + //Compute the SSAO of the rendered terrain, TODO: fix it breaking depth or breaking _something_ am not sure what + this.postProcessing.computeSSAO(viewport.MVP); + TimingStatistics.F.stop(); + + TimingStatistics.G.start(); + //We can render the translucent directly after as it is the furthest translucent objects + this.renderer.renderFarAwayTranslucent(viewport, this.chunkBoundRenderer.getDepthBoundTexture()); + TimingStatistics.G.stop(); + + + TimingStatistics.F.start(); + this.postProcessing.renderPost(viewport, matrices.projection(), boundFB); + TimingStatistics.F.stop(); + */ } + private void autoBalanceSubDivSize() { //only increase quality while there are very few mesh queues, this stops, // e.g. while flying and is rendering alot of low quality chunks - boolean canDecreaseSize = this.renderer.getMeshQueueCount() < 5000; + boolean canDecreaseSize = this.renderGen.getTaskCount() < 5000; float CHANGE_PER_SECOND = 30; //Auto fps targeting if (MinecraftClient.getInstance().getCurrentFps() < 45) { @@ -120,119 +290,38 @@ public class VoxyRenderSystem { ).mulLocal(makeProjectionMatrix(16, 16*3000)); } - public void renderOpaque(ChunkRenderMatrices matrices, FogParameters fogParameters, double cameraX, double cameraY, double cameraZ) { - if (IrisUtil.irisShadowActive()) { - return; + private boolean frexStillHasWork() { + if (!VoxyClient.isFrexActive()) { + return false; } - TimingStatistics.resetSamplers(); - - - //Do some very cheeky stuff for MiB - if (false) { - int sector = (((int)Math.floor(cameraX)>>4)+512)>>10; - cameraX -= sector<<14;//10+4 - cameraY += (16+(256-32-sector*30))*16; - } - - long startTime = System.nanoTime(); - TimingStatistics.all.start(); - TimingStatistics.main.start(); - - - - int oldFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING); - int boundFB = oldFB; - - //var target = DefaultTerrainRenderPasses.CUTOUT.getTarget(); - //boundFB = ((net.minecraft.client.texture.GlTexture) target.getColorAttachment()).getOrCreateFramebuffer(((GlBackend) RenderSystem.getDevice()).getFramebufferManager(), target.getDepthAttachment()); - if (boundFB == 0) { - throw new IllegalStateException("Cannot use the default framebuffer as cannot source from it"); - } - - //this.autoBalanceSubDivSize(); - - var projection = computeProjectionMat(matrices.projection());//RenderSystem.getProjectionMatrix(); - //var projection = new Matrix4f(matrices.projection()); - - int[] dims = new int[4]; - glGetIntegerv(GL_VIEWPORT, dims); - var viewport = this.renderer.getViewport(); - - viewport - .setProjection(projection) - .setModelView(new Matrix4f(matrices.modelView())) - .setCamera(cameraX, cameraY, cameraZ) - .setScreenSize(dims[2], dims[3]) - .setFogParameters(fogParameters) - .update(); - viewport.frameId++; - - TimingStatistics.E.start(); - this.chunkBoundRenderer.render(viewport); - TimingStatistics.E.stop(); - - TimingStatistics.F.start(); - this.postProcessing.setup(viewport.width, viewport.height, boundFB); - TimingStatistics.F.stop(); - - this.renderer.renderFarAwayOpaque(viewport, this.chunkBoundRenderer.getDepthBoundTexture()); - - - TimingStatistics.F.start(); - //Compute the SSAO of the rendered terrain, TODO: fix it breaking depth or breaking _something_ am not sure what - this.postProcessing.computeSSAO(viewport.MVP); - TimingStatistics.F.stop(); - - TimingStatistics.G.start(); - //We can render the translucent directly after as it is the furthest translucent objects - this.renderer.renderFarAwayTranslucent(viewport, this.chunkBoundRenderer.getDepthBoundTexture()); - TimingStatistics.G.stop(); - - - TimingStatistics.F.start(); - this.postProcessing.renderPost(viewport, matrices.projection(), boundFB); - TimingStatistics.F.stop(); - - TimingStatistics.main.stop(); - TimingStatistics.postDynamic.start(); - - PrintfDebugUtil.tick(); - - //As much dynamic runtime stuff here - { - //Tick upload stream (this is ok to do here as upload ticking is just memory management) - UploadStream.INSTANCE.tick(); - - while (this.renderDistanceTracker.setCenterAndProcess(cameraX, cameraZ) && VoxyClient.isFrexActive());//While FF is active, run until everything is processed - - //Done here as is allows less gl state resetup - this.renderer.tickModelService(Math.max(3_000_000-(System.nanoTime()-startTime), 500_000)); - } - TimingStatistics.postDynamic.stop(); - - glBindFramebuffer(GlConst.GL_FRAMEBUFFER, oldFB); - - {//Reset state manager stuffs - GlStateManager._glBindVertexArray(0);//Clear binding - - GlStateManager._activeTexture(GlConst.GL_TEXTURE0); - GlStateManager._bindTexture(0); - glBindSampler(0, 0); - - GlStateManager._activeTexture(GlConst.GL_TEXTURE1); - GlStateManager._bindTexture(0); - glBindSampler(1, 0); - - GlStateManager._activeTexture(GlConst.GL_TEXTURE2); - GlStateManager._bindTexture(0); - glBindSampler(2, 0); - } - TimingStatistics.all.stop(); + //If frex is running we must tick everything to ensure correctness + UploadStream.INSTANCE.tick(); + //Done here as is allows less gl state resetup + this.modelService.tick(100_000_000); + GL11.glFinish(); + return this.nodeManager.hasWork() || this.renderGen.getTaskCount()!=0 || !this.modelService.areQueuesEmpty(); } + public void setRenderDistance(int renderDistance) { + this.renderDistanceTracker.setRenderDistance(renderDistance); + } + + public Viewport getViewport() { + return this.viewportSelector.getViewport(); + } + + + + + public void addDebugInfo(List debug) { debug.add("Buf/Tex [#/Mb]: [" + GlBuffer.getCount() + "/" + (GlBuffer.getTotalSize()/1_000_000) + "],[" + GlTexture.getCount() + "/" + (GlTexture.getEstimatedTotalSize()/1_000_000)+"]"); - this.renderer.addDebugData(debug); + { + this.modelService.addDebugData(debug); + this.renderGen.addDebugData(debug); + this.nodeManager.addDebug(debug); + this.pipeline.addDebug(debug); + } { TimingStatistics.update(); debug.add("Voxy frame runtime (millis): " + TimingStatistics.dynamic.pVal() + ", " + TimingStatistics.main.pVal()+ ", " + TimingStatistics.postDynamic.pVal()+ ", " + TimingStatistics.all.pVal()); @@ -246,13 +335,58 @@ public class VoxyRenderSystem { Logger.info("Flushing download stream"); DownloadStream.INSTANCE.flushWaitClear(); Logger.info("Shutting down rendering"); - try {this.renderer.shutdown();this.chunkBoundRenderer.free();} catch (Exception e) {Logger.error("Error shutting down renderer", e);} - Logger.info("Shutting down post processor"); - if (this.postProcessing!=null){try {this.postProcessing.shutdown();} catch (Exception e) {Logger.error("Error shutting down post processor", e);}} + try { + //Cleanup callbacks + this.worldIn.setDirtyCallback(null); + this.worldIn.getMapper().setBiomeCallback(null); + this.worldIn.getMapper().setStateCallback(null); + + this.nodeManager.stop(); + + this.modelService.shutdown(); + this.renderGen.shutdown(); + this.traversal.free(); + this.nodeCleaner.free(); + + this.geometryData.free(); + this.chunkBoundRenderer.free(); + + this.viewportSelector.free(); + } catch (Exception e) {Logger.error("Error shutting down renderer components", e);} + Logger.info("Shutting down render pipeline"); + try {this.pipeline.free();} catch (Exception e){Logger.error("Error releasing render pipeline", e);} + + + Logger.info("Flushing download stream"); DownloadStream.INSTANCE.flushWaitClear(); //Release hold on the world this.worldIn.releaseRef(); + Logger.info("Render shutdown completed"); + } + + private static long getGeometryBufferSize() { + long geometryCapacity = Math.min((1L<<(64-Long.numberOfLeadingZeros(Capabilities.INSTANCE.ssboMaxSize-1)))<<1, 1L<<32)-1024/*(1L<<32)-1024*/; + if (Capabilities.INSTANCE.isIntel) { + geometryCapacity = Math.max(geometryCapacity, 1L<<30);//intel moment, force min 1gb + } + + //Limit to available dedicated memory if possible + if (Capabilities.INSTANCE.canQueryGpuMemory) { + //512mb less than avalible, + long limit = Capabilities.INSTANCE.getFreeDedicatedGpuMemory() - 1024*1024*1024; + // Give a minimum of 512 mb requirement + limit = Math.max(512*1024*1024, limit); + + geometryCapacity = Math.min(geometryCapacity, limit); + } + //geometryCapacity = 1<<28; + //geometryCapacity = 1<<30;//1GB test + var override = System.getProperty("voxy.geometryBufferSizeOverrideMB", ""); + if (!override.isEmpty()) { + geometryCapacity = Long.parseLong(override)*1024L*1024L; + } + return geometryCapacity; } } diff --git a/src/main/java/me/cortex/voxy/client/core/gl/GlBuffer.java b/src/main/java/me/cortex/voxy/client/core/gl/GlBuffer.java index 0c74d745..fb224151 100644 --- a/src/main/java/me/cortex/voxy/client/core/gl/GlBuffer.java +++ b/src/main/java/me/cortex/voxy/client/core/gl/GlBuffer.java @@ -2,6 +2,8 @@ package me.cortex.voxy.client.core.gl; import me.cortex.voxy.common.util.TrackedObject; import org.lwjgl.opengl.GL11; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE; import static org.lwjgl.opengl.GL15.glDeleteBuffers; @@ -57,7 +59,8 @@ public class GlBuffer extends TrackedObject { glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0); glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0); - glClearNamedBufferData(this.id, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[]{data}); + MemoryUtil.memPutInt(SCRATCH, data); + nglClearNamedBufferData(this.id, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, SCRATCH); return this; } @@ -72,4 +75,6 @@ public class GlBuffer extends TrackedObject { public GlBuffer name(String name) { return GlDebug.name(name, this); } + + private static final long SCRATCH = MemoryUtil.nmemAlloc(4); } diff --git a/src/main/java/me/cortex/voxy/client/core/gl/shader/Shader.java b/src/main/java/me/cortex/voxy/client/core/gl/shader/Shader.java index 25a6ed69..a9f1e26e 100644 --- a/src/main/java/me/cortex/voxy/client/core/gl/shader/Shader.java +++ b/src/main/java/me/cortex/voxy/client/core/gl/shader/Shader.java @@ -77,6 +77,13 @@ public class Shader extends TrackedObject { this.processor = processor; } + public Builder clone() { + var clone = new Builder<>(this.constructor, this.processor); + clone.defines.putAll(this.defines); + clone.sources.putAll(this.sources); + return clone; + } + public Builder define(String name) { this.defines.put(name, ""); return this; diff --git a/src/main/java/me/cortex/voxy/client/core/model/bakery/GlViewCapture.java b/src/main/java/me/cortex/voxy/client/core/model/bakery/GlViewCapture.java index 06057631..d8906083 100644 --- a/src/main/java/me/cortex/voxy/client/core/model/bakery/GlViewCapture.java +++ b/src/main/java/me/cortex/voxy/client/core/model/bakery/GlViewCapture.java @@ -4,9 +4,10 @@ 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; import me.cortex.voxy.client.core.gl.shader.ShaderType; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; -import static org.lwjgl.opengl.ARBDirectStateAccess.glClearNamedFramebufferfv; -import static org.lwjgl.opengl.ARBDirectStateAccess.glTextureParameteri; +import static org.lwjgl.opengl.ARBDirectStateAccess.*; import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_FRAMEBUFFER_BARRIER_BIT; import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_PIXEL_BUFFER_BARRIER_BIT; import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; @@ -64,7 +65,12 @@ public class GlViewCapture { } public void clear() { - glClearNamedFramebufferfv(this.framebuffer.id, GL_COLOR, 0, new float[]{0,0,0,0}); + try (var stack = MemoryStack.stackPush()) { + long ptr = stack.nmalloc(4*4); + MemoryUtil.memPutLong(ptr, 0); + MemoryUtil.memPutLong(ptr+8, 0); + nglClearNamedFramebufferfv(this.framebuffer.id, GL_COLOR, 0, ptr); + } glClearNamedFramebufferfi(this.framebuffer.id, GL_DEPTH_STENCIL, 0, 1.0f, 0); } 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 index ba46c392..10c0fbc4 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/ChunkBoundRenderer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/ChunkBoundRenderer.java @@ -44,8 +44,6 @@ public class ChunkBoundRenderer { .ubo(0, this.uniformBuffer) .ssbo(1, this.chunkPosBuffer); - private GlTexture depthBuffer = new GlTexture().store(GL_DEPTH_COMPONENT24, 1, 128, 128); - private final GlFramebuffer frameBuffer = new GlFramebuffer().bind(GL_DEPTH_ATTACHMENT, this.depthBuffer).verify(); private final LongOpenHashSet addQueue = new LongOpenHashSet(); private final LongOpenHashSet remQueue = new LongOpenHashSet(); @@ -72,19 +70,14 @@ public class ChunkBoundRenderer { this.remQueue.forEach(this::_remPos);//TODO: REPLACE WITH SCATTER COMPUTE this.remQueue.clear(); if (this.chunk2idx.isEmpty()&&!wasEmpty) {//When going from stuff to nothing need to clear the depth buffer - glClearNamedFramebufferfv(this.frameBuffer.id, GL_DEPTH, 0, new float[]{0}); + viewport.depthBoundingBuffer.clear(0); } } - if (this.depthBuffer.getWidth() != viewport.width || this.depthBuffer.getHeight() != viewport.height) { - this.depthBuffer.free(); - this.depthBuffer = new GlTexture().store(GL_DEPTH_COMPONENT24, 1, viewport.width, viewport.height); - this.frameBuffer.bind(GL_DEPTH_ATTACHMENT, this.depthBuffer).verify(); - glClearNamedFramebufferfv(this.frameBuffer.id, GL_DEPTH, 0, new float[]{0}); - } - if (this.chunk2idx.isEmpty() && this.addQueue.isEmpty()) return; + viewport.depthBoundingBuffer.clear(0); + long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, 128); long matPtr = ptr; ptr += 4*4*4; @@ -115,14 +108,13 @@ public class ChunkBoundRenderer { glFrontFace(GL_CW);//Reverse winding order //"reverse depth buffer" it goes from 0->1 where 1 is far away - glClearNamedFramebufferfv(this.frameBuffer.id, GL_DEPTH, 0, new float[]{0}); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_GREATER); } glBindVertexArray(RenderService.STATIC_VAO); - glBindFramebuffer(GL_FRAMEBUFFER, this.frameBuffer.id); + viewport.depthBoundingBuffer.bind(); this.rasterShader.bind(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE_BB_BYTE.id()); @@ -223,15 +215,8 @@ public class ChunkBoundRenderer { } public void free() { - this.depthBuffer.free(); - this.frameBuffer.free(); - this.rasterShader.free(); this.uniformBuffer.free(); this.chunkPosBuffer.free(); } - - public GlTexture getDepthBoundTexture() { - return this.depthBuffer; - } } 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 a2eb6f6b..702f9f2a 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 @@ -42,42 +42,19 @@ public class RenderService, J extends Vi private final WorldEngine world; - private static long getGeometryBufferSize() { - long geometryCapacity = Math.min((1L<<(64-Long.numberOfLeadingZeros(Capabilities.INSTANCE.ssboMaxSize-1)))<<1, 1L<<32)-1024/*(1L<<32)-1024*/; - if (Capabilities.INSTANCE.isIntel) { - geometryCapacity = Math.max(geometryCapacity, 1L<<30);//intel moment, force min 1gb - } - - //Limit to available dedicated memory if possible - if (Capabilities.INSTANCE.canQueryGpuMemory) { - //512mb less than avalible, - long limit = Capabilities.INSTANCE.getFreeDedicatedGpuMemory() - 1024*1024*1024; - // Give a minimum of 512 mb requirement - limit = Math.max(512*1024*1024, limit); - - geometryCapacity = Math.min(geometryCapacity, limit); - } - //geometryCapacity = 1<<28; - //geometryCapacity = 1<<30;//1GB test - var override = System.getProperty("voxy.geometryBufferSizeOverrideMB", ""); - if (!override.isEmpty()) { - geometryCapacity = Long.parseLong(override)*1024L*1024L; - } - return geometryCapacity; - } @SuppressWarnings("unchecked") public RenderService(WorldEngine world, ServiceThreadPool serviceThreadPool) { this.world = world; this.modelService = new ModelBakerySubsystem(world.getMapper()); - long geometryCapacity = getGeometryBufferSize(); + long geometryCapacity = 0; this.geometryData = (Q) new BasicSectionGeometryData(1<<20, geometryCapacity); //Max sections: ~500k - this.sectionRenderer = (T) new MDICSectionRenderer(this.modelService.getStore(), (BasicSectionGeometryData) this.geometryData); - Logger.info("Using renderer: " + this.sectionRenderer.getClass().getSimpleName() + " with geometry buffer of: " + geometryCapacity + " bytes"); + this.sectionRenderer = (T) new MDICSectionRenderer(null, this.modelService.getStore(), (BasicSectionGeometryData) this.geometryData); + //Logger.info("Using renderer: " + this.sectionRenderer.getClass().getSimpleName() + " with geometry buffer of: " + geometryCapacity + " bytes"); //Do something incredibly hacky, we dont need to keep the reference to this around, so just connect and discard @@ -135,7 +112,7 @@ public class RenderService, J extends Vi TimingStatistics.G.start(); - this.sectionRenderer.renderOpaque(viewport, depthBoundTexture); + this.sectionRenderer.renderOpaque(viewport); TimingStatistics.G.stop(); { @@ -193,12 +170,12 @@ public class RenderService, J extends Vi TimingStatistics.H.stop(); TimingStatistics.G.start(); - this.sectionRenderer.renderTemporal(viewport, depthBoundTexture); + this.sectionRenderer.renderTemporal(viewport); TimingStatistics.G.stop(); } - public void renderFarAwayTranslucent(J viewport, GlTexture depthBoundTexture) { - this.sectionRenderer.renderTranslucent(viewport, depthBoundTexture); + public void renderFarAwayTranslucent(J viewport) { + this.sectionRenderer.renderTranslucent(viewport); } public void addDebugData(List debug) { 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 fa6ae6ab..b952c60c 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,6 +1,7 @@ package me.cortex.voxy.client.core.rendering; import me.cortex.voxy.client.core.gl.GlBuffer; +import me.cortex.voxy.client.core.rendering.util.DepthFramebuffer; import me.cortex.voxy.client.core.rendering.util.HiZBuffer; import net.caffeinemc.mods.sodium.client.util.FogParameters; import net.minecraft.util.math.MathHelper; @@ -11,6 +12,8 @@ import java.lang.reflect.Field; public abstract class Viewport > { //public final HiZBuffer2 hiZBuffer = new HiZBuffer2(); public final HiZBuffer hiZBuffer = new HiZBuffer(); + public final DepthFramebuffer depthBoundingBuffer = new DepthFramebuffer(); + private static final Field planesField; static { try { @@ -53,6 +56,7 @@ public abstract class Viewport > { protected void delete0() { this.hiZBuffer.free(); + this.depthBoundingBuffer.free(); } public A setProjection(Matrix4f projection) { @@ -101,6 +105,8 @@ public abstract class Viewport > { (float) (this.cameraY-(sy<<5)), (float) (this.cameraZ-(sz<<5))); + this.depthBoundingBuffer.resize(this.width, this.height); + return (A) this; } 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 593df4b4..380d23a9 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 @@ -15,6 +15,7 @@ import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.util.MemoryBuffer; import me.cortex.voxy.common.world.WorldEngine; +import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; import static me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil.PRINTF_processor; @@ -131,7 +132,9 @@ public class HierarchicalOcclusionTraverser { //Use clear buffer, yes know is a bad idea, TODO: replace //Add the new top level node to the queue - glClearNamedBufferSubData(this.topNodeIds.id, GL_R32UI, aid*4L, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[]{id}); + MemoryUtil.memPutInt(SCRATCH, id); + nglClearNamedBufferSubData(this.topNodeIds.id, GL_R32UI, aid * 4L, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, SCRATCH); + if (this.topNode2idxMapping.put(id, aid) != -1) { throw new IllegalStateException(); } @@ -158,8 +161,10 @@ public class HierarchicalOcclusionTraverser { this.idx2topNodeMapping[idx] = endTLNId;//Set the old to the new if (this.topNode2idxMapping.put(endTLNId, idx) == -1) throw new IllegalStateException(); + //Move it server side, from end to new idx - glClearNamedBufferSubData(this.topNodeIds.id, GL_R32UI, idx*4L, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[]{endTLNId}); + MemoryUtil.memPutInt(SCRATCH, endTLNId); + nglClearNamedBufferSubData(this.topNodeIds.id, GL_R32UI, idx*4L, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, SCRATCH); } private static void setFrustum(Viewport viewport, long ptr) { @@ -359,4 +364,6 @@ public class HierarchicalOcclusionTraverser { this.scratchQueueB.free(); glDeleteSamplers(this.hizSampler); } + + private static final long SCRATCH = MemoryUtil.nmemAlloc(32);//32 bytes of scratch memory } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/post/FullscreenBlit.java b/src/main/java/me/cortex/voxy/client/core/rendering/post/FullscreenBlit.java index a1f6eb05..220f9d55 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/post/FullscreenBlit.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/post/FullscreenBlit.java @@ -17,9 +17,18 @@ public class FullscreenBlit { public FullscreenBlit(String fragId) { this(fragId, (a)->a); } + + public FullscreenBlit(String vertId, String fragId) { + this(vertId, fragId, (a)->a); + } + public FullscreenBlit(String fragId, Function, Shader.Builder> builder) { + this("voxy:post/fullscreen.vert", fragId, builder); + } + + public FullscreenBlit(String vertId, String fragId, Function, Shader.Builder> builder) { this.shader = builder.apply((Shader.Builder) Shader.make() - .add(ShaderType.VERTEX, "voxy:post/fullscreen.vert") + .add(ShaderType.VERTEX, vertId) .add(ShaderType.FRAGMENT, fragId)) .compile(); } diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/post/PostProcessing.java b/src/main/java/me/cortex/voxy/client/core/rendering/post/PostProcessing.java index 2d16d4cd..69bd0d11 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/post/PostProcessing.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/post/PostProcessing.java @@ -32,7 +32,7 @@ public class PostProcessing { private final FullscreenBlit emptyBlit = new FullscreenBlit("voxy:post/noop.frag"); //private final FullscreenBlit blitTexture = new FullscreenBlit("voxy:post/blit_texture_cutout.frag"); private final FullscreenBlit blitTexture = new FullscreenBlit("voxy:post/blit_texture_depth_cutout.frag", - a->a.defineIf("USE_ENV_FOG", useEnvFog)); + a->a.defineIf("USE_ENV_FOG", useEnvFog).define("EMIT_COLOUR")); private final Shader ssaoComp = Shader.make() .add(ShaderType.COMPUTE, "voxy:post/ssao.comp") .compile(); @@ -174,9 +174,9 @@ public class PostProcessing { float[] data = new float[4*4]; new Matrix4f(vp.MVP).invert().get(data); - glUniformMatrix4fv(2, false, data);//inverse fromProjection + glUniformMatrix4fv(1, false, data);//inverse fromProjection new Matrix4f(tooProjection).mul(vp.modelView).get(data); - glUniformMatrix4fv(3, false, data);//tooProjection + glUniformMatrix4fv(2, false, data);//tooProjection if (useEnvFog) { float start = vp.fogParameters.environmentalStart(); float end = vp.fogParameters.environmentalEnd(); @@ -185,9 +185,9 @@ public class PostProcessing { glUniform3f(5, vp.fogParameters.red(), vp.fogParameters.green(), vp.fogParameters.blue()); } - glBindTextureUnit(0, this.didSSAO?this.colourSSAO.id:this.colour.id); + glBindTextureUnit(0, this.depthStencil.id); + glBindTextureUnit(3, this.didSSAO?this.colourSSAO.id:this.colour.id); - glBindTextureUnit(1, this.depthStencil.id); //glTextureParameteri(this.depthStencil.id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT); glEnable(GL_DEPTH_TEST); 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 26f8d8f3..28c36060 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 @@ -17,10 +17,10 @@ public abstract class AbstractSectionRenderer , J extends this.modelStore = modelStore; } - public abstract void renderOpaque(T viewport, GlTexture depthBoundTexture); + public abstract void renderOpaque(T viewport); public abstract void buildDrawCalls(T viewport); - public abstract void renderTemporal(T viewport, GlTexture depthBoundTexture); - public abstract void renderTranslucent(T viewport, GlTexture depthBoundTexture); + public abstract void renderTemporal(T viewport); + public abstract void renderTranslucent(T viewport); public abstract T createViewport(); public abstract void free(); 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 25fd15e7..2cd08f83 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,10 +2,12 @@ package me.cortex.voxy.client.core.rendering.section; import me.cortex.voxy.client.RenderStatistics; +import me.cortex.voxy.client.core.AbstractRenderPipeline; import me.cortex.voxy.client.core.gl.Capabilities; import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.GlTexture; import me.cortex.voxy.client.core.gl.shader.Shader; +import me.cortex.voxy.client.core.gl.shader.ShaderLoader; import me.cortex.voxy.client.core.gl.shader.ShaderType; import me.cortex.voxy.client.core.model.ModelStore; import me.cortex.voxy.client.core.rendering.RenderService; @@ -41,11 +43,8 @@ public class MDICSectionRenderer extends AbstractSectionRenderer> cir) { var ret = cir.getReturnValue(); var instance = VoxyCommon.getInstance(); diff --git a/src/main/resources/assets/voxy/shaders/post/blit_texture_depth_cutout.frag b/src/main/resources/assets/voxy/shaders/post/blit_texture_depth_cutout.frag index 0b3c0487..84c2384c 100644 --- a/src/main/resources/assets/voxy/shaders/post/blit_texture_depth_cutout.frag +++ b/src/main/resources/assets/voxy/shaders/post/blit_texture_depth_cutout.frag @@ -1,13 +1,16 @@ #version 450 core -layout(binding = 0) uniform sampler2D colourTex; -layout(binding = 1) uniform sampler2D depthTex; -layout(location = 2) uniform mat4 invProjMat; -layout(location = 3) uniform mat4 projMat; +layout(binding = 0) uniform sampler2D depthTex; +layout(location = 1) uniform mat4 invProjMat; +layout(location = 2) uniform mat4 projMat; + +#ifdef EMIT_COLOUR +layout(binding = 3) uniform sampler2D colourTex; #ifdef USE_ENV_FOG layout(location = 4) uniform vec3 endParams; layout(location = 5) uniform vec3 fogColour; #endif +#endif out vec4 colour; in vec2 UV; @@ -22,28 +25,31 @@ float projDepth(vec3 pos) { } void main() { - colour = texture(colourTex, UV.xy); - if (colour.a == 0.0) { - discard; - } - float depth = texture(depthTex, UV.xy).r; if (depth == 0.0f) { discard; } vec3 point = rev3d(vec3(UV.xy, depth)); + depth = projDepth(point); + depth = min(1.0f-(2.0f/((1<<24)-1)), depth); + depth = depth * 0.5f + 0.5f; + depth = gl_DepthRange.diff * depth + gl_DepthRange.near; + gl_FragDepth = depth; + #ifdef EMIT_COLOUR + colour = texture(colourTex, UV.xy); + if (colour.a == 0.0) { + discard; + } #ifdef USE_ENV_FOG { float fogLerp = max(fma(min(length(point.xyz), endParams.x),endParams.y,endParams.z),0);//512 is 32*16 which is the render distance in blocks colour.rgb = mix(colour.rgb, fogColour, fogLerp); } #endif + #else + colour = vec4(0); + #endif - depth = projDepth(point); - depth = min(1.0f-(2.0f/((1<<24)-1)), depth); - depth = depth * 0.5f + 0.5f; - depth = gl_DepthRange.diff * depth + gl_DepthRange.near; - gl_FragDepth = depth; } \ No newline at end of file diff --git a/src/main/resources/assets/voxy/shaders/post/fullscreen2.vert b/src/main/resources/assets/voxy/shaders/post/fullscreen2.vert new file mode 100644 index 00000000..884b1d56 --- /dev/null +++ b/src/main/resources/assets/voxy/shaders/post/fullscreen2.vert @@ -0,0 +1,7 @@ +#version 330 core + +out vec2 UV; +void main() { + gl_Position = vec4(vec2(gl_VertexID&1, (gl_VertexID>>1)&1) * 4 - 1, 1f, 1); + UV = gl_Position.xy*0.5+0.5; +} \ No newline at end of file