diff --git a/game/graphics/renderer.cpp b/game/graphics/renderer.cpp index ef92244ab..25bd4fcdc 100644 --- a/game/graphics/renderer.cpp +++ b/game/graphics/renderer.cpp @@ -180,11 +180,13 @@ void Renderer::resetSwapchain() { gi.probeDrawPso = &Shaders::inst().probeDraw; gi.uboDraw = device.descriptors(*gi.probeDrawPso); + const uint32_t maxProbes = gi.maxProbes; if(gi.hashTable.isEmpty()) { gi.hashTable = device.ssbo(nullptr, 2'097'152*sizeof(uint32_t)); // 8MB gi.voteTable = device.ssbo(nullptr, gi.hashTable.byteSize()); - gi.probes = device.ssbo(nullptr, 8*1024*1024); // ~26K - gi.freeList = device.ssbo(nullptr, gi.probes.byteSize()); // TODO: fine size + gi.probes = device.ssbo(nullptr, maxProbes*128 + 64); // probes and header + gi.freeList = device.ssbo(nullptr, maxProbes*sizeof(uint32_t) + sizeof(int32_t)); + // gi.probesGBuff = device.image3d(TextureFormat::RGBA8, maxProbes*16, 8, 2); // 16x8 tile } uint32_t zero = 0; @@ -714,7 +716,7 @@ void Renderer::prepareGi(Tempest::Encoder& cmd, uint8_t cmd.dispatchThreads(sceneDepth.size()); cmd.setUniforms(*gi.probeGCPso, gi.uboProbes); - cmd.dispatch(1024); + cmd.dispatchThreads(gi.maxProbes); cmd.setUniforms(*gi.probeAllocPso, gi.uboProbes); cmd.dispatchThreads(sceneDepth.size()); @@ -741,13 +743,13 @@ void Renderer::drawProbesDbg(Tempest::Encoder& cmd, uint gi.uboDbg = device.descriptors(pso); gi.uboDbg.set(0, wview->sceneGlobals().uboGlobal[SceneGlobals::V_Main]); gi.uboDbg.set(1, gi.probes); + gi.uboDbg.set(2, gi.hashTable); } } cmd.setDebugMarker("GI-dbg"); - const size_t cnt = (gi.probes.byteSize()-sizeof(uint32_t))/(sizeof(float)*28); cmd.setUniforms(pso, gi.uboDbg); - cmd.draw(36, 0, cnt); + cmd.draw(36, 0, gi.maxProbes); } void Renderer::drawAmbient(Encoder& cmd, const WorldView& view) { diff --git a/game/graphics/renderer.h b/game/graphics/renderer.h index dfaa4bccd..696bf4e27 100644 --- a/game/graphics/renderer.h +++ b/game/graphics/renderer.h @@ -133,6 +133,7 @@ class Renderer final { } hiz; struct { + const uint32_t maxProbes = 65535-1; Tempest::DescriptorSet uboDbg; Tempest::ComputePipeline* probeClearPso = nullptr; diff --git a/shader/lighting/rt/probe_allocation.comp b/shader/lighting/rt/probe_allocation.comp index 46e81c116..79ccd939e 100644 --- a/shader/lighting/rt/probe_allocation.comp +++ b/shader/lighting/rt/probe_allocation.comp @@ -35,10 +35,10 @@ layout(binding = 0, std140) uniform UboScene { layout(binding = 1) uniform sampler2D diffuse; layout(binding = 2) uniform sampler2D normals; layout(binding = 3) uniform sampler2D depth; -layout(binding = 4, std430) buffer Hbo0 { uint voteTable[]; }; -layout(binding = 5, std430) buffer Hbo { Hash hashTable[]; }; -layout(binding = 6, std430) buffer Pbo { ProbesHeader probeHeader; Probe probe[]; }; -layout(binding = 7, std430) buffer Fbo0 { int cursor; uint list[]; } freeList; +layout(binding = 4, std430) buffer Hbo0 { uint voteTable[]; }; +layout(binding = 5, std430) readonly buffer Hbo { Hash hashTable[]; }; +layout(binding = 6, std430) buffer Pbo { ProbesHeader probeHeader; Probe probe[]; }; +layout(binding = 7, std430) buffer Fbo0 { int cursor; uint list[]; } freeList; ivec2 screenSize, fragCoord; uint threadId; @@ -68,6 +68,7 @@ bool reuseProbe(const uint h, vec3 pos) { return false; // NOTE: reconstructed position may jitter a little probe[probeId].bits |= REUSE_BIT; + probe[probeId].pNext = 0xFFFFFFFF; return true; } @@ -93,6 +94,7 @@ void processProbe(ivec3 gridPos, vec3 pos, int lod, vec3 pixelPos, vec3 pixelNor Probe p; p.pos = pos; p.bits = 0; + p.pNext = 0xFFFFFFFF; probe[cursor] = p; #endif } @@ -111,9 +113,8 @@ void main_process() { if(z>=0.99995) return; // sky - const float bias = 2.0; const float dist = linearDepth(z, scene.clipInfo); - const vec3 pos = unprojectDepth(z) + norm*bias; + const vec3 pos = unprojectDepth(z) + norm*probeCageBias; const int lod = probeGridLodFromDist(dist); probeQuery pq; @@ -130,13 +131,13 @@ void main_process() { } void main_clear() { - const uint laneID = gl_GlobalInvocationID.x; + const uint threadID = gl_GlobalInvocationID.x; - if(laneID=probeHeader.count) return; + probe[probeId].pNext = 0xFFFFFFFF; + if((probe[probeId].bits & REUSE_BIT)!=0) return; diff --git a/shader/lighting/rt/probe_ambient.frag b/shader/lighting/rt/probe_ambient.frag index f19a920f1..838a4d735 100644 --- a/shader/lighting/rt/probe_ambient.frag +++ b/shader/lighting/rt/probe_ambient.frag @@ -6,6 +6,7 @@ #include "lighting/rt/probe_common.glsl" #include "lighting/tonemapping.glsl" +#include "lighting/purkinje_shift.glsl" #include "scene.glsl" #include "common.glsl" @@ -30,19 +31,66 @@ vec3 unprojectDepth(const float z) { return (ret.xyz/ret.w); } -void processProbe(ivec3 gridPos, vec3 pos, int lod, vec3 pixelPos, vec3 pixelNorm, bool ignoreBad) { - const uint h = probeGridPosHash(gridPos) % hashTable.length(); - const uint cursor = hashTable[h].value; - if(cursor>=probeHeader.count) +Probe mkZeroProbe() { + Probe px; + px.color[0][0] = vec4(0); + px.color[0][1] = vec4(0); + px.color[1][0] = vec4(0); + px.color[1][1] = vec4(0); + px.color[2][0] = vec4(0); + px.color[2][1] = vec4(0); + px.bits = UNUSED_BIT; + return px; + } + +Probe readProbe(const ivec3 gridPos, const vec3 wpos) { + const uint h = probeGridPosHash(gridPos) % hashTable.length(); + uint probeId = hashTable[h].value; + + [[loop]] + for(int i=0; i<8; ++i) { + if(probeId>=probeHeader.count) + return mkZeroProbe(); + + const Probe p = probe[probeId]; + if((p.bits & TRACED_BIT)==0) + return p; + + const vec3 dp = wpos - p.pos; + if(dot(dp,dp)>1.0) { + probeId = p.pNext; + continue; + } + return p; + } + + return mkZeroProbe(); + } + +void processProbe(ivec3 gridPos, vec3 wpos, int lod, vec3 pixelPos, vec3 pixelNorm, bool ignoreBad) { + const vec3 ldir = wpos-pixelPos; + if(dot(ldir, pixelNorm)<=0) return; - const Probe p = probe[cursor]; - const vec3 ldir = p.pos-pixelPos; + const Probe p = readProbe(gridPos, wpos); + if((p.bits & TRACED_BIT)==0) + return; if((p.bits & BAD_BIT)!=0 && !ignoreBad) return; - if(dot(ldir, pixelNorm)<=0) - return; + + vec3 dp = wpos - p.pos; + if(dot(dp,dp)>1.0) { + // debug + // colorSum += vec4(vec3(0,0,1)*scene.GSunIntensity,1); + // return; // position is not what was expeted - hash collision + } + + if(ivec3(p.pos/probeGridStep)!=gridPos) { + // debug + // colorSum += vec4(vec3(0,0,1)*scene.GSunIntensity,1); + // return; + } float weight = 1; // cosine weight from https://advances.realtimerendering.com/s2015/SIGGRAPH_2015_Remedy_Notes.pdf @@ -56,12 +104,12 @@ void processProbe(ivec3 gridPos, vec3 pos, int lod, vec3 pixelPos, vec3 pixelNor } void gather(vec3 pos, vec3 norm, int lod, bool ignoreBad) { + colorSum = vec4(0); + probeQuery pq; probeQueryInitialize(pq, pos, lod); while(probeQueryProceed(pq)) { - vec3 wpos = probeQueryWorldPos(pq); - vec3 dir = (wpos-pos); - + vec3 wpos = probeQueryWorldPos(pq); ivec3 gPos = probeQueryGridPos(pq); processProbe(gPos, wpos, lod, pos, norm, ignoreBad); } @@ -80,21 +128,27 @@ void main() { const vec3 diff = texelFetch(gbufDiffuse, ivec2(gl_FragCoord.xy), 0).rgb; const vec3 norm = normalize(texelFetch(gbufNormal,ivec2(gl_FragCoord.xy),0).xyz*2.0-vec3(1.0)); - const float bias = 2.0; const float dist = linearDepth(z, scene.clipInfo); - const vec3 pos = unprojectDepth(z) + norm*bias; + const vec3 pos = unprojectDepth(z) + norm*probeCageBias; const int lod = probeGridLodFromDist(dist); gather(pos, norm, lod, false); if(colorSum.w<=0.000001) { - // maybe all probes do have bad-bit, check one LOD above for data gather(pos, norm, lod, true); } + if(colorSum.w<=0.000001) { + // debug + // colorSum = vec4(vec3(1,0,0)*scene.GSunIntensity,1); + colorSum = vec4(0,0,0,1); + } + const vec3 linear = textureAlbedo(diff); vec3 color = colorSum.rgb/max(colorSum.w,0.000001); color *= linear; + // night shift + color += purkinjeShift(color); color *= scene.exposure; // color = linear; outColor = vec4(color, 1); diff --git a/shader/lighting/rt/probe_common.glsl b/shader/lighting/rt/probe_common.glsl index 7b017ac73..701ed6829 100644 --- a/shader/lighting/rt/probe_common.glsl +++ b/shader/lighting/rt/probe_common.glsl @@ -11,21 +11,31 @@ const uint REUSE_BIT = 0x4; const uint BAD_BIT = 0x8; const uint NEW_BIT = 0x10; //for debug view -struct Probe { +struct ProbesHeader { // 64 bytes + uint count; + uint iterator; + uint tracedCount; + uint padd0; + uint padd1[12]; + //uint padd2[16]; + }; + +struct Probe { // 128 bytes vec3 pos; uint bits; - vec3 color[3][2]; // HL2-cube - //uvec2 gbuffer[128]; // x: normal[xy]; w - color[rgb]; normal[z] + uint pNext; + uint padd1[3]; + vec4 color[3][2]; // HL2-cube }; -struct ProbesHeader { - uint count; - uint iterator; - uint tracedCount; +struct ProbeGBuffer { + uvec2 gbuffer[128]; // x: normal[xy]; w - color[rgb]; normal[z] }; const float dbgViewRadius = 5; const float probeGridStep = 50; +const float probeCageBias = 5.0; +const float probeBadHitT = 4.5; uint probeGridPosHash(ivec3 gridPos) { return (gridPos.x * 18397) + (gridPos.y * 20483) + (gridPos.z * 29303); diff --git a/shader/lighting/rt/probe_dbg.frag b/shader/lighting/rt/probe_dbg.frag index 08bfcefd0..2aafb1621 100644 --- a/shader/lighting/rt/probe_dbg.frag +++ b/shader/lighting/rt/probe_dbg.frag @@ -14,6 +14,7 @@ layout(binding = 1, std430) readonly buffer Pbo { ProbesHeader probeHeader; Prob layout(location = 0) in vec3 center; layout(location = 1) in flat uint instanceIndex; +layout(location = 2) in flat uint isHashed; layout(location = 0) out vec4 outColor; @@ -27,7 +28,7 @@ vec3 unprojectDepth(float z) { vec3 lodColor(const in Probe p, vec3 norm) { float lamb = max(dot(scene.sunDir, norm), 0); float light = lamb*0.7+0.3; - return p.color[0][0]*light; + return p.color[0][0].rgb*light; } void main(void) { @@ -51,6 +52,9 @@ void main(void) { if((p.bits & NEW_BIT)!=0) clr = vec3(0,1,0); + if(isHashed==0) + clr = vec3(0,0,1); + outColor = vec4(clr,1.0); // outColor = vec4(1,0,0,1.0); } diff --git a/shader/lighting/rt/probe_dbg.vert b/shader/lighting/rt/probe_dbg.vert index 9cbc7497b..3ad1d5fbb 100644 --- a/shader/lighting/rt/probe_dbg.vert +++ b/shader/lighting/rt/probe_dbg.vert @@ -10,9 +10,11 @@ layout(binding = 0, std140) uniform UboScene { SceneDesc scene; }; layout(binding = 1, std430) readonly buffer Pbo { ProbesHeader probeHeader; Probe probe[]; }; +layout(binding = 2, std430) buffer Hbo0 { Hash hashTable[]; }; layout(location = 0) out vec3 center; layout(location = 1) out flat uint instanceIndex; +layout(location = 2) out flat uint isHashed; const vec3 v[8] = { @@ -37,17 +39,23 @@ const uint index[36] = { }; void main() { - if(gl_InstanceIndex>=probeHeader.count) { + const uint probeId = gl_InstanceIndex; + if(probeId>=probeHeader.count) { gl_Position = vec4(0); return; } - Probe p = probe[gl_InstanceIndex]; + Probe p = probe[probeId]; //p.pos = vec3(0); vec3 vert = v[index[gl_VertexIndex]]; gl_Position = scene.viewProject * vec4(p.pos + vert * dbgViewRadius, 1.0); center = p.pos; - instanceIndex = gl_InstanceIndex; + instanceIndex = probeId; + + const vec3 gridPos = probe[probeId].pos/probeGridStep; + const uint h = probeGridPosHash(ivec3(gridPos)) % hashTable.length(); + const uint cursor = hashTable[h].value; + isHashed = (cursor==probeId) ? 1 : 0; } diff --git a/shader/lighting/rt/probe_trace.comp b/shader/lighting/rt/probe_trace.comp index f3d33af68..fd29dec5e 100644 --- a/shader/lighting/rt/probe_trace.comp +++ b/shader/lighting/rt/probe_trace.comp @@ -110,21 +110,22 @@ vec4 sampleScene(const vec3 rayOrigin, const vec3 rayDirection, const vec3 sunDi rayQueryProceedShadow(rayQuery); // NOTE: fix naming if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionNoneEXT) { - if(rayDirection.y<-0.9) + if(rayDirection.y<-0.75) atomicAdd(badHits, 1); // shoot to ground and hit nothing return vec4(resolveMiss(rayOrigin, rayDirection, sunDir), 1); } - const float rayT = rayQueryGetIntersectionTEXT(rayQuery, true); - const bool face = false; // (rayQueryGetIntersectionFrontFaceEXT(rayQuery, true)); //note: not working on vegetation - const bool badbit = (face || rayT<5.0); + const HitDesc hit = pullCommitedHitDesc(rayQuery); + + const float rayT = rayQueryGetIntersectionTEXT(rayQuery, true); + const bool face = false; // (rayQueryGetIntersectionFrontFaceEXT(rayQuery, true)); //note: not working on vegetation + const bool badbit = (face || rayT=probeHeader.count || pNext==probeId) + pNext = 0xFFFFFFFF; + probe[probeId].pNext = pNext; + } + void processProbe(const uint probeId) { const uint laneId = gl_LocalInvocationIndex; const bool skip = ((probe[probeId].bits & (TRACED_BIT | UNUSED_BIT))!=0); barrier(); if(skip) { + if(laneId==0 && (probe[probeId].bits & TRACED_BIT)!=0) + hashmapInsert(probeId); probe[probeId].bits &= ~(REUSE_BIT | NEW_BIT); return; } @@ -220,8 +235,8 @@ void processProbe(const uint probeId) { if(b.w>0) b.rgb /= b.w; - probe[probeId].color[laneId][0] = a.rgb * M_PI; - probe[probeId].color[laneId][1] = b.rgb * M_PI; + probe[probeId].color[laneId][0].rgb = a.rgb * M_PI; + probe[probeId].color[laneId][1].rgb = b.rgb * M_PI; } if(laneId==0) { @@ -232,11 +247,8 @@ void processProbe(const uint probeId) { } } - if(laneId==0) { - const vec3 gridPos = probe[probeId].pos/probeGridStep; - const uint h = probeGridPosHash(ivec3(gridPos)) % hashTable.length(); - hashTable[h].value = probeId; - } + if(laneId==0) + hashmapInsert(probeId); } void main() {