basic translucency

This commit is contained in:
mcrcortex
2025-04-05 16:55:47 +10:00
parent 9e5e5e654d
commit 7cc92a533d
12 changed files with 188 additions and 267 deletions

View File

@@ -0,0 +1,12 @@
package me.cortex.voxy.client;
import me.cortex.voxy.common.world.WorldEngine;
public class RenderStatistics {
public static boolean enabled = true;
public static final int[] hierarchicalTraversalCounts = 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 int renderedQuadCount = 0;
}

View File

@@ -39,6 +39,13 @@ public class AutoBindingShader extends Shader {
return GlDebug.name(name, this);
}
public AutoBindingShader ssboIf(String define, GlBuffer buffer) {
if (this.defines.containsKey(define)) {
return this.ssbo(define, buffer);
}
return this;
}
public AutoBindingShader ssbo(int index, GlBuffer binding) {
return this.ssbo(index, binding, 0);
}
@@ -52,6 +59,11 @@ public class AutoBindingShader extends Shader {
return this;
}
public AutoBindingShader ubo(String define, GlBuffer buffer) {
return this.ubo(Integer.parseInt(this.defines.get(define)), buffer);
}
public AutoBindingShader ubo(int index, GlBuffer buffer) {
return this.ubo(index, buffer, 0);
}

View File

@@ -84,6 +84,13 @@ public class Shader extends TrackedObject {
return this;
}
public Builder<T> defineIf(String name, boolean condition, int value) {
if (condition) {
this.defines.put(name, Integer.toString(value));
}
return this;
}
public Builder<T> define(String name, int value) {
this.defines.put(name, Integer.toString(value));
return this;

View File

@@ -1,6 +1,7 @@
package me.cortex.voxy.client.core.rendering;
import io.netty.util.internal.MathUtil;
import me.cortex.voxy.client.RenderStatistics;
import me.cortex.voxy.client.core.gl.Capabilities;
import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
import me.cortex.voxy.client.core.model.ModelStore;
@@ -23,6 +24,7 @@ import net.minecraft.client.render.Camera;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static org.lwjgl.opengl.GL42.*;
@@ -157,6 +159,20 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
this.renderGen.addDebugData(debug);
this.sectionRenderer.addDebug(debug);
this.nodeManager.addDebug(debug);
if (RenderStatistics.enabled) {
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(", "))+"]");
}
}
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;
}
public void shutdown() {

View File

@@ -432,12 +432,13 @@ public class RenderDataFactory45 {
this.seondaryblockMesher.doAuxiliaryFaceOffset = false;
this.blockMesher.axis = axis;
this.seondaryblockMesher.axis = axis;
for (int layer = 0; layer < 32; layer++) {//(should be 1->31, then have outer face mesher)
for (int layer = 1; layer < 31; layer++) {//(should be 1->31, then have outer face mesher)
this.blockMesher.auxiliaryPosition = layer;
this.seondaryblockMesher.auxiliaryPosition = layer;
int cSkip = 0;
for (int other = 0; other < 32; other++) {//TODO: need to do the faces that border sections
int pidx = axis == 0 ? (layer * 32 + other) : (other * 32 + layer);
int skipAmount = axis==0?32*32:32;
int msk = this.nonOpaqueMasks[pidx];
@@ -465,13 +466,45 @@ public class RenderDataFactory45 {
int idx = index + (pidx * 32);
long B = this.sectionData[idx * 2+1];
//This is just some garbage hack test thing
if (ModelQueries.isTranslucent(B)) {
if (axis != 0) {
this.blockMesher.putNext(0);
this.seondaryblockMesher.putNext(0);
continue;
}
//Example thing thats just wrong but as example
long A = this.sectionData[idx * 2];
long MSK = 0xFFFFL<<26;
long O = this.sectionData[(idx+skipAmount)*2];
if ((O&MSK) != (A&MSK)) {
this.blockMesher.putNext((long) (false ? 0L : 1L) |
A |
(((0xFFL) & 0xFF) << 55)
);
} else {
this.blockMesher.putNext(0);
}
this.seondaryblockMesher.putNext(0);
/*
O = this.sectionData[(idx-skipAmount)*2];
if ((O&MSK) != (A&MSK)) {
this.seondaryblockMesher.putNext((long) (true ? 0L : 1L) |
A |
(((0xFFL) & 0xFF) << 55)
);
}*/
continue;
} else {
//this.blockMesher.putNext(0);
//this.seondaryblockMesher.putNext(0);
//continue;
long A = this.sectionData[idx * 2];
//Example thing thats just wrong but as example
@@ -485,6 +518,7 @@ public class RenderDataFactory45 {
);
}
}
}
this.blockMesher.endRow();
this.seondaryblockMesher.endRow();
}

View File

@@ -1,7 +1,9 @@
package me.cortex.voxy.client.core.rendering.hierachical;
import me.cortex.voxy.client.RenderStatistics;
import me.cortex.voxy.client.config.VoxyConfig;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.shader.AutoBindingShader;
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.PrintfDebugUtil;
@@ -38,15 +40,15 @@ public class HierarchicalOcclusionTraverser {
private final GlBuffer nodeBuffer;
private final GlBuffer uniformBuffer = new GlBuffer(1024).zero();
private final GlBuffer renderList = new GlBuffer(100_000 * 4 + 4).zero();//100k sections max to render, TODO: Maybe move to render service or somewhere else
private final GlBuffer statisticsBuffer = new GlBuffer(1024).zero();
private final GlBuffer queueMetaBuffer = new GlBuffer(4*4*5).zero();
private final GlBuffer scratchQueueA = new GlBuffer(100_000*4).zero();
private final GlBuffer scratchQueueB = new GlBuffer(100_000*4).zero();
private static final int LOCAL_WORK_SIZE_BITS = 5;
private static final int MAX_ITERATIONS = 5;
private static final int LOCAL_WORK_SIZE_BITS = 5;
private static int BINDING_COUNTER = 1;
private static final int SCENE_UNIFORM_BINDING = BINDING_COUNTER++;
@@ -58,11 +60,12 @@ public class HierarchicalOcclusionTraverser {
private static final int NODE_QUEUE_SOURCE_BINDING = BINDING_COUNTER++;
private static final int NODE_QUEUE_SINK_BINDING = BINDING_COUNTER++;
private static final int RENDER_TRACKER_BINDING = BINDING_COUNTER++;
private static final int STATISTICS_BUFFER_BINDING = BINDING_COUNTER++;
private final HiZBuffer hiZBuffer = new HiZBuffer();
private final int hizSampler = glGenSamplers();
private final Shader traversal = Shader.make(PRINTF_processor)
private final AutoBindingShader traversal = Shader.makeAuto(PRINTF_processor)
.defineIf("DEBUG", HIERARCHICAL_SHADER_DEBUG)
.define("MAX_ITERATIONS", MAX_ITERATIONS)
.define("LOCAL_SIZE_BITS", LOCAL_WORK_SIZE_BITS)
@@ -82,6 +85,9 @@ public class HierarchicalOcclusionTraverser {
.define("RENDER_TRACKER_BINDING", RENDER_TRACKER_BINDING)
.defineIf("HAS_STATISTICS", RenderStatistics.enabled)
.defineIf("STATISTICS_BUFFER_BINDING", RenderStatistics.enabled, STATISTICS_BUFFER_BINDING)
.add(ShaderType.COMPUTE, "voxy:lod/hierarchical/traversal_dev.comp")
.compile();
@@ -99,6 +105,15 @@ public class HierarchicalOcclusionTraverser {
glSamplerParameteri(this.hizSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(this.hizSampler, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glSamplerParameteri(this.hizSampler, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
this.traversal
.ubo("SCENE_UNIFORM_BINDING", this.uniformBuffer)
.ssbo("REQUEST_QUEUE_BINDING", this.requestBuffer)
.ssbo("RENDER_QUEUE_BINDING", this.renderList)
.ssbo("NODE_DATA_BINDING", this.nodeBuffer)
.ssbo("NODE_QUEUE_META_BINDING", this.queueMetaBuffer)
.ssbo("RENDER_TRACKER_BINDING", this.nodeCleaner.visibilityBuffer)
.ssboIf("STATISTICS_BUFFER_BINDING", this.statisticsBuffer);
}
private void uploadUniform(Viewport<?> viewport) {
@@ -127,28 +142,11 @@ public class HierarchicalOcclusionTraverser {
//Screen space size for descending
MemoryUtil.memPutFloat(ptr, (float) (screenspaceAreaDecreasingSize) /(viewport.width*viewport.height)); ptr += 4;
//VisibilityId
MemoryUtil.memPutInt(ptr, this.nodeCleaner.visibilityId); ptr += 4;
/*
//Very funny and cool thing that is possible
if (MinecraftClient.getInstance().getCurrentFps() < 30) {
VoxyConfig.CONFIG.subDivisionSize = Math.min(VoxyConfig.CONFIG.subDivisionSize + 5, 256);
}
if (60 < MinecraftClient.getInstance().getCurrentFps()) {
VoxyConfig.CONFIG.subDivisionSize = Math.max(VoxyConfig.CONFIG.subDivisionSize - 1, 32);
}*/
}
private void bindings() {
glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_UNIFORM_BINDING, this.uniformBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, REQUEST_QUEUE_BINDING, this.requestBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_QUEUE_BINDING, this.renderList.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, NODE_DATA_BINDING, this.nodeBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, NODE_QUEUE_META_BINDING, this.queueMetaBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, RENDER_TRACKER_BINDING, this.nodeCleaner.visibilityBuffer.id);
glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, this.queueMetaBuffer.id);
//Bind the hiz buffer
@@ -167,12 +165,25 @@ public class HierarchicalOcclusionTraverser {
this.bindings();
PrintfDebugUtil.bind();
if (RenderStatistics.enabled) {
this.statisticsBuffer.zero();
}
this.traverseInternal(this.nodeManager.getTopLevelNodeIds().size());
this.downloadResetRequestQueue();
if (RenderStatistics.enabled) {
DownloadStream.INSTANCE.download(this.statisticsBuffer, down->{
for (int i = 0; i < 5; i++) {
RenderStatistics.hierarchicalTraversalCounts[i] = MemoryUtil.memGetInt(down.address+i*4L);
}
for (int i = 0; i < 5; i++) {
RenderStatistics.hierarchicalRenderSections[i] = MemoryUtil.memGetInt(down.address+5*4L+i*4L);
}
});
}
//Bind the hiz buffer
glBindSampler(0, 0);
@@ -299,6 +310,7 @@ public class HierarchicalOcclusionTraverser {
this.hiZBuffer.free();
this.nodeBuffer.free();
this.uniformBuffer.free();
this.statisticsBuffer.free();
this.renderList.free();
this.queueMetaBuffer.free();
this.scratchQueueA.free();

View File

@@ -33,6 +33,7 @@ import static org.lwjgl.opengl.GL45.glCopyNamedBufferSubData;
//Uses MDIC to render the sections
public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, BasicSectionGeometryManager> {
private static final int TRANSLUCENT_OFFSET = 400_000;//in draw calls
private final Shader terrainShader = Shader.make()
.defineIf("DEBUG_RENDER", false)
.add(ShaderType.VERTEX, "voxy:lod/gl46/quads2.vert")
@@ -40,6 +41,7 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
.compile();
private final Shader commandGenShader = Shader.make()
.define("TRANSLUCENT_OFFSET", TRANSLUCENT_OFFSET)
.add(ShaderType.COMPUTE, "voxy:lod/gl46/cmdgen.comp")
.compile();
@@ -56,7 +58,7 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
//TODO: needs to be in the viewport, since it contains the compute indirect call/values
private final GlBuffer drawCountCallBuffer = new GlBuffer(1024).zero();
private final GlBuffer drawCallBuffer = new GlBuffer(5*4*400000).zero();//400k draw calls
private final GlBuffer drawCallBuffer = new GlBuffer(5*4*(400_000+100_000)).zero();//400k draw calls
private final GlBuffer positionScratchBuffer = new GlBuffer(8*400000).zero();//400k positions
public MDICSectionRenderer(ModelStore modelStore, int maxSectionCount, long geometryCapacity) {
@@ -127,31 +129,13 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
this.uploadUniformBuffer(viewport);
//TODO Move this to after culling has occured instead of here, since here the geometry might have changed and
// can cause explosions, while if do after culling, its after geometry changes
// well the thing is here it technicnally should be before geometry changes anyway tho??
// so here should actually be fine aswell???
// but doing it before enables computing temporal draw commands aswell to fix temporal coherance
// yea thats true, should move it probably
{
this.commandGenShader.bind();
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.drawCallBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.drawCountCallBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, this.geometryManager.getMetadataBufferId());
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, viewport.visibilityBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, viewport.indirectLookupBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.positionScratchBuffer.id);
glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, this.drawCountCallBuffer.id);
glDispatchComputeIndirect(0);
glMemoryBarrier(GL_COMMAND_BARRIER_BIT);
}
this.renderTerrain();
}
@Override
public void buildDrawCallsAndRenderTemporal(MDICViewport viewport, GlBuffer sectionRenderList) {
if (this.geometryManager.getSectionCount() == 0) return;
this.uploadUniformBuffer(viewport);
//Can do a sneeky trick, since the sectionRenderList is a list to things to render, it invokes the culler
// which only marks visible sections
@@ -190,10 +174,43 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
glDisable(GL_DEPTH_TEST);
}
{
this.commandGenShader.bind();
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.drawCallBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.drawCountCallBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, this.geometryManager.getMetadataBufferId());
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, viewport.visibilityBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, viewport.indirectLookupBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.positionScratchBuffer.id);
glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, this.drawCountCallBuffer.id);
glDispatchComputeIndirect(0);
glMemoryBarrier(GL_COMMAND_BARRIER_BIT);
}
}
@Override
public void renderTranslucent(MDICViewport viewport) {
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();
glBindVertexArray(RenderService.STATIC_VAO);//Needs to be before binding
this.bindRenderingBuffers();
glMultiDrawElementsIndirectCountARB(GL_TRIANGLES, GL_UNSIGNED_SHORT, TRANSLUCENT_OFFSET*5*4, 4*4, Math.min((int)(this.geometryManager.getSectionCount()*4.4+128), 100_000), 0);
glEnable(GL_CULL_FACE);
glBindVertexArray(0);
glBindSampler(0, 0);
glBindTextureUnit(0, 0);
glBindSampler(1, 0);
glBindTextureUnit(1, 0);
glDisable(GL_BLEND);
}

View File

@@ -168,11 +168,8 @@ public class WorldConversionFactory {
sample >>>= eBits;
byte light = lightSupplier.supply(i&0xF, (i>>8)&0xF, (i>>4)&0xF);
if (!(bId == 0 && (light == 0))) {
data[i] = Mapper.composeMappingId(light, bId, biomes[Integer.compress(i,0b1100_1100_1100)]);
} else {
data[i] = Mapper.AIR;
}
}
} else {
if (!(blockContainer.data.storage instanceof EmptyPaletteStorage)) {

View File

@@ -60,8 +60,8 @@ void main() {
//Note! its not with respect to the sectionId
//
//Check the occlusion data from last frame
bool shouldRender = visibilityData[gl_GlobalInvocationID.x] == frameId - 1;
//Check the occlusion data from this frame occlusion
bool shouldRender = visibilityData[gl_GlobalInvocationID.x] == frameId;
//Clear the occlusion data (not strictly? needed? i think???)
//visibilityData[gl_GlobalInvocationID.x] = 0;
@@ -100,8 +100,8 @@ void main() {
//Translucency
count = meta.cntA&0xFFFF;
if (count != 0) {
//uint translucentCommandPtr = atomicAdd(translucentDrawCount, 1) + 400000;//FIXME: dont hardcode this offset
//writeCmd(translucentCommandPtr, drawId, ptr, count);
uint translucentCommandPtr = atomicAdd(translucentDrawCount, 1) + TRANSLUCENT_OFFSET;//FIXME: dont hardcode this offset
writeCmd(translucentCommandPtr, drawId, ptr, count);
}
ptr += count;

View File

@@ -1,15 +0,0 @@
#define SCENE_UNIFORM_INDEX 0
#define NODE_DATA_INDEX 1
#define NODE_QUEUE_INDEX 2
#define REQUEST_QUEUE_INDEX 3
#define RENDER_QUEUE_INDEX 4
#define TRANSFORM_ARRAY_INDEX 5
#define NEXT_NODE_QUEUE_INDEX 6
#ifdef IS_DEBUG
#define DEBUG_RENDER_NODE_INDEX 7
#endif
//Samplers
#define HIZ_BINDING_INDEX 0

View File

@@ -1,186 +0,0 @@
#version 460 core
//TODO: increase local size
#define LOCAL_SIZE_BITS 5
#define LOCAL_SIZE_MSK ((1<<LOCAL_SIZE_BITS)-1)
#define LOCAL_SIZE (1<<LOCAL_SIZE_BITS)
layout(local_size_x=LOCAL_SIZE) in;//, local_size_y=1
#import <voxy:lod/hierarchical/binding_points.glsl>
#line 7
//The queue contains 3 atomics
// end (the current processing pointer)
// head (the current point that is ok to read from)
// top (An atomic that is only used for writing to)
//The way it works when enqueuing
// top is incremented by x,
// write the data getting enqueued at the starting point specified by the `top` incrmenet
// then increment head strictly _AFTER_ writing to the queue, this ensures that the data is always written and avaible in the queue
layout(binding = SCENE_UNIFORM_INDEX, std140) uniform SceneUniform {
mat4 VP;
ivec3 camSecPos;
uint screenW;
vec3 camSubSecPos;
uint screenH;
uint requestQueueMaxSize;
uint renderQueueMaxSize;
float decendSSS;
};
layout(binding = REQUEST_QUEUE_INDEX, std430) restrict buffer RequestQueue {
uint requestQueueIndex;
uint[] requestQueue;
};
layout(binding = RENDER_QUEUE_INDEX, std430) restrict buffer RenderQueue {
uint renderQueueIndex;
uint[] renderQueue;
};
layout(binding = NODE_QUEUE_INDEX, std430) restrict buffer NodeQueue {
uint nodeQueueSize;
uint[] nodeQueue;
};
layout(binding = NEXT_NODE_QUEUE_INDEX, std430) restrict buffer NextNodeQueue {
uint nextNodeQueueIndex;
uint[] nextNodeQueue;
};
#ifdef IS_DEBUG
layout(binding = DEBUG_RENDER_NODE_INDEX, std430) restrict buffer DebugRenderNodeQueue {
uint debugRenderNodeQueueIndex;
uint[] debugRenderNodeQueue;
};
#endif
#import <voxy:lod/hierarchical/transform.glsl>
#import <voxy:lod/hierarchical/node.glsl>
//Contains all the screenspace computation
#import <voxy:lod/hierarchical/screenspace.glsl>
void addRequest(inout UnpackedNode node) {
if (!hasRequested(node)) {
//printf("Request %d %d %d %d", node.nodeId, node.flags, node.meshPtr, node.childPtr);
//TODO: maybe try using only 1 variable and it being <0 being bad
if (requestQueueIndex < requestQueueMaxSize) {
//Mark node as having a request submitted to prevent duplicate submissions
requestQueue[atomicAdd(requestQueueIndex, 1)] = getId(node);
markRequested(node);
}
}
}
void enqueueChildren(in UnpackedNode node) {
//printf("children");
uint children = getChildCount(node);
uint ptr = getChildPtr(node);
uint widx = atomicAdd(nextNodeQueueIndex, children);
for (int i = 0; i < children; i++) {
nextNodeQueue[widx+i] = ptr+i;
}
}
void enqueueSelfForRender(in UnpackedNode node) {
//printf("render %d@[%d,%d,%d]", node.lodLevel, node.pos.x, node.pos.y, node.pos.z);
if ((!isEmptyMesh(node)) && renderQueueIndex < renderQueueMaxSize) {
renderQueue[atomicAdd(renderQueueIndex, 1)] = getMesh(node);
#ifdef IS_DEBUG
debugRenderNodeQueue[atomicAdd(debugRenderNodeQueueIndex, 1)] = node.nodeId;
#endif
}
}
//TODO: need to add an empty mesh, as a parent node might not have anything to render but the children do??
void main() {
if (gl_GlobalInvocationID.x>=nodeQueueSize) {
return;
}
UnpackedNode node;
//Setup/unpack the node
unpackNode(node, nodeQueue[gl_GlobalInvocationID.x]);
//TODO: check the node is OK first??? maybe?
//Compute screenspace
setupScreenspace(node);
//printf("Node %d@[%d,%d,%d] - %d - %f", node.lodLevel, node.pos.x, node.pos.y, node.pos.z, node.flags, (size.x*size.y*screenW*screenH));
//debugDumpNode(node);
if (outsideFrustum() || isCulledByHiz()) {
//printf("HizCulled");
//We are done here, dont do any more, the issue is the shader barriers maybe
// its culled, maybe just mark it as culled?
//printf("Cull");
} else {
//It is visible, TODO: maybe do a more detailed hiz test? (or make it so that )
//Only decend if not a root node
if (node.lodLevel!=0 && shouldDecend()) {
if (hasChildren(node)) {
//printf("A");
enqueueChildren(node);
} else {
//printf("B");
addRequest(node);
//TODO: use self mesh (is error state if it doesnt have one since all leaf nodes should have a mesh)
// Basicly guarenteed to have a mesh, if it doesnt it is very very bad and incorect since its a violation of the graph properties
// that all leaf nodes must contain a mesh
enqueueSelfForRender(node);
}
} else {
if (hasMesh(node)) {
//printf("C");
enqueueSelfForRender(node);
} else {
//printf("D");
//!! not ideal, we want to render this mesh but dont have it. If we havent sent a request
// then send a request for a mesh for this node.
addRequest(node);
//TODO: Decend into children? maybe add a bitflag saying is bad if the immediate children dont have meshes
enqueueChildren(node);
}
}
}
}
/*
Persistent threading
//Thread 0 grabs a batch when empty
void main() {
while (true) {
//Each thread processes an entry on the queue and pushes all children to the queue if it is determined the children need to be added
}
}
*/
//If a request is successfully added to the RequestQueue, must update NodeData to mark that the node has been put into the request queue
// to prevent it from being requested every frame and blocking the queue
//Once a suitable render section is found, it is put into the RenderQueue, or if its not availbe its put into the RequestQueue
// and its children are rendered instead if it has them avalible
//NOTE: EXPERIMENT: INSTEAD OF PERSISTENT THREADS
//TODO: since we know the tree depth is worst case 5, we can just do an indirect dispatch 5 times one for each layer
// issues with this approach, barriers and waiting for one to finish before the otehr can be executed
// advantages, MUCH SIMPLER, no shader barriers needed really , issue is need a flipflip queue but thats ok,
// also ensures the gpu is full of work capacity
// this might be what i do to start with since its much easier to do
// not sure

View File

@@ -36,6 +36,13 @@ layout(binding = RENDER_TRACKER_BINDING, std430) restrict writeonly buffer rende
uint[] lastRenderFrame;
};
#ifdef HAS_STATISTICS
layout(binding = STATISTICS_BUFFER_BINDING, std430) restrict buffer statisticsBuffer {
uint traversalCounts[5];
uint renderCounts[5];
};
#endif
void addRequest(inout UnpackedNode node) {
//printf("Put node decend request");
if (!hasRequested(node)) {
@@ -72,12 +79,20 @@ void enqueueSelfForRender(in UnpackedNode node) {
#ifdef IS_DEBUG
debugRenderNodeQueue[atomicAdd(debugRenderNodeQueueIndex, 1)] = node.nodeId;
#endif
#ifdef HAS_STATISTICS
atomicAdd(renderCounts[node.lodLevel], 1);
#endif
}
}
}
void traverse(in UnpackedNode node) {
#ifdef HAS_STATISTICS
atomicAdd(traversalCounts[node.lodLevel], 1);
#endif
//Compute screenspace
setupScreenspace(node);
//debugDumpNode(node);