This commit is contained in:
mcrcortex
2025-08-07 10:44:08 +10:00
parent 16952e13e4
commit c8b0df6ff9
19 changed files with 842 additions and 231 deletions

View File

@@ -2,6 +2,10 @@ package me.cortex.voxy.client;
import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldEngine;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class RenderStatistics { public class RenderStatistics {
public static boolean enabled = false; 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[] hierarchicalRenderSections = new int[WorldEngine.MAX_LOD_LAYER+1];
public static final int[] visibleSections = 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 final int[] quadCount = new int[WorldEngine.MAX_LOD_LAYER+1];
public static void addDebug(List<String> 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;
}
} }

View File

@@ -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<String> 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;
}
}

View File

@@ -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();
}
}

View File

@@ -5,12 +5,20 @@ import com.mojang.blaze3d.opengl.GlStateManager;
import me.cortex.voxy.client.TimingStatistics; import me.cortex.voxy.client.TimingStatistics;
import me.cortex.voxy.client.VoxyClient; import me.cortex.voxy.client.VoxyClient;
import me.cortex.voxy.client.config.VoxyConfig; 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.GlBuffer;
import me.cortex.voxy.client.core.gl.GlTexture; import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.rendering.ChunkBoundRenderer; import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
import me.cortex.voxy.client.core.rendering.RenderDistanceTracker; import me.cortex.voxy.client.core.model.ModelStore;
import me.cortex.voxy.client.core.rendering.RenderService; import me.cortex.voxy.client.core.rendering.*;
import me.cortex.voxy.client.core.rendering.post.PostProcessing; 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.DownloadStream;
import me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil; import me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil;
import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream;
@@ -25,22 +33,40 @@ import org.joml.Matrix4f;
import org.joml.Matrix4fc; import org.joml.Matrix4fc;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import java.util.Arrays;
import java.util.List; import java.util.List;
import static org.lwjgl.opengl.GL11.GL_VIEWPORT; import static org.lwjgl.opengl.GL11.GL_VIEWPORT;
import static org.lwjgl.opengl.GL11.glGetIntegerv; 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.GL_DRAW_FRAMEBUFFER_BINDING;
import static org.lwjgl.opengl.GL30C.glBindFramebuffer; import static org.lwjgl.opengl.GL30C.glBindFramebuffer;
import static org.lwjgl.opengl.GL33.glBindSampler; import static org.lwjgl.opengl.GL33.glBindSampler;
public class VoxyRenderSystem { public class VoxyRenderSystem {
private final RenderService renderer;
private final PostProcessing postProcessing;
private final WorldEngine worldIn; 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; private final RenderDistanceTracker renderDistanceTracker;
public final ChunkBoundRenderer chunkBoundRenderer; 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) { 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 //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 // than timeout, we keep the world acquired
@@ -51,41 +77,185 @@ public class VoxyRenderSystem {
glFinish(); glFinish();
this.worldIn = world; 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 long geometryCapacity = getGeometryBufferSize();
if (false) { {
minSec = -8;
maxSec = 7;
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, this.pipeline = RenderPipelineFactory.createPipeline(this.nodeManager, this.nodeCleaner, this.traversal, this::frexStillHasWork);
minSec, var sectionRenderer = createSectionRenderer(this.pipeline, this.modelService.getStore(), this.geometryData);
maxSec, this.pipeline.setSectionRenderer(sectionRenderer);
this.renderer::addTopLevelNode, this.viewportSelector = new ViewportSelector<>(sectionRenderer::createViewport);
this.renderer::removeTopLevelNode);
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(); 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) { } catch (RuntimeException e) {
world.releaseRef();//If something goes wrong, we must release the world first world.releaseRef();//If something goes wrong, we must release the world first
throw e; throw e;
} }
} }
public void setRenderDistance(int renderDistance) { public void renderOpaque(ChunkRenderMatrices matrices, FogParameters fogParameters, double cameraX, double cameraY, double cameraZ) {
this.renderDistanceTracker.setRenderDistance(renderDistance); 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() { private void autoBalanceSubDivSize() {
//only increase quality while there are very few mesh queues, this stops, //only increase quality while there are very few mesh queues, this stops,
// e.g. while flying and is rendering alot of low quality chunks // 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; float CHANGE_PER_SECOND = 30;
//Auto fps targeting //Auto fps targeting
if (MinecraftClient.getInstance().getCurrentFps() < 45) { if (MinecraftClient.getInstance().getCurrentFps() < 45) {
@@ -120,119 +290,38 @@ public class VoxyRenderSystem {
).mulLocal(makeProjectionMatrix(16, 16*3000)); ).mulLocal(makeProjectionMatrix(16, 16*3000));
} }
public void renderOpaque(ChunkRenderMatrices matrices, FogParameters fogParameters, double cameraX, double cameraY, double cameraZ) { private boolean frexStillHasWork() {
if (IrisUtil.irisShadowActive()) { if (!VoxyClient.isFrexActive()) {
return; return false;
} }
TimingStatistics.resetSamplers(); //If frex is running we must tick everything to ensure correctness
UploadStream.INSTANCE.tick();
//Done here as is allows less gl state resetup
//Do some very cheeky stuff for MiB this.modelService.tick(100_000_000);
if (false) { GL11.glFinish();
int sector = (((int)Math.floor(cameraX)>>4)+512)>>10; return this.nodeManager.hasWork() || this.renderGen.getTaskCount()!=0 || !this.modelService.areQueuesEmpty();
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();
} }
public void setRenderDistance(int renderDistance) {
this.renderDistanceTracker.setRenderDistance(renderDistance);
}
public Viewport<?> getViewport() {
return this.viewportSelector.getViewport();
}
public void addDebugInfo(List<String> debug) { public void addDebugInfo(List<String> debug) {
debug.add("Buf/Tex [#/Mb]: [" + GlBuffer.getCount() + "/" + (GlBuffer.getTotalSize()/1_000_000) + "],[" + GlTexture.getCount() + "/" + (GlTexture.getEstimatedTotalSize()/1_000_000)+"]"); 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(); TimingStatistics.update();
debug.add("Voxy frame runtime (millis): " + TimingStatistics.dynamic.pVal() + ", " + TimingStatistics.main.pVal()+ ", " + TimingStatistics.postDynamic.pVal()+ ", " + TimingStatistics.all.pVal()); 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"); Logger.info("Flushing download stream");
DownloadStream.INSTANCE.flushWaitClear(); DownloadStream.INSTANCE.flushWaitClear();
Logger.info("Shutting down rendering"); Logger.info("Shutting down rendering");
try {this.renderer.shutdown();this.chunkBoundRenderer.free();} catch (Exception e) {Logger.error("Error shutting down renderer", e);} try {
Logger.info("Shutting down post processor"); //Cleanup callbacks
if (this.postProcessing!=null){try {this.postProcessing.shutdown();} catch (Exception e) {Logger.error("Error shutting down post processor", e);}} 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"); Logger.info("Flushing download stream");
DownloadStream.INSTANCE.flushWaitClear(); DownloadStream.INSTANCE.flushWaitClear();
//Release hold on the world //Release hold on the world
this.worldIn.releaseRef(); 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;
} }
} }

View File

@@ -2,6 +2,8 @@ package me.cortex.voxy.client.core.gl;
import me.cortex.voxy.common.util.TrackedObject; import me.cortex.voxy.common.util.TrackedObject;
import org.lwjgl.opengl.GL11; 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.GL11.GL_UNSIGNED_BYTE;
import static org.lwjgl.opengl.GL15.glDeleteBuffers; 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_ROWS, 0);
glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 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; return this;
} }
@@ -72,4 +75,6 @@ public class GlBuffer extends TrackedObject {
public GlBuffer name(String name) { public GlBuffer name(String name) {
return GlDebug.name(name, this); return GlDebug.name(name, this);
} }
private static final long SCRATCH = MemoryUtil.nmemAlloc(4);
} }

View File

@@ -77,6 +77,13 @@ public class Shader extends TrackedObject {
this.processor = processor; this.processor = processor;
} }
public Builder<T> clone() {
var clone = new Builder<>(this.constructor, this.processor);
clone.defines.putAll(this.defines);
clone.sources.putAll(this.sources);
return clone;
}
public Builder<T> define(String name) { public Builder<T> define(String name) {
this.defines.put(name, ""); this.defines.put(name, "");
return this; return this;

View File

@@ -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.GlTexture;
import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType; 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.*;
import static org.lwjgl.opengl.ARBDirectStateAccess.glTextureParameteri;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_FRAMEBUFFER_BARRIER_BIT; 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_PIXEL_BUFFER_BARRIER_BIT;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
@@ -64,7 +65,12 @@ public class GlViewCapture {
} }
public void clear() { 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); glClearNamedFramebufferfi(this.framebuffer.id, GL_DEPTH_STENCIL, 0, 1.0f, 0);
} }

View File

@@ -44,8 +44,6 @@ public class ChunkBoundRenderer {
.ubo(0, this.uniformBuffer) .ubo(0, this.uniformBuffer)
.ssbo(1, this.chunkPosBuffer); .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 addQueue = new LongOpenHashSet();
private final LongOpenHashSet remQueue = 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.forEach(this::_remPos);//TODO: REPLACE WITH SCATTER COMPUTE
this.remQueue.clear(); this.remQueue.clear();
if (this.chunk2idx.isEmpty()&&!wasEmpty) {//When going from stuff to nothing need to clear the depth buffer 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; if (this.chunk2idx.isEmpty() && this.addQueue.isEmpty()) return;
viewport.depthBoundingBuffer.clear(0);
long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, 128); long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, 128);
long matPtr = ptr; ptr += 4*4*4; long matPtr = ptr; ptr += 4*4*4;
@@ -115,14 +108,13 @@ public class ChunkBoundRenderer {
glFrontFace(GL_CW);//Reverse winding order glFrontFace(GL_CW);//Reverse winding order
//"reverse depth buffer" it goes from 0->1 where 1 is far away //"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_CULL_FACE);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_GREATER); glDepthFunc(GL_GREATER);
} }
glBindVertexArray(RenderService.STATIC_VAO); glBindVertexArray(RenderService.STATIC_VAO);
glBindFramebuffer(GL_FRAMEBUFFER, this.frameBuffer.id); viewport.depthBoundingBuffer.bind();
this.rasterShader.bind(); this.rasterShader.bind();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE_BB_BYTE.id()); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE_BB_BYTE.id());
@@ -223,15 +215,8 @@ public class ChunkBoundRenderer {
} }
public void free() { public void free() {
this.depthBuffer.free();
this.frameBuffer.free();
this.rasterShader.free(); this.rasterShader.free();
this.uniformBuffer.free(); this.uniformBuffer.free();
this.chunkPosBuffer.free(); this.chunkPosBuffer.free();
} }
public GlTexture getDepthBoundTexture() {
return this.depthBuffer;
}
} }

View File

@@ -42,42 +42,19 @@ public class RenderService<T extends AbstractSectionRenderer<J, Q>, J extends Vi
private final WorldEngine world; 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") @SuppressWarnings("unchecked")
public RenderService(WorldEngine world, ServiceThreadPool serviceThreadPool) { public RenderService(WorldEngine world, ServiceThreadPool serviceThreadPool) {
this.world = world; this.world = world;
this.modelService = new ModelBakerySubsystem(world.getMapper()); this.modelService = new ModelBakerySubsystem(world.getMapper());
long geometryCapacity = getGeometryBufferSize(); long geometryCapacity = 0;
this.geometryData = (Q) new BasicSectionGeometryData(1<<20, geometryCapacity); this.geometryData = (Q) new BasicSectionGeometryData(1<<20, geometryCapacity);
//Max sections: ~500k //Max sections: ~500k
this.sectionRenderer = (T) new MDICSectionRenderer(this.modelService.getStore(), (BasicSectionGeometryData) this.geometryData); 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"); //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 //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<T extends AbstractSectionRenderer<J, Q>, J extends Vi
TimingStatistics.G.start(); TimingStatistics.G.start();
this.sectionRenderer.renderOpaque(viewport, depthBoundTexture); this.sectionRenderer.renderOpaque(viewport);
TimingStatistics.G.stop(); TimingStatistics.G.stop();
{ {
@@ -193,12 +170,12 @@ public class RenderService<T extends AbstractSectionRenderer<J, Q>, J extends Vi
TimingStatistics.H.stop(); TimingStatistics.H.stop();
TimingStatistics.G.start(); TimingStatistics.G.start();
this.sectionRenderer.renderTemporal(viewport, depthBoundTexture); this.sectionRenderer.renderTemporal(viewport);
TimingStatistics.G.stop(); TimingStatistics.G.stop();
} }
public void renderFarAwayTranslucent(J viewport, GlTexture depthBoundTexture) { public void renderFarAwayTranslucent(J viewport) {
this.sectionRenderer.renderTranslucent(viewport, depthBoundTexture); this.sectionRenderer.renderTranslucent(viewport);
} }
public void addDebugData(List<String> debug) { public void addDebugData(List<String> debug) {

View File

@@ -1,6 +1,7 @@
package me.cortex.voxy.client.core.rendering; package me.cortex.voxy.client.core.rendering;
import me.cortex.voxy.client.core.gl.GlBuffer; 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 me.cortex.voxy.client.core.rendering.util.HiZBuffer;
import net.caffeinemc.mods.sodium.client.util.FogParameters; import net.caffeinemc.mods.sodium.client.util.FogParameters;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
@@ -11,6 +12,8 @@ import java.lang.reflect.Field;
public abstract class Viewport <A extends Viewport<A>> { public abstract class Viewport <A extends Viewport<A>> {
//public final HiZBuffer2 hiZBuffer = new HiZBuffer2(); //public final HiZBuffer2 hiZBuffer = new HiZBuffer2();
public final HiZBuffer hiZBuffer = new HiZBuffer(); public final HiZBuffer hiZBuffer = new HiZBuffer();
public final DepthFramebuffer depthBoundingBuffer = new DepthFramebuffer();
private static final Field planesField; private static final Field planesField;
static { static {
try { try {
@@ -53,6 +56,7 @@ public abstract class Viewport <A extends Viewport<A>> {
protected void delete0() { protected void delete0() {
this.hiZBuffer.free(); this.hiZBuffer.free();
this.depthBoundingBuffer.free();
} }
public A setProjection(Matrix4f projection) { public A setProjection(Matrix4f projection) {
@@ -101,6 +105,8 @@ public abstract class Viewport <A extends Viewport<A>> {
(float) (this.cameraY-(sy<<5)), (float) (this.cameraY-(sy<<5)),
(float) (this.cameraZ-(sz<<5))); (float) (this.cameraZ-(sz<<5)));
this.depthBoundingBuffer.resize(this.width, this.height);
return (A) this; return (A) this;
} }

View File

@@ -15,6 +15,7 @@ import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.Logger; import me.cortex.voxy.common.Logger;
import me.cortex.voxy.common.util.MemoryBuffer; import me.cortex.voxy.common.util.MemoryBuffer;
import me.cortex.voxy.common.world.WorldEngine; import me.cortex.voxy.common.world.WorldEngine;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import static me.cortex.voxy.client.core.rendering.util.PrintfDebugUtil.PRINTF_processor; 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 //Use clear buffer, yes know is a bad idea, TODO: replace
//Add the new top level node to the queue //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) { if (this.topNode2idxMapping.put(id, aid) != -1) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
@@ -158,8 +161,10 @@ public class HierarchicalOcclusionTraverser {
this.idx2topNodeMapping[idx] = endTLNId;//Set the old to the new this.idx2topNodeMapping[idx] = endTLNId;//Set the old to the new
if (this.topNode2idxMapping.put(endTLNId, idx) == -1) if (this.topNode2idxMapping.put(endTLNId, idx) == -1)
throw new IllegalStateException(); throw new IllegalStateException();
//Move it server side, from end to new idx //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) { private static void setFrustum(Viewport<?> viewport, long ptr) {
@@ -359,4 +364,6 @@ public class HierarchicalOcclusionTraverser {
this.scratchQueueB.free(); this.scratchQueueB.free();
glDeleteSamplers(this.hizSampler); glDeleteSamplers(this.hizSampler);
} }
private static final long SCRATCH = MemoryUtil.nmemAlloc(32);//32 bytes of scratch memory
} }

View File

@@ -17,9 +17,18 @@ public class FullscreenBlit {
public FullscreenBlit(String fragId) { public FullscreenBlit(String fragId) {
this(fragId, (a)->a); this(fragId, (a)->a);
} }
public FullscreenBlit(String vertId, String fragId) {
this(vertId, fragId, (a)->a);
}
public <T extends Shader> FullscreenBlit(String fragId, Function<Shader.Builder<T>, Shader.Builder<T>> builder) { public <T extends Shader> FullscreenBlit(String fragId, Function<Shader.Builder<T>, Shader.Builder<T>> builder) {
this("voxy:post/fullscreen.vert", fragId, builder);
}
public <T extends Shader> FullscreenBlit(String vertId, String fragId, Function<Shader.Builder<T>, Shader.Builder<T>> builder) {
this.shader = builder.apply((Shader.Builder<T>) Shader.make() this.shader = builder.apply((Shader.Builder<T>) Shader.make()
.add(ShaderType.VERTEX, "voxy:post/fullscreen.vert") .add(ShaderType.VERTEX, vertId)
.add(ShaderType.FRAGMENT, fragId)) .add(ShaderType.FRAGMENT, fragId))
.compile(); .compile();
} }

View File

@@ -32,7 +32,7 @@ public class PostProcessing {
private final FullscreenBlit emptyBlit = new FullscreenBlit("voxy:post/noop.frag"); 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_cutout.frag");
private final FullscreenBlit blitTexture = new FullscreenBlit("voxy:post/blit_texture_depth_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() private final Shader ssaoComp = Shader.make()
.add(ShaderType.COMPUTE, "voxy:post/ssao.comp") .add(ShaderType.COMPUTE, "voxy:post/ssao.comp")
.compile(); .compile();
@@ -174,9 +174,9 @@ public class PostProcessing {
float[] data = new float[4*4]; float[] data = new float[4*4];
new Matrix4f(vp.MVP).invert().get(data); 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); new Matrix4f(tooProjection).mul(vp.modelView).get(data);
glUniformMatrix4fv(3, false, data);//tooProjection glUniformMatrix4fv(2, false, data);//tooProjection
if (useEnvFog) { if (useEnvFog) {
float start = vp.fogParameters.environmentalStart(); float start = vp.fogParameters.environmentalStart();
float end = vp.fogParameters.environmentalEnd(); float end = vp.fogParameters.environmentalEnd();
@@ -185,9 +185,9 @@ public class PostProcessing {
glUniform3f(5, vp.fogParameters.red(), vp.fogParameters.green(), vp.fogParameters.blue()); 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); //glTextureParameteri(this.depthStencil.id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);

View File

@@ -17,10 +17,10 @@ public abstract class AbstractSectionRenderer <T extends Viewport<T>, J extends
this.modelStore = modelStore; 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 buildDrawCalls(T viewport);
public abstract void renderTemporal(T viewport, GlTexture depthBoundTexture); public abstract void renderTemporal(T viewport);
public abstract void renderTranslucent(T viewport, GlTexture depthBoundTexture); public abstract void renderTranslucent(T viewport);
public abstract T createViewport(); public abstract T createViewport();
public abstract void free(); public abstract void free();

View File

@@ -2,10 +2,12 @@ package me.cortex.voxy.client.core.rendering.section;
import me.cortex.voxy.client.RenderStatistics; 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.Capabilities;
import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlTexture; 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.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.gl.shader.ShaderType;
import me.cortex.voxy.client.core.model.ModelStore; import me.cortex.voxy.client.core.model.ModelStore;
import me.cortex.voxy.client.core.rendering.RenderService; import me.cortex.voxy.client.core.rendering.RenderService;
@@ -41,11 +43,8 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
private static final int TRANSLUCENT_OFFSET = 400_000;//in draw calls private static final int TRANSLUCENT_OFFSET = 400_000;//in draw calls
private static final int TEMPORAL_OFFSET = 500_000;//in draw calls private static final int TEMPORAL_OFFSET = 500_000;//in draw calls
private static final int STATISTICS_BUFFER_BINDING = 8; private static final int STATISTICS_BUFFER_BINDING = 8;
private final Shader terrainShader = Shader.make() private final Shader terrainShader;
.defineIf("DEBUG_RENDER", false) private final Shader translucentTerrainShader;
.add(ShaderType.VERTEX, "voxy:lod/gl46/quads2.vert")
.add(ShaderType.FRAGMENT, "voxy:lod/gl46/quads.frag")
.compile();
private final Shader commandGenShader = Shader.make() private final Shader commandGenShader = Shader.make()
.define("TRANSLUCENT_WRITE_BASE", 1024) .define("TRANSLUCENT_WRITE_BASE", 1024)
@@ -89,8 +88,32 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
//Statistics //Statistics
private final GlBuffer statisticsBuffer = new GlBuffer(1024).zero(); private final GlBuffer statisticsBuffer = new GlBuffer(1024).zero();
public MDICSectionRenderer(ModelStore modelStore, BasicSectionGeometryData geometryData) { private final AbstractRenderPipeline pipeline;
public MDICSectionRenderer(AbstractRenderPipeline pipeline, ModelStore modelStore, BasicSectionGeometryData geometryData) {
super(modelStore, geometryData); super(modelStore, geometryData);
this.pipeline = pipeline;
//The pipeline can be used to transform the renderer in abstract ways
var builder = Shader.make()
.defineIf("DEBUG_RENDER", false)
.add(ShaderType.VERTEX, "voxy:lod/gl46/quads2.vert");
String frag = ShaderLoader.parse("voxy:lod/gl46/quads.frag");
String opaqueFrag = pipeline.patchOpaqueShader(this, frag);
opaqueFrag = opaqueFrag==null?frag:opaqueFrag;
this.terrainShader = builder.clone()
.addSource(ShaderType.FRAGMENT, opaqueFrag)
.compile();
String translucentFrag = pipeline.patchTranslucentShader(this, frag);
if (translucentFrag == null) {
this.translucentTerrainShader = builder.clone()
.addSource(ShaderType.FRAGMENT, opaqueFrag)
.compile();
} else {
this.translucentTerrainShader = this.terrainShader;
}
} }
private void uploadUniformBuffer(MDICViewport viewport) { private void uploadUniformBuffer(MDICViewport viewport) {
@@ -113,28 +136,30 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
} }
private void bindRenderingBuffers(MDICViewport viewport, GlTexture depthBoundTexture) { private void bindRenderingBuffers(MDICViewport viewport) {
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id); glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometryManager.getGeometryBuffer().id); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometryManager.getGeometryBuffer().id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.geometryManager.getMetadataBuffer().id); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.geometryManager.getMetadataBuffer().id);
this.modelStore.bind(3, 4, 0); this.modelStore.bind(3, 4, 0);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, viewport.positionScratchBuffer.id); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, viewport.positionScratchBuffer.id);
LightMapHelper.bind(1); LightMapHelper.bind(1);
glBindTextureUnit(2, depthBoundTexture.id); glBindTextureUnit(2, viewport.depthBoundingBuffer.getDepthTex().id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id()); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id());
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, viewport.drawCallBuffer.id); glBindBuffer(GL_DRAW_INDIRECT_BUFFER, viewport.drawCallBuffer.id);
glBindBuffer(GL_PARAMETER_BUFFER_ARB, viewport.drawCountCallBuffer.id); glBindBuffer(GL_PARAMETER_BUFFER_ARB, viewport.drawCountCallBuffer.id);
} }
private void renderTerrain(MDICViewport viewport, GlTexture depthBoundTexture, long indirectOffset, long drawCountOffset, int maxDrawCount) { private void renderTerrain(MDICViewport viewport, long indirectOffset, long drawCountOffset, int maxDrawCount) {
//RenderLayer.getCutoutMipped().startDrawing(); //RenderLayer.getCutoutMipped().startDrawing();
this.pipeline.bindOpaqueFramebuffer();
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
this.terrainShader.bind(); this.terrainShader.bind();
glBindVertexArray(RenderService.STATIC_VAO);//Needs to be before binding glBindVertexArray(RenderService.STATIC_VAO);//Needs to be before binding
this.bindRenderingBuffers(viewport, depthBoundTexture); this.bindRenderingBuffers(viewport);
glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed
glProvokingVertex(GL_FIRST_VERTEX_CONVENTION); glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
@@ -151,25 +176,27 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
} }
@Override @Override
public void renderOpaque(MDICViewport viewport, GlTexture dbt) { public void renderOpaque(MDICViewport viewport) {
if (this.geometryManager.getSectionCount() == 0) return; if (this.geometryManager.getSectionCount() == 0) return;
this.uploadUniformBuffer(viewport); this.uploadUniformBuffer(viewport);
this.renderTerrain(viewport, dbt, 0, 4*3, Math.min((int)(this.geometryManager.getSectionCount()*4.4+128), 400_000)); this.renderTerrain(viewport, 0, 4*3, Math.min((int)(this.geometryManager.getSectionCount()*4.4+128), 400_000));
} }
@Override @Override
public void renderTranslucent(MDICViewport viewport, GlTexture depthBoundTexture) { public void renderTranslucent(MDICViewport viewport) {
if (this.geometryManager.getSectionCount() == 0) return; if (this.geometryManager.getSectionCount() == 0) return;
this.pipeline.bindTranslucentFramebuffer();
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
this.terrainShader.bind(); this.translucentTerrainShader.bind();
glBindVertexArray(RenderService.STATIC_VAO);//Needs to be before binding glBindVertexArray(RenderService.STATIC_VAO);//Needs to be before binding
this.bindRenderingBuffers(viewport, depthBoundTexture); this.bindRenderingBuffers(viewport);
glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed
glProvokingVertex(GL_FIRST_VERTEX_CONVENTION); glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
@@ -289,10 +316,10 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
} }
@Override @Override
public void renderTemporal(MDICViewport viewport, GlTexture dbt) { public void renderTemporal(MDICViewport viewport) {
if (this.geometryManager.getSectionCount() == 0) return; if (this.geometryManager.getSectionCount() == 0) return;
//Render temporal //Render temporal
this.renderTerrain(viewport, dbt, TEMPORAL_OFFSET*5*4, 4*5, Math.min(this.geometryManager.getSectionCount(), 100_000)); this.renderTerrain(viewport, TEMPORAL_OFFSET*5*4, 4*5, Math.min(this.geometryManager.getSectionCount(), 100_000));
} }
@Override @Override
@@ -308,6 +335,9 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
@Override @Override
public void free() { public void free() {
if (this.terrainShader != this.translucentTerrainShader) {
this.translucentTerrainShader.free();
}
this.uniform.free(); this.uniform.free();
this.distanceCountBuffer.free(); this.distanceCountBuffer.free();
this.terrainShader.free(); this.terrainShader.free();

View File

@@ -0,0 +1,63 @@
package me.cortex.voxy.client.core.rendering.util;
import me.cortex.voxy.client.core.gl.GlFramebuffer;
import me.cortex.voxy.client.core.gl.GlTexture;
import org.lwjgl.opengl.GL11C;
import org.lwjgl.opengl.GL14C;
import org.lwjgl.system.MemoryStack;
import static org.lwjgl.opengl.ARBDirectStateAccess.glClearNamedFramebufferfv;
import static org.lwjgl.opengl.ARBDirectStateAccess.nglClearNamedFramebufferfv;
import static org.lwjgl.opengl.GL11.GL_DEPTH_COMPONENT;
import static org.lwjgl.opengl.GL11C.GL_DEPTH;
import static org.lwjgl.opengl.GL14.GL_DEPTH_COMPONENT24;
import static org.lwjgl.opengl.GL30C.*;
public class DepthFramebuffer {
private final int depthType;
private GlTexture depthBuffer;
public final GlFramebuffer framebuffer = new GlFramebuffer();
public DepthFramebuffer() {
this(GL_DEPTH_COMPONENT24);
}
public DepthFramebuffer(int depthType) {
this.depthType = depthType;
}
public void resize(int width, int height) {
if (this.depthBuffer == null || this.depthBuffer.getWidth() != width || this.depthBuffer.getHeight() != height) {
if (this.depthBuffer != null) {
this.depthBuffer.free();
}
this.depthBuffer = new GlTexture().store(this.depthType, 1, width, height);
this.framebuffer.bind(this.depthType == GL_DEPTH24_STENCIL8?GL_DEPTH_STENCIL_ATTACHMENT: GL_DEPTH_ATTACHMENT, this.depthBuffer).verify();
}
}
public void clear() {
this.clear(1.0f);
}
public void clear(float depth) {
try (var stack = MemoryStack.stackPush()) {
nglClearNamedFramebufferfv(this.framebuffer.id, GL_DEPTH, 0, stack.nfloat(depth));
}
}
public GlTexture getDepthTex() {
return this.depthBuffer;
}
public void free() {
this.framebuffer.free();
if (this.depthBuffer != null) {
this.depthBuffer.free();
}
}
public void bind() {
glBindFramebuffer(GL_FRAMEBUFFER, this.framebuffer.id);
}
}

View File

@@ -13,7 +13,7 @@ import java.util.List;
@Mixin(DebugHud.class) @Mixin(DebugHud.class)
public class MixinDebugHud { public class MixinDebugHud {
@Inject(method = "getRightText", at = @At("TAIL")) @Inject(method = "getRightText", at = @At("RETURN"))
private void injectDebug(CallbackInfoReturnable<List<String>> cir) { private void injectDebug(CallbackInfoReturnable<List<String>> cir) {
var ret = cir.getReturnValue(); var ret = cir.getReturnValue();
var instance = VoxyCommon.getInstance(); var instance = VoxyCommon.getInstance();

View File

@@ -1,13 +1,16 @@
#version 450 core #version 450 core
layout(binding = 0) uniform sampler2D colourTex; layout(binding = 0) uniform sampler2D depthTex;
layout(binding = 1) uniform sampler2D depthTex; layout(location = 1) uniform mat4 invProjMat;
layout(location = 2) uniform mat4 invProjMat; layout(location = 2) uniform mat4 projMat;
layout(location = 3) uniform mat4 projMat;
#ifdef EMIT_COLOUR
layout(binding = 3) uniform sampler2D colourTex;
#ifdef USE_ENV_FOG #ifdef USE_ENV_FOG
layout(location = 4) uniform vec3 endParams; layout(location = 4) uniform vec3 endParams;
layout(location = 5) uniform vec3 fogColour; layout(location = 5) uniform vec3 fogColour;
#endif #endif
#endif
out vec4 colour; out vec4 colour;
in vec2 UV; in vec2 UV;
@@ -22,28 +25,31 @@ float projDepth(vec3 pos) {
} }
void main() { void main() {
colour = texture(colourTex, UV.xy);
if (colour.a == 0.0) {
discard;
}
float depth = texture(depthTex, UV.xy).r; float depth = texture(depthTex, UV.xy).r;
if (depth == 0.0f) { if (depth == 0.0f) {
discard; discard;
} }
vec3 point = rev3d(vec3(UV.xy, depth)); 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 #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 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); colour.rgb = mix(colour.rgb, fogColour, fogLerp);
} }
#endif #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;
} }

View File

@@ -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;
}