Part A
This commit is contained in:
@@ -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<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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -77,6 +77,13 @@ public class Shader extends TrackedObject {
|
||||
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) {
|
||||
this.defines.put(name, "");
|
||||
return this;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,42 +42,19 @@ public class RenderService<T extends AbstractSectionRenderer<J, Q>, 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<T extends AbstractSectionRenderer<J, Q>, 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<T extends AbstractSectionRenderer<J, Q>, 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<String> debug) {
|
||||
|
||||
@@ -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 <A extends Viewport<A>> {
|
||||
//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 <A extends Viewport<A>> {
|
||||
|
||||
protected void delete0() {
|
||||
this.hiZBuffer.free();
|
||||
this.depthBoundingBuffer.free();
|
||||
}
|
||||
|
||||
public A setProjection(Matrix4f projection) {
|
||||
@@ -101,6 +105,8 @@ public abstract class Viewport <A extends Viewport<A>> {
|
||||
(float) (this.cameraY-(sy<<5)),
|
||||
(float) (this.cameraZ-(sz<<5)));
|
||||
|
||||
this.depthBoundingBuffer.resize(this.width, this.height);
|
||||
|
||||
return (A) this;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 <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()
|
||||
.add(ShaderType.VERTEX, "voxy:post/fullscreen.vert")
|
||||
.add(ShaderType.VERTEX, vertId)
|
||||
.add(ShaderType.FRAGMENT, fragId))
|
||||
.compile();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -17,10 +17,10 @@ public abstract class AbstractSectionRenderer <T extends Viewport<T>, 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();
|
||||
|
||||
|
||||
@@ -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<MDICViewport, B
|
||||
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 STATISTICS_BUFFER_BINDING = 8;
|
||||
private final Shader terrainShader = Shader.make()
|
||||
.defineIf("DEBUG_RENDER", false)
|
||||
.add(ShaderType.VERTEX, "voxy:lod/gl46/quads2.vert")
|
||||
.add(ShaderType.FRAGMENT, "voxy:lod/gl46/quads.frag")
|
||||
.compile();
|
||||
private final Shader terrainShader;
|
||||
private final Shader translucentTerrainShader;
|
||||
|
||||
private final Shader commandGenShader = Shader.make()
|
||||
.define("TRANSLUCENT_WRITE_BASE", 1024)
|
||||
@@ -89,8 +88,32 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
|
||||
//Statistics
|
||||
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);
|
||||
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) {
|
||||
@@ -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_SHADER_STORAGE_BUFFER, 1, this.geometryManager.getGeometryBuffer().id);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.geometryManager.getMetadataBuffer().id);
|
||||
this.modelStore.bind(3, 4, 0);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, viewport.positionScratchBuffer.id);
|
||||
LightMapHelper.bind(1);
|
||||
glBindTextureUnit(2, depthBoundTexture.id);
|
||||
glBindTextureUnit(2, viewport.depthBoundingBuffer.getDepthTex().id);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id());
|
||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, viewport.drawCallBuffer.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();
|
||||
|
||||
this.pipeline.bindOpaqueFramebuffer();
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
this.terrainShader.bind();
|
||||
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
|
||||
glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
|
||||
@@ -151,25 +176,27 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderOpaque(MDICViewport viewport, GlTexture dbt) {
|
||||
public void renderOpaque(MDICViewport viewport) {
|
||||
if (this.geometryManager.getSectionCount() == 0) return;
|
||||
|
||||
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
|
||||
public void renderTranslucent(MDICViewport viewport, GlTexture depthBoundTexture) {
|
||||
public void renderTranslucent(MDICViewport viewport) {
|
||||
if (this.geometryManager.getSectionCount() == 0) return;
|
||||
this.pipeline.bindTranslucentFramebuffer();
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
this.terrainShader.bind();
|
||||
this.translucentTerrainShader.bind();
|
||||
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
|
||||
glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
|
||||
@@ -289,10 +316,10 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderTemporal(MDICViewport viewport, GlTexture dbt) {
|
||||
public void renderTemporal(MDICViewport viewport) {
|
||||
if (this.geometryManager.getSectionCount() == 0) return;
|
||||
//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
|
||||
@@ -308,6 +335,9 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
|
||||
|
||||
@Override
|
||||
public void free() {
|
||||
if (this.terrainShader != this.translucentTerrainShader) {
|
||||
this.translucentTerrainShader.free();
|
||||
}
|
||||
this.uniform.free();
|
||||
this.distanceCountBuffer.free();
|
||||
this.terrainShader.free();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import java.util.List;
|
||||
|
||||
@Mixin(DebugHud.class)
|
||||
public class MixinDebugHud {
|
||||
@Inject(method = "getRightText", at = @At("TAIL"))
|
||||
@Inject(method = "getRightText", at = @At("RETURN"))
|
||||
private void injectDebug(CallbackInfoReturnable<List<String>> cir) {
|
||||
var ret = cir.getReturnValue();
|
||||
var instance = VoxyCommon.getInstance();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user