From a43b1567b390e67a6f731762f03000aa33b0e642 Mon Sep 17 00:00:00 2001 From: ecker Date: Sat, 15 Aug 2026 23:36:26 -0500 Subject: [PATCH] rabbithole of swapping VXGI to SVOGI (currently enabled arbitrary regions to voxelize into (mostly working)), repaired shadowmaps (kind of flickers from depth precision when moving), to-do: fix annoying race issue when GPU is under load that is not fixed with N-buffered attachments, but is fixed with vkDeviceWaitIdle (although this is a horribad bandaid fix) --- bin/data/config.json | 16 +- bin/data/shaders/base/graph/frag.glsl | 2 +- bin/data/shaders/common/functions.h | 6 - bin/data/shaders/common/structs.h | 14 +- bin/data/shaders/common/vxgi.h | 144 +++++------- bin/data/shaders/display/deferred/comp/comp.h | 12 +- bin/data/shaders/display/vxgi/comp.glsl | 30 +-- bin/data/shaders/display/vxgi/mips.comp.glsl | 6 +- bin/data/shaders/graph/voxelize/frag.glsl | 70 +++--- bin/data/shaders/graph/voxelize/geom.glsl | 117 +++++----- engine/inc/uf/engine/graph/graph.h | 2 + engine/inc/uf/engine/graph/pod.inl | 7 + .../inc/uf/ext/vulkan/rendermodes/deferred.h | 1 - engine/inc/uf/utils/string/rle.h | 4 +- engine/inc/uf/utils/string/rle.inl | 28 ++- engine/src/engine/ext/raytrace/behavior.cpp | 24 +- engine/src/engine/ext/scene/behavior.cpp | 17 +- engine/src/engine/ext/voxelizer/behavior.cpp | 209 ++++++++++++------ engine/src/engine/ext/voxelizer/behavior.h | 2 +- engine/src/engine/graph/graph.cpp | 132 +++++++++-- engine/src/ext/vulkan/rendermode.cpp | 13 +- engine/src/ext/vulkan/rendermodes/base.cpp | 34 ++- .../src/ext/vulkan/rendermodes/deferred.cpp | 46 ++-- .../ext/vulkan/rendermodes/rendertarget.cpp | 23 +- .../src/ext/vulkan/rendermodes/transition.inl | 12 +- engine/src/ext/vulkan/shader.cpp | 2 +- engine/src/utils/debug/draw.cpp | 4 +- 27 files changed, 582 insertions(+), 395 deletions(-) diff --git a/bin/data/config.json b/bin/data/config.json index 0f6108a0..05b2be85 100644 --- a/bin/data/config.json +++ b/bin/data/config.json @@ -3,10 +3,10 @@ "scenes": { "start": "StartMenu", "lights": { "enabled": true, - "lightmaps": true, + "lightmaps": false, "max": 32, "shadows": { - "enabled": false, + "enabled": true, "update": 4, "max": 16, "samples": 1 @@ -27,17 +27,15 @@ } }, "vxgi": { - "limiter": 0, - // "limiter": 0.0125, - // "limiter": 5, + // "limiter": 0, + "limiter": 0.0125, + // "limiter": 1, "size": 256, "dispatch": 16, - "cascades": 3, - "cascadePower": 1.5, + "regions": 3, "granularity": 32, - "voxelizeScale": 1, "occlusionFalloff": 2, - "traceStartOffsetFactor": 1, + "traceStartOffsetFactor": 0.1, "filtering": "LINEAR", "shadows": 0, "extents": { diff --git a/bin/data/shaders/base/graph/frag.glsl b/bin/data/shaders/base/graph/frag.glsl index c9ce3350..45b367d9 100644 --- a/bin/data/shaders/base/graph/frag.glsl +++ b/bin/data/shaders/base/graph/frag.glsl @@ -15,7 +15,7 @@ layout (binding = 6, set = 1) uniform sampler2D samplerTextures[TEXTURES]; layout (binding = 7, set = 1) uniform samplerCube samplerCubemaps[CUBEMAPS]; layout (binding = 8, set = 0) uniform sampler3D samplerNoise; #if VXGI - layout (binding = 9, set = 0) uniform sampler3D voxelOutput[CASCADES]; + layout (binding = 9, set = 0) uniform sampler3D voxelOutput[REGIONS]; #endif #if RT layout (binding = 10, set = 0) uniform accelerationStructureEXT tlas; diff --git a/bin/data/shaders/common/functions.h b/bin/data/shaders/common/functions.h index 00f75ddc..547b9a8c 100644 --- a/bin/data/shaders/common/functions.h +++ b/bin/data/shaders/common/functions.h @@ -231,12 +231,6 @@ vec2 rayBoxDst( vec3 boundsMin, vec3 boundsMax, in Ray ray ) { const float tEnd = max(0, min( tmax.x, min(tmax.y, tmax.z) ) - tStart); return vec2(tStart, tEnd); } -#if VXGI -float cascadePower( uint x ) { - return pow(1 + x, ubo.settings.vxgi.cascadePower); -// return max( 1, x * ubo.settings.vxgi.cascadePower ); -} -#endif #if FRAGMENT void whitenoise(inout vec3 color, const vec4 parameters) { const float flicker = parameters.x; diff --git a/bin/data/shaders/common/structs.h b/bin/data/shaders/common/structs.h index a93f0566..7c897a73 100644 --- a/bin/data/shaders/common/structs.h +++ b/bin/data/shaders/common/structs.h @@ -250,17 +250,10 @@ struct SettingsBloom { uint padding; }; struct SettingsVxgi { - mat4 matrix; - - float cascadePower; float granularity; - float voxelizeScale; float occlusionFalloff; - float traceStartOffsetFactor; uint shadows; - uint padding2; - uint padding3; }; struct SettingsRayTrace { vec2 defaultRayBounds; @@ -326,4 +319,11 @@ struct RayTracePayload { uint primitiveID; vec2 attributes; // Triangle triangle; +}; + +struct Region { + vec3 minBounds; + uint index; + vec3 maxBounds; + uint size; }; \ No newline at end of file diff --git a/bin/data/shaders/common/vxgi.h b/bin/data/shaders/common/vxgi.h index 09e6c7a3..50310db2 100644 --- a/bin/data/shaders/common/vxgi.h +++ b/bin/data/shaders/common/vxgi.h @@ -1,100 +1,87 @@ -// GI -float cascadeScales[CASCADES]; -float cascadeScalesInv[CASCADES]; -void precomputeCascades() { - for ( int i = 0; i < CASCADES; ++i ) { - cascadeScales[i] = cascadePower(i); - cascadeScalesInv[i] = 1.0 / cascadeScales[i]; +uint findRegion(vec3 pos) { + for ( uint i = 0; i < regions.length(); ++i ) { + Region r = regions[i]; + if ( all(greaterThanEqual(pos, r.minBounds)) && all(lessThanEqual(pos, r.maxBounds)) ) return i; } + return regions.length(); } + vec4 voxelTrace( inout Ray ray, float aperture, float maxDistance ) { + ray.direction.x = abs(ray.direction.x) < 0.00001 ? 0.00001 : ray.direction.x; + ray.direction.y = abs(ray.direction.y) < 0.00001 ? 0.00001 : ray.direction.y; + ray.direction.z = abs(ray.direction.z) < 0.00001 ? 0.00001 : ray.direction.z; + ray.origin += ray.direction * voxelInfo.radianceSizeRecip * 1.5; -#if VXGI_NDC - ray.origin = vec3( ubo.settings.vxgi.matrix * vec4( ray.origin, 1.0 ) ); - ray.direction = vec3( ubo.settings.vxgi.matrix * vec4( ray.direction, 0.0 ) ); - float absMax = max3(abs(ray.origin)); -#else - const float voxelOrigin = vec3( ubo.settings.vxgi.matrix * vec4( ray.origin, 1.0 ) ); - float absMax = max3(abs(voxelOrigin)); -#endif + uint regionIdx = findRegion(ray.origin); + if (regionIdx == regions.length()) return vec4(0); - uint cascade = 0; - for ( uint c = 0; c < CASCADES - 1; ++c ) { - if ( absMax * cascadeScalesInv[c] < (1.0 - voxelInfo.radianceSizeRecip)) { - cascade = c; - break; - } - cascade = CASCADES - 1; - } - - const float maxCascadeScale = cascadeScales[CASCADES-1]; - float currentCascadeScale = cascadeScales[cascade]; - float currentCascadeScaleInv = cascadeScalesInv[cascade]; + Region region = regions[regionIdx]; const float granularity = ubo.settings.vxgi.granularity; const float occlusionFalloff = ubo.settings.vxgi.occlusionFalloff; const float coneCoefficient = 2.0 * tan(aperture * 0.5); - - const uint maxSteps = uint(voxelInfo.radianceSize * maxCascadeScale * granularity); + + const uint maxSteps = uint(region.size * granularity); const float maxRadiance = 0.90; - // box - const vec2 rayBoxInfoA = rayBoxDst( voxelInfo.min * currentCascadeScale, voxelInfo.max * currentCascadeScale, ray ); - const vec2 rayBoxInfoB = rayBoxDst( voxelInfo.min * maxCascadeScale, voxelInfo.max * maxCascadeScale, ray ); - const float tStart = rayBoxInfoA.x; - const float tEnd = maxDistance > 0 ? min(maxDistance, rayBoxInfoB.y) : rayBoxInfoB.y; - const float tDelta = voxelInfo.radianceSizeRecip * granularity; + const vec2 rayBoxInfo = rayBoxDst( region.minBounds, region.maxBounds, ray ); + const float tStart = rayBoxInfo.x; + const float tEnd = maxDistance > 0 ? min(maxDistance, rayBoxInfo.y) : rayBoxInfo.y; + + float voxelWorldSize = (region.maxBounds.x - region.minBounds.x) / float(region.size); + float tDelta = voxelWorldSize * granularity; - // marcher ray.distance = tStart + tDelta * ubo.settings.vxgi.traceStartOffsetFactor; - ray.position = vec3(0); + ray.distance += tDelta * rand2(gl_GlobalInvocationID.xy); - vec4 radiance = vec4(0); - vec3 uvw = vec3(0); - float coneDiameter = coneCoefficient * ray.distance; - float level = aperture > 0 ? log2( coneDiameter ) : 0; vec4 color = vec4(0); float occlusion = 0; uint stepCounter = 0; - - ray.distance += tDelta * rand2(gl_GlobalInvocationID.xy); while ( color.a < maxRadiance && occlusion < 1.0 && ray.distance < tEnd && stepCounter++ < maxSteps ) { - float stepScale = max(1.0, (coneDiameter * currentCascadeScale) * 1.5); + float coneDiameter = coneCoefficient * ray.distance; + float stepScale = max(1.0, (coneDiameter * float(region.size)) * 1.5); + ray.distance += tDelta * stepScale; ray.position = ray.origin + ray.direction * ray.distance; - absMax = max3(abs(ray.position)); - if ( absMax * currentCascadeScaleInv > 0.99 ) { - if ( ++cascade >= CASCADES ) break; + if (any(lessThan(ray.position, region.minBounds)) || any(greaterThan(ray.position, region.maxBounds))) { + regionIdx = findRegion(ray.position); + if (regionIdx == regions.length()) break; - currentCascadeScale = cascadeScales[cascade]; - currentCascadeScaleInv = cascadeScalesInv[cascade]; + region = regions[regionIdx]; - ray.distance += tDelta * currentCascadeScale; - continue; + voxelWorldSize = (region.maxBounds.x - region.minBounds.x) / float(region.size); + tDelta = voxelWorldSize * granularity; } - uvw = ray.position * currentCascadeScaleInv * 0.5 + 0.5; - coneDiameter = coneCoefficient * ray.distance; - level = aperture > 0 ? log2( coneDiameter ) : 0; + vec3 uvw = (ray.position - region.minBounds) / (region.maxBounds - region.minBounds); + + float level = aperture > 0 ? log2( coneDiameter * float(region.size) ) : 0; + vec4 radiance = textureLod(voxelOutput[nonuniformEXT(regionIdx)], uvw, level); - radiance = textureLod(voxelOutput[nonuniformEXT(cascade)], uvw.xzy, level); - color.rgb += (1.0 - color.a) * radiance.rgb * radiance.a; color.a += (1.0 - color.a) * radiance.a; occlusion += ((1.0f - occlusion) * radiance.a) / (1.0f + occlusionFalloff * coneDiameter); } - return maxDistance > 0 ? color : vec4(color.rgb, occlusion); + + vec4 finalColor = maxDistance > 0 ? color : vec4(color.rgb, occlusion); + +// if (any(isnan(finalColor)) || any(isinf(finalColor))) return vec4(0.0); + + return finalColor; } + vec4 voxelConeTrace( inout Ray ray, float aperture ) { - return voxelTrace( ray, aperture, 0 ); + return voxelTrace( ray, aperture, 4096.0 ); } + vec4 voxelTrace( inout Ray ray, float maxDistance ) { - return voxelTrace( ray, 0, maxDistance ); + return voxelTrace( ray, 0.0, maxDistance ); } + uint voxelShadowsCount = 0; float shadowFactorVXGI( const Light light, float def ) { if ( ubo.settings.vxgi.shadows < ++voxelShadowsCount ) return 1.0; @@ -109,20 +96,12 @@ float shadowFactorVXGI( const Light light, float def ) { return 1.0 - voxelTrace( ray, SHADOW_APERTURE, z ).a; } void indirectLightingVXGI() { - precomputeCascades(); - voxelInfo.radianceSize = textureSize( voxelOutput[0], 0 ).x; voxelInfo.radianceSizeRecip = 1.0 / voxelInfo.radianceSize; voxelInfo.mipmapLevels = log2(voxelInfo.radianceSize) + 1; -#if VXGI_NDC - voxelInfo.min = vec3( -1 ); - voxelInfo.max = vec3( 1 ); -#else - const mat4 inverseOrtho = inverse( ubo.settings.vxgi.matrix ); - voxelInfo.min = vec3( inverseOrtho * vec4( -1, -1, -1, 1 ) ); - voxelInfo.max = vec3( inverseOrtho * vec4( 1, 1, 1, 1 ) ); -#endif + voxelInfo.min = vec3( -1.0 ); + voxelInfo.max = vec3( 1.0 ); vec4 indirectDiffuse = vec4(0); vec4 indirectSpecular = vec4(0); @@ -135,12 +114,12 @@ void indirectLightingVXGI() { #if 0 { - Ray ray; - ray.direction = N; - ray.origin = P + N * (voxelInfo.radianceSizeRecip * 4.0); + Ray ray; + ray.direction = N; + ray.origin = P + N * (voxelInfo.radianceSizeRecip * 4.0); - indirectDiffuse = voxelConeTrace(ray, 1.0f); - surface.material.occlusion += 1.0 - clamp(indirectDiffuse.a, 0.0, 1.0); + indirectDiffuse = voxelConeTrace(ray, 1.0f); + surface.material.occlusion += 1.0 - clamp(indirectDiffuse.a, 0.0, 1.0); } #else const uint CONES_COUNT = 4; @@ -159,7 +138,7 @@ void indirectLightingVXGI() { for ( uint i = 0; i < CONES_COUNT; ++i ) { Ray ray; ray.direction = CONES[i].xyz; - ray.origin = P; // + ray.direction; + ray.origin = P; indirectDiffuse += voxelConeTrace( ray, DIFFUSE_CONE_APERTURE ) * weight; weight = PI * 0.15f; } @@ -168,22 +147,21 @@ void indirectLightingVXGI() { indirectDiffuse *= DIFFUSE_INDIRECT_FACTOR; #endif - const float SPECULAR_CONE_APERTURE = clamp(tan(PI * 0.5f * surface.material.roughness), 0.0174533f, PI); // tan( R * PI * 0.5f * 0.1f ); - const float SPECULAR_INDIRECT_FACTOR = (1.0f - surface.material.metallic) * (1.0f - surface.material.roughness); // * 0.25; // 1.0f; + const float SPECULAR_CONE_APERTURE = clamp(tan(PI * 0.5f * surface.material.roughness), 0.0174533f, PI); + const float SPECULAR_INDIRECT_FACTOR = (1.0f - surface.material.metallic) * (1.0f - surface.material.roughness); if ( SPECULAR_INDIRECT_FACTOR > 0.0f ) { const vec3 R = reflect( normalize(P - surface.ray.origin), N ); Ray ray; ray.direction = R; - ray.origin = P; // + ray.direction; + ray.origin = P; indirectSpecular = voxelConeTrace( ray, SPECULAR_CONE_APERTURE ); } indirectSpecular *= SPECULAR_INDIRECT_FACTOR; - // Calculate Fresnel { const vec3 V = normalize(surface.ray.origin - surface.position.world); - const vec3 N = surface.normal.world; - const float NdotV = max(dot(N, V), 0.0); + const vec3 N_dir = surface.normal.world; + const float NdotV = max(dot(N_dir, V), 0.0); const vec3 F0 = mix(vec3(0.04), surface.material.albedo.rgb, surface.material.metallic); const vec3 F = fresnelSchlick(F0, NdotV); @@ -192,9 +170,7 @@ void indirectLightingVXGI() { } surface.material.indirect += indirectDiffuse + indirectSpecular; - - // deferred sampling doesn't have a blended albedo buffer - // in place we'll just cone trace behind the window + #if !RT if ( 0.1 < surface.material.albedo.a && surface.material.albedo.a < 1.0 ) { Ray ray; diff --git a/bin/data/shaders/display/deferred/comp/comp.h b/bin/data/shaders/display/deferred/comp/comp.h index 3b8c8d37..a6637f25 100644 --- a/bin/data/shaders/display/deferred/comp/comp.h +++ b/bin/data/shaders/display/deferred/comp/comp.h @@ -22,7 +22,7 @@ layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in; layout (constant_id = 0) const uint TEXTURES = 512; layout (constant_id = 1) const uint CUBEMAPS = 128; #if VXGI - layout (constant_id = 2) const uint CASCADES = 16; + layout (constant_id = 2) const uint REGIONS = 16; #endif #if !MULTISAMPLING @@ -99,10 +99,14 @@ layout (binding = 20, set = 1) uniform sampler2D samplerTextures[TEXTURES]; layout (binding = 21, set = 1) uniform samplerCube samplerCubemaps[CUBEMAPS]; layout (binding = 22, set = 0) uniform sampler3D samplerNoise; #if VXGI - layout (binding = 23, set = 0) uniform sampler3D voxelOutput[CASCADES]; +// layout (binding = 23, set = 0) uniform sampler3D voxelOutput[REGIONS]; + layout (binding = 23, set = 0) uniform sampler3D voxelOutput[REGIONS]; + layout (std140, binding = 24, set = 0) readonly buffer Regions { + Region regions[]; + }; #endif #if RT - layout (binding = 24, set = 0) uniform accelerationStructureEXT tlas; + layout (binding = 25, set = 0) uniform accelerationStructureEXT tlas; #endif #if BUFFER_REFERENCE @@ -326,7 +330,7 @@ void directLighting() { surface.material.indirect.rgb = vec3(0); surface.fragment.rgb = radiance.rgb; surface.fragment.a = 1; // radiance.a; - //return; + return; } #endif diff --git a/bin/data/shaders/display/vxgi/comp.glsl b/bin/data/shaders/display/vxgi/comp.glsl index 0fa4ee09..5f841747 100644 --- a/bin/data/shaders/display/vxgi/comp.glsl +++ b/bin/data/shaders/display/vxgi/comp.glsl @@ -17,7 +17,7 @@ layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in; layout (constant_id = 0) const uint TEXTURES = 512; layout (constant_id = 1) const uint CUBEMAPS = 128; -layout (constant_id = 2) const uint CASCADES = 16; +layout (constant_id = 2) const uint REGIONS = 16; #include "../../common/macros.h" #include "../../common/structs.h" @@ -52,10 +52,10 @@ layout (std140, binding = 7) readonly buffer Lights { layout (binding = 8) uniform sampler2D samplerTextures[TEXTURES]; layout (binding = 9) uniform samplerCube samplerCubemaps[CUBEMAPS]; -layout (binding = 10, r32ui) uniform readonly uimage3D voxelId[CASCADES]; -layout (binding = 11, r32ui) uniform readonly uimage3D voxelNormal[CASCADES]; -layout (binding = 12, r32ui) uniform readonly uimage3D voxelRadiance[CASCADES]; -layout (binding = 13, rgba8) uniform writeonly image3D voxelOutput[CASCADES]; +layout (binding = 10, r32ui) uniform readonly uimage3D voxelId[REGIONS]; +layout (binding = 11, r32ui) uniform readonly uimage3D voxelNormal[REGIONS]; +layout (binding = 12, r32ui) uniform readonly uimage3D voxelRadiance[REGIONS]; +layout (binding = 13, rgba8) uniform writeonly image3D voxelOutput[REGIONS]; #include "../../common/functions.h" #include "../../common/light.h" @@ -63,12 +63,12 @@ layout (binding = 13, rgba8) uniform writeonly image3D voxelOutput[CASCADES]; #include "../../common/shadows.h" void main() { - const vec3 tUvw = gl_GlobalInvocationID.xzy; - for ( uint CASCADE = 0; CASCADE < CASCADES; ++CASCADE ) { + const vec3 tUvw = gl_GlobalInvocationID.xyz; + for ( uint REGION = 0; REGION < REGIONS; ++REGION ) { #if 0 - vec4 A = unpackUnorm4x8(imageLoad(voxelRadiance[CASCADE], ivec3(tUvw)).r); + vec4 A = unpackUnorm4x8(imageLoad(voxelRadiance[REGION], ivec3(tUvw)).r); A.a = length(luma(A.rgb)) > 0.001 ? 1 : 0; - imageStore(voxelOutput[CASCADE], ivec3(tUvw), A); + imageStore(voxelOutput[REGION], ivec3(tUvw), A); #else surface.pass = 0; // PushConstant.pass; surface.fragment = vec4(0); @@ -76,7 +76,7 @@ void main() { surface.motion = vec2(0); surface.material.indirect = vec4(0); - const uint packedID = imageLoad(voxelId[CASCADE], ivec3(tUvw) ).x; + const uint packedID = imageLoad(voxelId[REGION], ivec3(tUvw) ).x; const uvec2 ID = uvec2( (packedID & 0xFFFF), (packedID >> 16) @@ -89,7 +89,7 @@ void main() { // if ( ID.x == 0 || ID.y == 0 ) { #if 1 if ( DISCARD_DUE_TO_DIVERGENCE ) { - imageStore(voxelOutput[CASCADE], ivec3(tUvw), vec4(0)); + imageStore(voxelOutput[REGION], ivec3(tUvw), vec4(0)); continue; } #endif @@ -102,9 +102,9 @@ void main() { surface.fragment = material.colorEmissive; #if 0 - vec4 A = imageLoad(voxelOutput[CASCADE], ivec3(tUvw) ); + vec4 A = imageLoad(voxelOutput[REGION], ivec3(tUvw) ); #else - vec4 A = unpackUnorm4x8(imageLoad(voxelRadiance[CASCADE], ivec3(tUvw)).r); + vec4 A = unpackUnorm4x8(imageLoad(voxelRadiance[REGION], ivec3(tUvw)).r); A.a = float(uint(A.a * 255.0 + 0.5) & 0xF) / 15.0; #endif @@ -134,9 +134,9 @@ void main() { #endif if ( DISCARD_DUE_TO_DIVERGENCE ) { - imageStore(voxelOutput[CASCADE], ivec3(tUvw), vec4(0)); + imageStore(voxelOutput[REGION], ivec3(tUvw), vec4(0)); } else { - imageStore(voxelOutput[CASCADE], ivec3(tUvw), vec4(surface.fragment.rgb, surface.material.albedo.a)); + imageStore(voxelOutput[REGION], ivec3(tUvw), vec4(surface.fragment.rgb, surface.material.albedo.a)); } #endif } diff --git a/bin/data/shaders/display/vxgi/mips.comp.glsl b/bin/data/shaders/display/vxgi/mips.comp.glsl index d4b00c93..14b44f2c 100644 --- a/bin/data/shaders/display/vxgi/mips.comp.glsl +++ b/bin/data/shaders/display/vxgi/mips.comp.glsl @@ -14,7 +14,7 @@ layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in; -layout (constant_id = 0) const uint CASCADES = 8; +layout (constant_id = 0) const uint REGIONS = 8; layout (constant_id = 1) const uint MIPS = 9; // 256^3 texture = 9 mips layout(push_constant) uniform PushBlock { @@ -24,8 +24,8 @@ layout(push_constant) uniform PushBlock { uint workGroupOffset; } PushConstant_; -layout (binding = 0) uniform sampler3D voxelRadiance[CASCADES]; -layout (binding = 1, rgba8) coherent uniform image3D voxelMips[CASCADES * (MIPS - 1)]; +layout (binding = 0) uniform sampler3D voxelRadiance[REGIONS]; +layout (binding = 1, rgba8) coherent uniform image3D voxelMips[REGIONS * (MIPS - 1)]; layout (binding = 2, std430) buffer AtomicCounter { uint counter; diff --git a/bin/data/shaders/graph/voxelize/frag.glsl b/bin/data/shaders/graph/voxelize/frag.glsl index 859c4fd3..346ca3ab 100644 --- a/bin/data/shaders/graph/voxelize/frag.glsl +++ b/bin/data/shaders/graph/voxelize/frag.glsl @@ -1,7 +1,7 @@ #version 450 #pragma shader_stage(fragment) -// to-do: convert to use functions.h surface population functions +#extension GL_EXT_nonuniform_qualifier : require #define FRAGMENT 1 #define DEFERRED_SAMPLING 0 @@ -10,37 +10,39 @@ #define BLEND 0 #define USE_LIGHTMAP 1 layout (constant_id = 0) const uint TEXTURES = 512; -layout (constant_id = 1) const uint CASCADES = 16; +layout (constant_id = 1) const uint REGIONS = 16; #define MAX_TEXTURES textures.length() #include "../../common/macros.h" #include "../../common/structs.h" -layout (binding = 6) uniform sampler2D samplerTextures[TEXTURES]; -layout (std140, binding = 7) readonly buffer DrawCommands { +layout (binding = 7) uniform sampler2D samplerTextures[TEXTURES]; +layout (std140, binding = 8) readonly buffer DrawCommands { DrawCommand drawCommands[]; }; -layout (std140, binding = 8) readonly buffer Instances { +layout (std140, binding = 9) readonly buffer Instances { Instance instances[]; }; -layout (std140, binding = 9) readonly buffer InstanceAddresseses { +layout (std140, binding = 10) readonly buffer InstanceAddresseses { InstanceAddresses addresses[]; }; - -layout (std140, binding = 10) readonly buffer Materials { +layout (std140, binding = 11) readonly buffer Materials { Material materials[]; }; -layout (std140, binding = 11) readonly buffer Textures { +layout (std140, binding = 12) readonly buffer Textures { Texture textures[]; }; -layout (std140, binding = 12) readonly buffer Lights { +layout (std140, binding = 13) readonly buffer Lights { Light lights[]; }; +layout (std140, binding = 14) readonly buffer RegionsBuffer { + Region regions[]; +}; -layout (binding = 13, r32ui) uniform volatile uimage3D voxelId[CASCADES]; -layout (binding = 14, r32ui) uniform volatile uimage3D voxelNormal[CASCADES]; -layout (binding = 15, r32ui) uniform volatile uimage3D voxelRadiance[CASCADES]; -layout (binding = 16, rgba8) uniform writeonly image3D voxelOutput[CASCADES]; +layout (binding = 15, r32ui) uniform volatile uimage3D voxelId[REGIONS]; +layout (binding = 16, r32ui) uniform volatile uimage3D voxelNormal[REGIONS]; +layout (binding = 17, r32ui) uniform volatile uimage3D voxelRadiance[REGIONS]; +layout (binding = 18, rgba8) uniform writeonly image3D voxelOutput[REGIONS]; layout (location = 0) flat in uvec4 inId; layout (location = 1) flat in vec4 inPOS0; @@ -55,12 +57,16 @@ layout (location = 8) in vec3 inTangent; #include "../../common/functions.h" void main() { - const uint CASCADE = inId.w; - if ( CASCADES <= CASCADE ) discard; - const vec3 P = inPosition.xzy * 0.5 + 0.5; - if ( abs(P.x) > 1 || abs(P.y) > 1 || abs(P.z) > 1 ) discard; + const uint REGION_INDEX = inId.w; + if ( REGIONS <= REGION_INDEX ) discard; - const uint triangleID = uint(inId.x); // gl_PrimitiveID + Region region = regions[REGION_INDEX]; + + const vec3 P = (inPosition.xyz - region.minBounds) / (region.maxBounds - region.minBounds); + const float epsilon = 0.001; + if ( any(lessThan(P, vec3(-epsilon))) || any(greaterThan(P, vec3(1.0 + epsilon))) ) discard; + + const uint triangleID = uint(inId.x); const uint drawID = uint(inId.y); const uint instanceID = uint(inId.z); @@ -75,22 +81,12 @@ void main() { surface.instance = instance; vec4 A = material.colorBase; - float M = material.factorMetallic; - float R = material.factorRoughness; - float AO = material.factorOcclusion; - - // sample albedo + if ( validTextureIndex( material.indexAlbedo ) ) { A = sampleTexture( material.indexAlbedo ); } - // alpha mode OPAQUE if ( material.modeAlpha == 0 ) { A.a = 1; - // alpha mode BLEND - } else if ( material.modeAlpha == 1 ) { - float dither = interleavedGradientNoise(gl_FragCoord.xy); - // if ( A.a < dither ) discard; - // alpha mode MASK } else if ( material.modeAlpha == 2 ) { if ( A.a < abs(material.factorAlphaCutoff) ) discard; A.a = 1; @@ -103,33 +99,31 @@ void main() { } #endif - // sample normal vec3 N = inNormal; vec3 T = inTangent; T = normalize(T - dot(T, N) * N); vec3 B = cross(T, N); mat3 TBN = mat3(T, B, N); -// mat3 TBN = mat3(N, B, T); if ( T != vec3(0) && validTextureIndex( material.indexNormal ) ) { N = TBN * normalize( sampleTexture( material.indexNormal ).xyz * 2.0 - 1.0 ); } - const ivec3 uvw = ivec3(P * imageSize(voxelOutput[CASCADE])); + const ivec3 uvw = ivec3(P * imageSize(voxelOutput[nonuniformEXT(REGION_INDEX)])); { uint packedId = ( instanceID + 1 ) << 16 | ( drawID + 1 ); - imageAtomicMax(voxelId[CASCADE], ivec3(uvw), packedId); + imageAtomicMax(voxelId[nonuniformEXT(REGION_INDEX)], uvw, packedId); } { vec2 N_E = encodeNormals( normalize( N ) ); uint packedNormal = packHalf2x16(N_E); - imageAtomicMax(voxelNormal[CASCADE], uvw, packedNormal); + imageAtomicMax(voxelNormal[nonuniformEXT(REGION_INDEX)], uvw, packedNormal); } { uint l = uint(clamp(luma(A.rgb), 0.0, 1.0) * 15.0) & 0xF; - uint a = uint(clamp( A.a, 0.0, 1.0) * 15.0) & 0xF; + uint a = uint(clamp( A.a, 0.0, 1.0) * 15.0) & 0xF; float packedLumaAlpha = float((l << 4) | a) / 255.0; uint packedRadiance = packUnorm4x8(vec4(A.rgb, packedLumaAlpha)); - imageAtomicMax(voxelRadiance[CASCADE], uvw, packedRadiance); + imageAtomicMax(voxelRadiance[nonuniformEXT(REGION_INDEX)], uvw, packedRadiance); } -} \ No newline at end of file +} diff --git a/bin/data/shaders/graph/voxelize/geom.glsl b/bin/data/shaders/graph/voxelize/geom.glsl index 83781ab6..3810562c 100644 --- a/bin/data/shaders/graph/voxelize/geom.glsl +++ b/bin/data/shaders/graph/voxelize/geom.glsl @@ -1,6 +1,6 @@ #version 450 #pragma shader_stage(geometry) -#extension GL_EXT_multiview : require +#extension GL_EXT_multiview : require layout(triangles) in; layout(triangle_strip, max_vertices = 3) out; @@ -25,72 +25,87 @@ layout (location = 6) out vec2 outSt; layout (location = 7) out vec3 outNormal; layout (location = 8) out vec3 outTangent; +#include "../../common/macros.h" +#include "../../common/structs.h" + layout (binding = 5) uniform UBO { - mat4 voxel; - - float cascadePower; float granularity; - float voxelizeScale; float occlusionFalloff; - uint shadows; uint padding1; - uint padding2; - uint padding3; } ubo; -float cascadePower( uint x ) { - return pow(1 + x, ubo.cascadePower); -// return max( 1, x * ubo.cascadePower ); -} +layout (std140, binding = 6) readonly buffer RegionsBuffer { + Region regions[]; +}; #define USE_CROSS 0 void main(){ - const float HALF_PIXEL = ubo.voxelizeScale; - const vec3 C = ( inPosition[0] + inPosition[1] + inPosition[2] ) / 3.0; + const vec3 triMin = min(inPosition[0], min(inPosition[1], inPosition[2])); + const vec3 triMax = max(inPosition[0], max(inPosition[1], inPosition[2])); + + uint emittedTriangles = 0; + for (uint r = 0; r < regions.length(); ++r) { + Region region = regions[r]; + if ( region.size == 0 ) continue; + + const float margin = (region.maxBounds.x - region.minBounds.x) / float(region.size) * 1.0; + + bool intersects = (triMin.x - margin <= region.maxBounds.x && triMax.x + margin >= region.minBounds.x) && + (triMin.y - margin <= region.maxBounds.y && triMax.y + margin >= region.minBounds.y) && + (triMin.z - margin <= region.maxBounds.z && triMax.z + margin >= region.minBounds.z); + + if ( !intersects ) continue; + const float HALF_PIXEL = (region.maxBounds.x - region.minBounds.x) / float(region.size) * 0.5; + const vec3 C = ( inPosition[0] + inPosition[1] + inPosition[2] ) / 3.0; #if USE_CROSS - const vec3 N = abs(cross(inPosition[2] - inPosition[0], inPosition[1] - inPosition[0])); + const vec3 N = abs(cross(inPosition[2] - inPosition[0], inPosition[1] - inPosition[0])); #else - const vec3 N = abs(inNormal[0] + inNormal[1] + inNormal[2]); - uint A = N.y > N.x ? 1 : 0; - A = N.z > N[A] ? 2 : A; + const vec3 N = abs(inNormal[0] + inNormal[1] + inNormal[2]); + uint A = N.y > N.x ? 1 : 0; + A = N.z > N[A] ? 2 : A; #endif - - const uint CASCADE = gl_ViewIndex; // inId[0].w; - const float power = cascadePower(CASCADE); - vec3 P[3] = { - vec3( ubo.voxel * vec4( inPosition[0], 1 ) ) / power, - vec3( ubo.voxel * vec4( inPosition[1], 1 ) ) / power, - vec3( ubo.voxel * vec4( inPosition[2], 1 ) ) / power, - }; - #pragma unroll 3 - for( uint i = 0; i < 3; ++i ){ - const vec3 D = normalize( inPosition[i] - C ) * HALF_PIXEL; + vec3 P[3]; + #pragma unroll 3 + for( uint i = 0; i < 3; ++i ){ + vec3 localPos = (inPosition[i] - region.minBounds) / (region.maxBounds - region.minBounds); + P[i] = localPos * 2.0 - 1.0; + } - outPosition = P[i] + D; - outPOS0 = inPOS0[i]; - outPOS1 = inPOS1[i]; - outUv = inUv[i]; - outSt = inSt[i]; - outColor = inColor[i]; - outNormal = inNormal[i]; - outTangent = inTangent[i]; - outId = inId[i]; - outId.w = gl_ViewIndex; - const vec3 P = outPosition; // + D; - #if USE_CROSS - if ( N.z > N.x && N.z > N.y ) gl_Position = vec4(P.x, P.y, 0, 1); - else if ( N.x > N.y && N.x > N.z ) gl_Position = vec4(P.y, P.z, 0, 1); - else gl_Position = vec4(P.x, P.z, 0, 1); - #else - if ( A == 0 ) gl_Position = vec4(P.zy, 0, 1 ); - else if ( A == 1 ) gl_Position = vec4(P.xz, 0, 1 ); - else if ( A == 2 ) gl_Position = vec4(P.xy, 0, 1 ); - #endif - EmitVertex(); + #pragma unroll 3 + for( uint i = 0; i < 3; ++i ){ + const vec3 D = normalize( inPosition[i] - C ) * HALF_PIXEL; + vec3 projectedD = D / (region.maxBounds - region.minBounds) * 2.0; + + outPosition = inPosition[i] + D; + outPOS0 = inPOS0[i]; + outPOS1 = inPOS1[i]; + outUv = inUv[i]; + outSt = inSt[i]; + outColor = inColor[i]; + outNormal = inNormal[i]; + outTangent = inTangent[i]; + outId = inId[i]; + outId.w = r; + + const vec3 finalP = P[i] + projectedD; + #if USE_CROSS + if ( N.z > N.x && N.z > N.y ) gl_Position = vec4(finalP.x, finalP.y, 0, 1); + else if ( N.x > N.y && N.x > N.z ) gl_Position = vec4(finalP.y, finalP.z, 0, 1); + else gl_Position = vec4(finalP.x, finalP.z, 0, 1); + #else + if ( A == 0 ) gl_Position = vec4(finalP.zy, 0, 1 ); + else if ( A == 1 ) gl_Position = vec4(finalP.xz, 0, 1 ); + else if ( A == 2 ) gl_Position = vec4(finalP.xy, 0, 1 ); + #endif + EmitVertex(); + } + EndPrimitive(); + + emittedTriangles++; + if ( emittedTriangles >= 4 ) break; } - EndPrimitive(); } \ No newline at end of file diff --git a/engine/inc/uf/engine/graph/graph.h b/engine/inc/uf/engine/graph/graph.h index 79be582f..2683bf73 100644 --- a/engine/inc/uf/engine/graph/graph.h +++ b/engine/inc/uf/engine/graph/graph.h @@ -115,6 +115,7 @@ namespace pod { uf::stl::vector shadow2Ds; uf::stl::vector shadowCubes; + uf::stl::vector regions; // flattened variants uf::stl::vector flattenedPrimitives; @@ -130,6 +131,7 @@ namespace pod { uf::renderer::Buffer material; uf::renderer::Buffer texture; uf::renderer::Buffer light; + uf::renderer::Buffer region; uf::renderer::Texture2D depthPyramid; } buffers; diff --git a/engine/inc/uf/engine/graph/pod.inl b/engine/inc/uf/engine/graph/pod.inl index 7c62d368..21a8acb6 100644 --- a/engine/inc/uf/engine/graph/pod.inl +++ b/engine/inc/uf/engine/graph/pod.inl @@ -113,6 +113,13 @@ namespace pod { uf::Image data; uf::renderer::Texture2D handle; }; + + struct UF_API Region { + pod::Vector3f minBounds = {}; + alignas(4) uint32_t index = 0; + pod::Vector3f maxBounds = {}; + alignas(4) uint32_t size = 64; + }; } namespace pod { diff --git a/engine/inc/uf/ext/vulkan/rendermodes/deferred.h b/engine/inc/uf/ext/vulkan/rendermodes/deferred.h index 1a33d243..be40d2b6 100644 --- a/engine/inc/uf/ext/vulkan/rendermodes/deferred.h +++ b/engine/inc/uf/ext/vulkan/rendermodes/deferred.h @@ -16,7 +16,6 @@ namespace ext { virtual void initialize( Device& device ); virtual void build( bool = true ); virtual void tick(); - virtual VkSubmitInfo queue(); virtual void render(); virtual void destroy(); }; diff --git a/engine/inc/uf/utils/string/rle.h b/engine/inc/uf/utils/string/rle.h index 4264df48..91780863 100644 --- a/engine/inc/uf/utils/string/rle.h +++ b/engine/inc/uf/utils/string/rle.h @@ -4,6 +4,7 @@ #include #include +#include namespace pod { template @@ -20,8 +21,9 @@ namespace uf { namespace rle { template typename pod::RLE::string_t encode( const uf::stl::vector& ); + template - uf::stl::vector decode( const pod::RLE& ); + uf::stl::vector decode( const uf::stl::vector>& ); } } diff --git a/engine/inc/uf/utils/string/rle.inl b/engine/inc/uf/utils/string/rle.inl index f61fc0e7..ed784567 100644 --- a/engine/inc/uf/utils/string/rle.inl +++ b/engine/inc/uf/utils/string/rle.inl @@ -1,24 +1,34 @@ template typename pod::RLE::string_t uf::rle::encode( const uf::stl::vector& source ) { typename pod::RLE::string_t destination; + if ( source.empty() ) return destination; + destination.reserve( source.size() ); - for ( std::size_t i = 0; i < source.size(); ++i ) { - pod::RLE reg = { 1, source[i] }; - while ( i + 1 < source.size() && source[i] == source[i + 1] ) ++reg.length, ++i; - destination.push_back( reg ); + for ( size_t i = 0; i < source.size(); ++i ) { + auto reg = pod::RLE{ 1, source[i] }; + + while ( i + 1 < source.size() && source[i] == source[i + 1] && reg.length < std::numeric_limits::max() ) { + ++reg.length; + ++i; + } + destination.emplace_back( reg ); } destination.shrink_to_fit(); return destination; } -template -uf::stl::vector uf::rle::decode( const pod::RLE& source ) { - uf::stl::vector destination; - for ( auto& s : source ) { +template +uf::stl::vector uf::rle::decode( const uf::stl::vector>& source ) { + uf::stl::vector destination; + if ( source.empty() ) return destination; + + for ( const auto& s : source ) { destination.reserve( destination.size() + s.length ); - for ( std::size_t i = 0; i < s.length; ++i ) destination.push_back(s.value); + for ( size_t i = 0; i < s.length; ++i ) { + destination.emplace_back(s.value); + } } destination.shrink_to_fit(); diff --git a/engine/src/engine/ext/raytrace/behavior.cpp b/engine/src/engine/ext/raytrace/behavior.cpp index cc7802e3..dc486b93 100644 --- a/engine/src/engine/ext/raytrace/behavior.cpp +++ b/engine/src/engine/ext/raytrace/behavior.cpp @@ -208,27 +208,27 @@ void ext::RayTraceSceneBehavior::tick( uf::Object& self ) { size_t maxTextures2D = uf::config["engine"]["scenes"]["textures"]["max"]["2D"].as(512); size_t maxTexturesCube = uf::config["engine"]["scenes"]["textures"]["max"]["cube"].as(128); size_t maxTextures3D = uf::config["engine"]["scenes"]["textures"]["max"]["3D"].as(1); - size_t maxCascades = uf::config["engine"]["scenes"]["vxgi"]["cascades"].as(16); + size_t maxRegions = uf::config["engine"]["scenes"]["vxgi"]["regions"].as(64); - shader.aliasBuffer( storage.buffers.instance ); - shader.aliasBuffer( storage.buffers.addresses ); - shader.aliasBuffer( storage.buffers.object ); - shader.aliasBuffer( storage.buffers.material ); - shader.aliasBuffer( storage.buffers.texture ); - shader.aliasBuffer( storage.buffers.light ); + shader.aliasBuffer( "instance", storage.buffers.instance ); + shader.aliasBuffer( "addresses", storage.buffers.addresses ); + shader.aliasBuffer( "object", storage.buffers.object ); + shader.aliasBuffer( "material", storage.buffers.material ); + shader.aliasBuffer( "texture", storage.buffers.texture ); + shader.aliasBuffer( "light", storage.buffers.light ); shader.setSpecializationConstants({ { "TEXTURES", maxTextures2D }, { "CUBEMAPS", maxTexturesCube }, - { "CASCADES", maxCascades }, + { "REGIONS", maxRegions }, }); shader.setDescriptorCounts({ { "samplerTextures", maxTextures2D }, { "samplerCubemaps", maxTexturesCube }, - { "voxelId", maxCascades }, - { "voxelNormal", maxCascades }, - { "voxelRadiance", maxCascades }, - { "voxelOutput", maxCascades }, + { "voxelId", maxRegions }, + { "voxelNormal", maxRegions }, + { "voxelRadiance", maxRegions }, + { "voxelOutput", maxRegions }, }); } } diff --git a/engine/src/engine/ext/scene/behavior.cpp b/engine/src/engine/ext/scene/behavior.cpp index 4b4141c3..e6fd2fd8 100644 --- a/engine/src/engine/ext/scene/behavior.cpp +++ b/engine/src/engine/ext/scene/behavior.cpp @@ -551,7 +551,7 @@ void ext::ExtSceneBehavior::tick( uf::Object& self ) { if ( image.viewType == uf::renderer::enums::Image::VIEW_TYPE_CUBE ) { textureCubeOffset++; - } else { + } else if ( image.viewType == uf::renderer::enums::Image::VIEW_TYPE_2D ) { texture2dOffset++; } } @@ -626,9 +626,10 @@ void ext::ExtSceneBehavior::tick( uf::Object& self ) { constexpr uint32_t MODE_CUBEMAP = 1; constexpr uint32_t MODE_SEPARATE_2DS = 2; - //for ( uint32_t i = 0; i < entities.size(); ++i ) { - for ( auto idx : indices ) { - auto& info = entities[idx]; + for ( uint32_t i = 0; i < entities.size(); ++i ) { + auto& info = entities[i]; + //for ( auto idx : indices ) { + // auto& info = entities[idx]; uf::Entity* entity = info.entity; int32_t boundIndexMap = -1; @@ -1110,14 +1111,13 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, uf::renderer::Graphic } bloom; struct VXGI { - alignas(16) pod::Matrix4f matrix; - alignas(4) float cascadePower; alignas(4) float granularity; alignas(4) float voxelizeScale; alignas(4) float occlusionFalloff; - alignas(4) float traceStartOffsetFactor; + alignas(4) uint32_t shadows; + alignas(4) uint32_t padding1; alignas(4) uint32_t padding2; alignas(4) uint32_t padding3; } vxgi; @@ -1260,12 +1260,9 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, uf::renderer::Graphic .threshold = metadata.bloom.threshold, }; uniforms.settings.vxgi = UniformDescriptor::Settings::VXGI{ - .matrix = metadataVxgi.extents.matrix, - .cascadePower = metadataVxgi.cascadePower, .granularity = metadataVxgi.granularity, .voxelizeScale = 1.0f / (metadataVxgi.voxelizeScale * std::max( metadataVxgi.voxelSize.x, std::max(metadataVxgi.voxelSize.y, metadataVxgi.voxelSize.z))), .occlusionFalloff = metadataVxgi.occlusionFalloff, - .traceStartOffsetFactor = metadataVxgi.traceStartOffsetFactor, .shadows = metadataVxgi.shadows, }; diff --git a/engine/src/engine/ext/voxelizer/behavior.cpp b/engine/src/engine/ext/voxelizer/behavior.cpp index fed5aab2..5149ad6b 100644 --- a/engine/src/engine/ext/voxelizer/behavior.cpp +++ b/engine/src/engine/ext/voxelizer/behavior.cpp @@ -27,7 +27,7 @@ namespace { }; struct PushConstants { uint32_t mips; - uint32_t cascade; + uint32_t region; uint32_t numWorkGroups; uint32_t workGroupOffset; }; @@ -49,7 +49,7 @@ void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) { UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson); auto mips = uf::vector::mips( metadata.voxelSize ); - for ( size_t i = 0; i < metadata.cascades; ++i ) { + for ( size_t i = 0; i < metadata.regions; ++i ) { const bool HDR = false; auto& id = sceneTextures.voxels.id.emplace_back(); id.sampler.descriptor.filter.min = uf::renderer::enums::Filter::NEAREST; @@ -131,8 +131,8 @@ void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) { } renderMode.metadata.pipelines.emplace_back(uf::renderer::settings::pipelines::names::vxgi); renderMode.metadata.samples = 1; - // renderMode.metadata.subpasses = metadata.cascades; - renderMode.metadata.views = metadata.cascades; + // renderMode.metadata.subpasses = metadata.regions; + renderMode.metadata.views = metadata.regions; renderMode.width = metadata.fragmentSize.x; renderMode.height = metadata.fragmentSize.y; @@ -152,7 +152,7 @@ void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) { size_t maxTextures2D = uf::config["engine"]["scenes"]["textures"]["max"]["2D"].as(512); size_t maxTexturesCube = uf::config["engine"]["scenes"]["textures"]["max"]["cube"].as(128); size_t maxTextures3D = uf::config["engine"]["scenes"]["textures"]["max"]["3D"].as(1); - size_t maxCascades = uf::config["engine"]["scenes"]["vxgi"]["cascades"].as(16); + size_t maxRegions = uf::config["engine"]["scenes"]["vxgi"]["regions"].as(16); size_t maxMips = uf::vector::mips( pod::Vector3ui{ 256, 256, 256 } ); // log2(256) = 9 renderMode.metadata.json["shaders"] = true; @@ -163,15 +163,15 @@ void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) { shader.setSpecializationConstants({ { "TEXTURES", maxTextures2D }, { "CUBEMAPS", maxTexturesCube }, - { "CASCADES", maxCascades }, + { "REGIONS", maxRegions }, }); shader.setDescriptorCounts({ { "samplerTextures", maxTextures2D }, { "samplerCubemaps", maxTexturesCube }, - { "voxelId", maxCascades }, - { "voxelNormal", maxCascades }, - { "voxelRadiance", maxCascades }, - { "voxelOutput", maxCascades }, + { "voxelId", maxRegions }, + { "voxelNormal", maxRegions }, + { "voxelRadiance", maxRegions }, + { "voxelOutput", maxRegions }, }); auto& scene = uf::scene::getCurrentScene(); @@ -193,12 +193,12 @@ void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) { shader.setSpecializationConstants({ { "TEXTURES", maxTextures2D }, { "CUBEMAPS", maxTexturesCube }, - { "CASCADES", maxCascades }, + { "REGIONS", maxRegions }, { "MIPS", maxMips }, }); shader.setDescriptorCounts({ - { "voxelRadiance", maxCascades }, - { "voxelMips", maxCascades * (maxMips - 1) }, + { "voxelRadiance", maxRegions }, + { "voxelMips", maxRegions * (maxMips - 1) }, }); auto& scene = uf::scene::getCurrentScene(); @@ -214,6 +214,21 @@ void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) { #endif renderMode.bindCallback( renderMode.CALLBACK_BEGIN, [&]( VkCommandBuffer commandBuffer, size_t _ ){ + VkMemoryBarrier warBarrier = {}; + warBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + warBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + warBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + vkCmdPipelineBarrier( + commandBuffer, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 1, &warBarrier, + 0, nullptr, + 0, nullptr + ); + // clear textures VkImageSubresourceRange subresourceRange = {}; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; @@ -226,37 +241,94 @@ void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) { for ( auto& t : sceneTextures.voxels.id ) vkCmdClearColorImage( commandBuffer, t.image, t.layout, &clearColor, 1, &subresourceRange ); for ( auto& t : sceneTextures.voxels.normal ) vkCmdClearColorImage( commandBuffer, t.image, t.layout, &clearColor, 1, &subresourceRange ); for ( auto& t : sceneTextures.voxels.radiance ) vkCmdClearColorImage( commandBuffer, t.image, t.layout, &clearColor, 1, &subresourceRange ); + #if !ALIAS_OUTPUT_TO_RADIANCE for ( auto& t : sceneTextures.voxels.output ) vkCmdClearColorImage( commandBuffer, t.image, t.layout, &clearColor, 1, &subresourceRange ); + #endif + VkMemoryBarrier clearBarrier = {}; + clearBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + clearBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + clearBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + + vkCmdPipelineBarrier( + commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, + 1, &clearBarrier, + 0, nullptr, + 0, nullptr + ); }); // renderMode.bindCallback( renderMode.CALLBACK_END, [&]( VkCommandBuffer commandBuffer, size_t _ ){ - // parse voxel lighting + VkMemoryBarrier fragToCompBarrier = {}; + fragToCompBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + fragToCompBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + fragToCompBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier( + commandBuffer, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, + 1, &fragToCompBarrier, + 0, nullptr, + 0, nullptr + ); + if ( blitter.initialized ) { auto descriptor = blitter.descriptor; - //descriptor.pipeline = "lighting"; blitter.record( commandBuffer, descriptor ); } - - // generate mipmaps + + VkMemoryBarrier compToCompBarrier = {}; + compToCompBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + compToCompBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + compToCompBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + + vkCmdPipelineBarrier( + commandBuffer, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, + 1, &compToCompBarrier, + 0, nullptr, + 0, nullptr + ); + #if COMPUTE_MIPMAP_GENERATION if ( blitter.initialized ) { auto& shader = blitter.material.getShader("compute", "mipmap"); auto mips = uf::vector::mips( pod::Vector3ui{ blitter.descriptor.bind.width, blitter.descriptor.bind.height, blitter.descriptor.bind.depth } ); - for ( auto cascade = 0; cascade < sceneTextures.voxels.output.size(); ++cascade ) { + uint32_t wgX = (blitter.descriptor.bind.width + 15) / 16; + uint32_t wgY = (blitter.descriptor.bind.height + 15) / 16; + uint32_t wgZ = (blitter.descriptor.bind.depth + 15) / 16; + uint32_t totalWorkGroups = wgX * wgY * wgZ; + + for ( auto region = 0; region < sceneTextures.voxels.output.size(); ++region ) { vkCmdFillBuffer(commandBuffer, metadata.atomicCounter.buffer, 0, 4, 0); + VkMemoryBarrier counterBarrier{VK_STRUCTURE_TYPE_MEMORY_BARRIER}; + counterBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; counterBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; counterBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, &counterBarrier, 0, nullptr, 0, nullptr); + vkCmdPipelineBarrier(commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, + 1, &counterBarrier, + 0, nullptr, + 0, nullptr + ); auto& pushConstant = shader.pushConstants.front().get<::PushConstants>(); pushConstant = { .mips = mips, - .cascade = cascade, - .numWorkGroups = 0, + .region = region, + .numWorkGroups = totalWorkGroups, .workGroupOffset = 0, }; auto descriptor = blitter.descriptor; @@ -278,7 +350,23 @@ void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) { t.setImageLayout( commandBuffer, t.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, t.layout, subresourceRange ); } #endif + + VkMemoryBarrier compToDeferredBarrier = {}; + compToDeferredBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + compToDeferredBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; + compToDeferredBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier( + commandBuffer, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, + 1, &compToDeferredBarrier, + 0, nullptr, + 0, nullptr + ); }); + } #endif } @@ -286,6 +374,9 @@ void ext::VoxelizerSceneBehavior::tick( uf::Object& self ) { #if UF_USE_VULKAN if ( !this->hasComponent() ) return; + // extremely cringe but the load placed on the GPU causes a nasty issue + vkDeviceWaitIdle(ext::vulkan::device.logicalDevice); + auto& metadata = this->getComponent(); auto& renderMode = this->getComponent(); @@ -307,37 +398,43 @@ void ext::VoxelizerSceneBehavior::tick( uf::Object& self ) { } #if 1 - // bool should = false; - // if ( renderMode.metadata.limiter.frequency <= 0 && renderMode.metadata.limiter.timer <= 0 ) should = true; - // else if ( renderMode.metadata.limiter.timer + renderMode.metadata.limiter.frequency >= renderMode.metadata.limiter.frequency ) should = true; - - // if ( renderMode.execute ) { if ( renderMode.metadata.limiter.execute ) { - // if ( should ) { auto& controller = scene.getController(); auto& camera = scene.getCamera( controller ); auto controllerTransform = uf::transform::flatten( camera.getTransform() ); - - float voxelWorldSizeX = (metadata.extents.max.x - metadata.extents.min.x) / (float)(metadata.voxelSize.x); - float voxelWorldSizeY = (metadata.extents.max.y - metadata.extents.min.y) / (float)(metadata.voxelSize.y); - float voxelWorldSizeZ = (metadata.extents.max.z - metadata.extents.min.z) / (float)(metadata.voxelSize.z); - pod::Vector3f controllerPosition = controllerTransform.position - metadata.extents.min; + // update storage regions + auto& storage = uf::graph::getStorage( *this ); + { + storage.regions.clear(); + for ( uint32_t i = 0; i < metadata.regions; ++i ) { + float scale = std::pow(2.0f, static_cast(i)); - controllerPosition.x = std::floor(controllerPosition.x / voxelWorldSizeX) * voxelWorldSizeX; - controllerPosition.y = std::floor(controllerPosition.y / voxelWorldSizeY) * voxelWorldSizeY; - controllerPosition.z = std::floor(controllerPosition.z / voxelWorldSizeZ) * voxelWorldSizeZ; + pod::Vector3f halfExtents = (metadata.extents.max - metadata.extents.min) * 0.5f * scale; - controllerPosition += metadata.extents.min; + pod::Region region; + region.minBounds = controllerTransform.position - halfExtents; + region.maxBounds = controllerTransform.position + halfExtents; + region.index = i; + region.size = metadata.voxelSize.x; - controllerPosition.x = std::floor(controllerPosition.x / voxelWorldSizeX) * voxelWorldSizeX; - controllerPosition.y = std::floor(controllerPosition.y / voxelWorldSizeY) * voxelWorldSizeY; - controllerPosition.z = -std::floor(controllerPosition.z / voxelWorldSizeZ) * voxelWorldSizeZ; + storage.regions.emplace_back( region ); + } - pod::Vector3f min = metadata.extents.min + controllerPosition; - pod::Vector3f max = metadata.extents.max + controllerPosition; - - metadata.extents.matrix = uf::matrix::orthographic( min.x, max.x, min.y, max.y, min.z, max.z ); + size_t maxRegions = uf::config["engine"]["scenes"]["vxgi"]["regions"].as(16); + while ( storage.regions.size() < maxRegions ) { + pod::Region dummy; + dummy.minBounds = { 0.0f, 0.0f, 0.0f }; + dummy.maxBounds = { 0.0f, 0.0f, 0.0f }; + dummy.index = 0; + dummy.size = 0; + storage.regions.emplace_back( dummy ); + } + auto rebuild = storage.buffers.region.update( (const void*) storage.regions.data(), storage.regions.size() * sizeof(pod::Region) ); + if ( rebuild ) { + storage.stale = true; + } + } auto/*&*/ graph = scene.getGraph(); for ( auto entity : graph ) { @@ -346,27 +443,17 @@ void ext::VoxelizerSceneBehavior::tick( uf::Object& self ) { if ( blitter.material.hasShader("geometry", uf::renderer::settings::pipelines::names::vxgi) ) { auto& shader = blitter.material.getShader("geometry", uf::renderer::settings::pipelines::names::vxgi); struct UniformDescriptor { - /*alignas(16)*/ pod::Matrix4f matrix; - /*alignas(4)*/ float cascadePower; /*alignas(4)*/ float granularity; - /*alignas(4)*/ float voxelizeScale; - /*alignas(4)*/ float occlusionFalloff; - - /*alignas(4)*/ float traceStartOffsetFactor; + /*alignas(4)*/ float occlusionFalloff; /*alignas(4)*/ uint32_t shadows; - /*alignas(4)*/ uint32_t padding2; - /*alignas(4)*/ uint32_t padding3; + /*alignas(4)*/ uint32_t regions; }; UniformDescriptor uniforms = { - .matrix = metadata.extents.matrix, - .cascadePower = metadata.cascadePower, .granularity = metadata.granularity, - .voxelizeScale = 1.0f / (metadata.voxelizeScale * std::max( metadata.voxelSize.x, std::max(metadata.voxelSize.y, metadata.voxelSize.z))), - .occlusionFalloff = metadata.occlusionFalloff, - - .traceStartOffsetFactor = metadata.traceStartOffsetFactor, + .occlusionFalloff = metadata.occlusionFalloff, .shadows = metadata.shadows, + .regions = storage.regions.size(), }; shader.updateBuffer( (const void*) &uniforms, sizeof(uniforms), shader.getUniformBuffer("UBO") ); } @@ -404,10 +491,8 @@ void ext::VoxelizerSceneBehavior::Metadata::serialize( uf::Object& self, uf::Ser serializer["vxgi"]["limiter"] = /*this->*/limiter.frequency; serializer["vxgi"]["dispatch"] = /*this->*/dispatchSize.x; - serializer["vxgi"]["cascades"] = /*this->*/cascades; - serializer["vxgi"]["cascadePower"] = /*this->*/cascadePower; + serializer["vxgi"]["regions"] = /*this->*/regions; serializer["vxgi"]["granularity"] = /*this->*/granularity; - serializer["vxgi"]["voxelizeScale"] = /*this->*/voxelizeScale; serializer["vxgi"]["occlusionFalloff"] = /*this->*/occlusionFalloff; serializer["vxgi"]["traceStartOffsetFactor"] = /*this->*/traceStartOffsetFactor; serializer["vxgi"]["shadows"] = /*this->*/shadows; @@ -436,10 +521,8 @@ void ext::VoxelizerSceneBehavior::Metadata::deserialize( uf::Object& self, uf::S /*this->*/dispatchSize.y = serializer["vxgi"]["dispatch"].as(/*this->*/dispatchSize.x); /*this->*/dispatchSize.z = serializer["vxgi"]["dispatch"].as(/*this->*/dispatchSize.x); - /*this->*/cascades = serializer["vxgi"]["cascades"].as(/*this->*/cascades); - /*this->*/cascadePower = serializer["vxgi"]["cascadePower"].as(/*this->*/cascadePower); + /*this->*/regions = serializer["vxgi"]["regions"].as(/*this->*/regions); /*this->*/granularity = serializer["vxgi"]["granularity"].as(/*this->*/granularity); - /*this->*/voxelizeScale = serializer["vxgi"]["voxelizeScale"].as(/*this->*/voxelizeScale); /*this->*/occlusionFalloff = serializer["vxgi"]["occlusionFalloff"].as(/*this->*/occlusionFalloff); /*this->*/traceStartOffsetFactor = serializer["vxgi"]["traceStartOffsetFactor"].as(/*this->*/traceStartOffsetFactor); /*this->*/shadows = serializer["vxgi"]["shadows"].as(/*this->*/shadows); diff --git a/engine/src/engine/ext/voxelizer/behavior.h b/engine/src/engine/ext/voxelizer/behavior.h index 74e8fb78..87462092 100644 --- a/engine/src/engine/ext/voxelizer/behavior.h +++ b/engine/src/engine/ext/voxelizer/behavior.h @@ -24,7 +24,7 @@ namespace ext { uf::stl::vector views; #endif - size_t cascades = 0; + size_t regions = 0; float cascadePower = 0; float granularity = 0; float voxelizeScale = 0; diff --git a/engine/src/engine/graph/graph.cpp b/engine/src/engine/graph/graph.cpp index 5dcdba9a..02e1f773 100644 --- a/engine/src/engine/graph/graph.cpp +++ b/engine/src/engine/graph/graph.cpp @@ -16,6 +16,7 @@ #include #include +#include "../ext/voxelizer/behavior.h" // yucky #if UF_ENV_DREAMCAST #define UF_DEBUG_TIMER_MULTITRACE_START(...) UF_TIMER_MULTITRACE_START(__VA_ARGS__) @@ -208,17 +209,17 @@ namespace { size_t maxTextures = storage.textures.map.size(); size_t maxCubemaps = uf::config["engine"]["scenes"]["textures"]["max"]["cube"].as(128); size_t maxTextures3D = uf::config["engine"]["scenes"]["textures"]["max"]["3D"].as(128); - uint32_t maxCascades = sceneTextures.voxels.id.size(); + uint32_t maxRegions = sceneTextures.voxels.id.size(); shader.setSpecializationConstants({ { "TEXTURES", maxTextures }, { "CUBEMAPS", maxCubemaps }, - { "CASCADES", maxCascades }, + { "REGIONS", maxRegions }, }); shader.setDescriptorCounts({ { "samplerTextures", maxTextures }, { "samplerCubemaps", maxCubemaps }, - { "voxelOutput", maxCascades }, + { "voxelOutput", maxRegions }, }); #endif } @@ -279,21 +280,21 @@ namespace { } uint32_t maxTextures = texture2Ds; - uint32_t maxCascades = sceneTextures.voxels.id.size(); + uint32_t maxRegions = sceneTextures.voxels.id.size(); // fragment shader { auto& shader = graphic.material.getShader("fragment", uf::renderer::settings::pipelines::names::vxgi); shader.setSpecializationConstants({ { "TEXTURES", maxTextures }, - { "CASCADES", maxCascades }, + { "REGIONS", maxRegions }, }); shader.setDescriptorCounts({ { "samplerTextures", maxTextures }, - { "voxelId", maxCascades }, - { "voxelNormal", maxCascades }, - { "voxelRadiance", maxCascades }, - { "voxelOutput", maxCascades }, + { "voxelId", maxRegions }, + { "voxelNormal", maxRegions }, + { "voxelRadiance", maxRegions }, + { "voxelOutput", maxRegions }, }); for ( auto& t : sceneTextures.voxels.id ) shader.textures.emplace_back().aliasTexture(t); @@ -411,11 +412,11 @@ namespace { // bind buffers ::resetBuffers( shader ); - shader.aliasBuffer( storage.buffers.joint ); - shader.aliasBuffer( vertexPositionBuffer ); - shader.aliasBuffer( vertexJointsBuffer ); - shader.aliasBuffer( vertexWeightsBuffer ); - shader.aliasBuffer( vertexOutPosition ); + shader.aliasBuffer( "joint", storage.buffers.joint ); + shader.aliasBuffer( "vertexPositionBuffer", vertexPositionBuffer ); + shader.aliasBuffer( "vertexJointsBuffer", vertexJointsBuffer ); + shader.aliasBuffer( "vertexWeightsBuffer", vertexWeightsBuffer ); + shader.aliasBuffer( "vertexOutPosition", vertexOutPosition ); } graphic.generateBottomAccelerationStructures(); @@ -502,6 +503,13 @@ namespace { // vxgi pipeline if ( uf::renderer::settings::pipelines::vxgi ) { + // geom + { + auto& shader = graphic.material.getShader("geometry", uf::renderer::settings::pipelines::names::vxgi); + + ::resetBuffers( shader ); + shader.aliasBuffer( "region", storage.buffers.region ); + } // fragment shader { auto& shader = graphic.material.getShader("fragment", uf::renderer::settings::pipelines::names::vxgi); @@ -514,6 +522,7 @@ namespace { shader.aliasBuffer( "material", storage.buffers.material ); shader.aliasBuffer( "texture", storage.buffers.texture ); shader.aliasBuffer( "light", storage.buffers.light ); + shader.aliasBuffer( "region", storage.buffers.region ); } } // baking pipeline @@ -1770,6 +1779,7 @@ void uf::graph::initialize( pod::Graph::Storage& storage, size_t initialElements if ( !storage.buffers.material.buffer ) storage.buffers.material.initialize( (const void*) nullptr, sizeof(pod::Material) * initialElements, uf::renderer::enums::Buffer::STORAGE ); if ( !storage.buffers.texture.buffer ) storage.buffers.texture.initialize( (const void*) nullptr, sizeof(pod::Texture) * initialElements, uf::renderer::enums::Buffer::STORAGE ); if ( !storage.buffers.light.buffer ) storage.buffers.light.initialize( (const void*) nullptr, sizeof(pod::Light) * initialElements, uf::renderer::enums::Buffer::STORAGE ); + if ( !storage.buffers.region.buffer ) storage.buffers.region.initialize( (const void*) nullptr, sizeof(pod::Region) * initialElements, uf::renderer::enums::Buffer::STORAGE ); } void uf::graph::initialize( pod::Graph& graph ) { @@ -1935,6 +1945,7 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) { rebuild = storage.buffers.lodMetadata.update( (const void*) lodMetadata.data(), lodMetadata.size() * sizeof(pod::LODMetadata) ) || rebuild; rebuild = storage.buffers.material.update( (const void*) materials.data(), materials.size() * sizeof(pod::Material) ) || rebuild; rebuild = storage.buffers.texture.update( (const void*) textures.data(), textures.size() * sizeof(pod::Texture) ) || rebuild; +// rebuild = storage.buffers.region.update( (const void*) storage.regions.data(), storage.regions.size() * sizeof(pod::Region) ) || rebuild; storage.stale = false; } @@ -1956,9 +1967,11 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) { shader.aliasBuffer( "drawCommands", storage.buffers.drawCommands ); shader.aliasBuffer( "instance", storage.buffers.instance ); shader.aliasBuffer( "addresses", storage.buffers.addresses ); + shader.aliasBuffer( "object", storage.buffers.object ); shader.aliasBuffer( "material", storage.buffers.material ); shader.aliasBuffer( "texture", storage.buffers.texture ); shader.aliasBuffer( "light", storage.buffers.light ); + shader.aliasBuffer( "region", storage.buffers.region ); } } #endif @@ -2096,13 +2109,13 @@ void uf::graph::render( pod::Graph::Storage& storage ) { } #endif - storage.buffers.camera.update( (const void*) &viewport, sizeof(pod::Camera::Viewports) ); - #if UF_USE_VULKAN - if ( !renderMode || !renderMode->hasBuffer("camera") || renderMode->getType() == "Swapchain" ) return; - auto& buffer = renderMode->getBuffer("camera"); - buffer.update( (const void*) &viewport, sizeof(pod::Camera::Viewports) ); + if ( renderMode->hasBuffer("camera") ) { + auto& buffer = renderMode->getBuffer("camera"); + buffer.update( (const void*) &viewport, sizeof(pod::Camera::Viewports) ); + } else #endif + storage.buffers.camera.update( (const void*) &viewport, sizeof(pod::Camera::Viewports) ); } void uf::graph::destroy( bool soft ) { soft = false; @@ -2162,6 +2175,7 @@ void uf::graph::destroy( pod::Graph::Storage& storage, bool soft ) { storage.buffers.material.destroy(true); storage.buffers.texture.destroy(true); storage.buffers.light.destroy(true); + storage.buffers.region.destroy(true); storage.buffers.depthPyramid.destroy(true); } @@ -2839,6 +2853,86 @@ void uf::graph::reload( pod::Graph& graph ) { if ( !readQueue.empty() ) { executing = true; } +/* + // to-do: finish implementing + if ( uf::renderer::settings::pipelines::vxgi ) { + auto& scene = uf::scene::getCurrentScene(); + // auto& metadataVxgi = // to-do: bind settings properly, can probably just fill it out with a dummy struct to test with for the moment + struct { + pod::Vector3f voxelSize = { 64, 64, 64 }; + size_t cascades = 256; + } metadataVxgi; + + storage.regions.clear(); + struct RegionBounds { + pod::Vector3f min = { std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max() }; + pod::Vector3f max = {-std::numeric_limits::max(),-std::numeric_limits::max(),-std::numeric_limits::max() }; + bool initialized = false; // could probably just check if min/max are not max/-max'd + }; + uf::stl::unordered_map sectors; + uf::stl::unordered_map indexMap; + + for ( auto& node : graph.nodes ) { + if ( !(0 <= node.mesh && node.mesh < graph.meshes.size()) ) continue; + if ( !node.entity ) continue; + + auto& entity = node.entity->as(); + auto& transform = entity.getComponent>(); + auto worldTransform = uf::transform::flatten( transform ); + auto model = uf::transform::model( transform ); + + auto& key = graph.primitives[node.mesh]; + auto& primitives = storage.primitives.map[key]; + + for ( auto& primitive : primitives ) { + int32_t regionID = primitive.instance.lightmapID; + if ( regionID < 0 ) continue; + + auto& bounds = primitive.instance.bounds; + auto& sector = sectors[regionID]; + + pod::Vector3f corners[8] = { + {bounds.min.x, bounds.min.y, bounds.min.z}, + {bounds.max.x, bounds.min.y, bounds.min.z}, + {bounds.min.x, bounds.max.y, bounds.min.z}, + {bounds.max.x, bounds.max.y, bounds.min.z}, + {bounds.min.x, bounds.min.y, bounds.max.z}, + {bounds.max.x, bounds.min.y, bounds.max.z}, + {bounds.min.x, bounds.max.y, bounds.max.z}, + {bounds.max.x, bounds.max.y, bounds.max.z} + }; + + for ( int c = 0; c < 8; ++c ) { + pod::Vector3f transformed = uf::matrix::multiply( model, corners[c], 1.0f ); + sector.min = uf::vector::min( sector.min, transformed ); + sector.max = uf::vector::max( sector.max, transformed ); + } + sector.initialized = true; + } + } + + for ( auto& [regionID, sector] : sectors ) { + if ( !sector.initialized ) continue; + if ( indexMap.count(regionID) == 0 ) { + uint32_t nextIndex = storage.regions.size(); + if ( nextIndex >= metadataVxgi.cascades ) continue; + indexMap[regionID] = nextIndex; + } + + float padding = 2.0f; + pod::Region region = {}; + region.minBounds = sector.min - padding; + region.maxBounds = sector.max + padding; + region.size = metadataVxgi.voxelSize.x; + region.index = indexMap[regionID]; + + storage.regions.emplace_back( region ); + if ( storage.regions.size() >= metadataVxgi.cascades ) break; + } + + storage.stale = true; + } +*/ uf::asset::processIO( readQueue, true, false ); // async, wait } diff --git a/engine/src/ext/vulkan/rendermode.cpp b/engine/src/ext/vulkan/rendermode.cpp index dc792ec9..23d77314 100644 --- a/engine/src/ext/vulkan/rendermode.cpp +++ b/engine/src/ext/vulkan/rendermode.cpp @@ -385,14 +385,15 @@ VkSubmitInfo ext::vulkan::RenderMode::queue() { if ( !metadata.limiter.execute ) return {}; auto& commands = getCommands( this->mostRecentCommandPoolId ); + VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.pWaitDstStageMask = NULL; // Pointer to the list of pipeline stages that the semaphore waits will occur at - submitInfo.pWaitSemaphores = NULL; // Semaphore(s) to wait upon before the submitted command buffer starts executing - submitInfo.waitSemaphoreCount = 0; // One wait semaphore - submitInfo.pSignalSemaphores = NULL; // Semaphore(s) to be signaled when command buffers have completed - submitInfo.signalSemaphoreCount = 0; // One signal semaphore - submitInfo.pCommandBuffers = &commands[states::currentBuffer]; // Command buffers(s) to execute in this batch (submission) + submitInfo.pWaitDstStageMask = NULL; + submitInfo.pWaitSemaphores = NULL; + submitInfo.waitSemaphoreCount = 0; + submitInfo.pSignalSemaphores = &renderCompleteSemaphores[states::currentBuffer]; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pCommandBuffers = &commands[states::currentBuffer]; submitInfo.commandBufferCount = 1; return submitInfo; diff --git a/engine/src/ext/vulkan/rendermodes/base.cpp b/engine/src/ext/vulkan/rendermodes/base.cpp index 0b53e6f7..139d116a 100644 --- a/engine/src/ext/vulkan/rendermodes/base.cpp +++ b/engine/src/ext/vulkan/rendermodes/base.cpp @@ -267,16 +267,34 @@ void ext::vulkan::BaseRenderMode::render() { device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::END, "end" ); VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer)); + + { + VkSubmitInfo submitInfo = this->queue(); + VkQueue queue = device->getQueue( QueueEnum::GRAPHICS ); + + STATIC_THREAD_LOCAL(uf::stl::vector, waitSemaphores); + STATIC_THREAD_LOCAL(uf::stl::vector, waitStages); + + waitSemaphores.push_back(swapchain.presentCompleteSemaphores[states::currentBuffer]); + waitStages.push_back(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + + for ( auto* layer : layers ) { + if ( !layer || !layer->executed ) continue; + + waitSemaphores.push_back(layer->renderCompleteSemaphores[states::currentBuffer]); + waitStages.push_back(VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + } + + submitInfo.waitSemaphoreCount = waitSemaphores.size(); + submitInfo.pWaitSemaphores = waitSemaphores.data(); + submitInfo.pWaitDstStageMask = waitStages.data(); + + VkResult res = vkQueueSubmit( queue, 1, &submitInfo, fences[states::currentBuffer]); + VK_CHECK_QUEUE_CHECKPOINT( queue, res ); + } } - { - VkSubmitInfo submitInfo = this->queue(); - VkQueue queue = device->getQueue( QueueEnum::GRAPHICS ); - VkResult res = vkQueueSubmit( queue, 1, &submitInfo, fences[states::currentBuffer]); - VK_CHECK_QUEUE_CHECKPOINT( queue, res ); - } - - VK_CHECK_RESULT(swapchain.queuePresent(device->getQueue( QueueEnum::PRESENT ), ::imageIndex, renderCompleteSemaphores[::imageIndex])); + VK_CHECK_RESULT(swapchain.queuePresent(device->getQueue( QueueEnum::PRESENT ), ::imageIndex, renderCompleteSemaphores[states::currentBuffer])); states::currentBuffer = (states::currentBuffer + 1) % ext::vulkan::swapchain.buffers; this->executed = true; diff --git a/engine/src/ext/vulkan/rendermodes/deferred.cpp b/engine/src/ext/vulkan/rendermodes/deferred.cpp index 74e091d1..3ee55198 100644 --- a/engine/src/ext/vulkan/rendermodes/deferred.cpp +++ b/engine/src/ext/vulkan/rendermodes/deferred.cpp @@ -342,20 +342,20 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) { size_t maxTextures2D = uf::config["engine"]["scenes"]["textures"]["max"]["2D"].as(512); size_t maxTexturesCube = uf::config["engine"]["scenes"]["textures"]["max"]["cube"].as(128); size_t maxTextures3D = uf::config["engine"]["scenes"]["textures"]["max"]["3D"].as(128); - size_t maxCascades = uf::config["engine"]["scenes"]["vxgi"]["cascades"].as(16); + size_t maxRegions = uf::config["engine"]["scenes"]["vxgi"]["regions"].as(64); shader.setSpecializationConstants({ { "TEXTURES", maxTextures2D }, { "CUBEMAPS", maxTexturesCube }, - { "CASCADES", maxCascades }, + { "REGIONS", maxRegions }, }); shader.setDescriptorCounts({ { "samplerTextures", maxTextures2D }, { "samplerCubemaps", maxTexturesCube }, - { "voxelId", maxCascades }, - { "voxelNormal", maxCascades }, - { "voxelRadiance", maxCascades }, - { "voxelOutput", maxCascades }, + { "voxelId", maxRegions }, + { "voxelNormal", maxRegions }, + { "voxelRadiance", maxRegions }, + { "voxelOutput", maxRegions }, }); shader.aliasAttachment("id", this); @@ -545,15 +545,16 @@ void ext::vulkan::DeferredRenderMode::build( bool resized ) { auto& shader = blitter.material.getShader("compute", "deferred"); shader.metadata.aliases.buffers.clear(); - shader.aliasBuffer( storage.buffers.camera ); - // shader.aliasBuffer( storage.buffers.joint ); - shader.aliasBuffer( storage.buffers.drawCommands ); - shader.aliasBuffer( storage.buffers.instance ); - shader.aliasBuffer( storage.buffers.addresses ); - shader.aliasBuffer( storage.buffers.object ); - shader.aliasBuffer( storage.buffers.material ); - shader.aliasBuffer( storage.buffers.texture ); - shader.aliasBuffer( storage.buffers.light ); + shader.aliasBuffer( "camera", storage.buffers.camera ); + // shader.aliasBuffer( "joint", storage.buffers.joint ); + shader.aliasBuffer( "drawCommands", storage.buffers.drawCommands ); + shader.aliasBuffer( "instance", storage.buffers.instance ); + shader.aliasBuffer( "addresses", storage.buffers.addresses ); + shader.aliasBuffer( "object", storage.buffers.object ); + shader.aliasBuffer( "material", storage.buffers.material ); + shader.aliasBuffer( "texture", storage.buffers.texture ); + shader.aliasBuffer( "light", storage.buffers.light ); + shader.aliasBuffer( "region", storage.buffers.region ); } // (re)initialize pipelines @@ -660,21 +661,6 @@ void ext::vulkan::DeferredRenderMode::tick() { this->build( resized ); } } -VkSubmitInfo ext::vulkan::DeferredRenderMode::queue() { - auto& commands = getCommands( this->mostRecentCommandPoolId ); - - VkSubmitInfo submitInfo = {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.pWaitDstStageMask = NULL; - submitInfo.pWaitSemaphores = NULL; - submitInfo.waitSemaphoreCount = 0; - submitInfo.pSignalSemaphores = NULL; - submitInfo.signalSemaphoreCount = 0; - submitInfo.pCommandBuffers = &commands[states::currentBuffer]; - submitInfo.commandBufferCount = 1; - - return submitInfo; -} void ext::vulkan::DeferredRenderMode::render() { if ( this->commands.container().empty() ) return; diff --git a/engine/src/ext/vulkan/rendermodes/rendertarget.cpp b/engine/src/ext/vulkan/rendermodes/rendertarget.cpp index 41e8e8e2..efb468fa 100644 --- a/engine/src/ext/vulkan/rendermodes/rendertarget.cpp +++ b/engine/src/ext/vulkan/rendermodes/rendertarget.cpp @@ -23,7 +23,7 @@ ext::vulkan::GraphicDescriptor ext::vulkan::RenderTargetRenderMode::bindGraphicD descriptor.cullMode = VK_CULL_MODE_NONE; descriptor.depth.test = false; descriptor.depth.write = false; - } else if ( metadata.type == "depth" ) { + } else if ( metadata.type == "depth" || metadata.type == uf::renderer::settings::pipelines::names::vxgi ) { descriptor.cullMode = VK_CULL_MODE_NONE; } return descriptor; @@ -43,7 +43,7 @@ void ext::vulkan::RenderTargetRenderMode::initialize( Device& device ) { // if ( metadata.type == "depth" ) { - buffers.emplace_back().initialize( NULL, sizeof(pod::Camera::Viewports), uf::renderer::enums::Buffer::UNIFORM ); + this->metadata.buffers["camera"] = this->initializeBuffer( (const void*) nullptr, sizeof(pod::Camera::Viewports), uf::renderer::enums::Buffer::UNIFORM ); } if ( metadata.type == "depth" || metadata.type == uf::renderer::settings::pipelines::names::vxgi ) { @@ -247,15 +247,16 @@ void ext::vulkan::RenderTargetRenderMode::build( bool resized ) { if ( metadata.type == uf::renderer::settings::pipelines::names::vxgi ) { auto& shader = blitter.material.getShader("compute"); - // shader.aliasBuffer( storage.buffers.camera ); - // shader.aliasBuffer( storage.buffers.joint ); - shader.aliasBuffer( storage.buffers.drawCommands ); - shader.aliasBuffer( storage.buffers.instance ); - shader.aliasBuffer( storage.buffers.addresses ); - shader.aliasBuffer( storage.buffers.object ); - shader.aliasBuffer( storage.buffers.material ); - shader.aliasBuffer( storage.buffers.texture ); - shader.aliasBuffer( storage.buffers.light ); + // shader.aliasBuffer( "camera", storage.buffers.camera ); + // shader.aliasBuffer( "joint", storage.buffers.joint ); + shader.aliasBuffer( "drawCommands", storage.buffers.drawCommands ); + shader.aliasBuffer( "instance", storage.buffers.instance ); + shader.aliasBuffer( "addresses", storage.buffers.addresses ); + shader.aliasBuffer( "object", storage.buffers.object ); + shader.aliasBuffer( "material", storage.buffers.material ); + shader.aliasBuffer( "texture", storage.buffers.texture ); + shader.aliasBuffer( "light", storage.buffers.light ); + shader.aliasBuffer( "region", storage.buffers.region ); } // (re)initialize pipelines diff --git a/engine/src/ext/vulkan/rendermodes/transition.inl b/engine/src/ext/vulkan/rendermodes/transition.inl index 5d94962e..3584469a 100644 --- a/engine/src/ext/vulkan/rendermodes/transition.inl +++ b/engine/src/ext/vulkan/rendermodes/transition.inl @@ -8,7 +8,7 @@ namespace { VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ) { uf::stl::unordered_set transitioned; - + VkImageSubresourceRange subresourceRange; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = 1; @@ -51,11 +51,17 @@ namespace { if ( isDepth && layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ) { oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; } - uf::renderer::Texture::setImageLayout( commandBuffer, image, oldLayout, descriptor.layout, subresourceRange ); + + VkImageLayout targetLayout = descriptor.layout; + if ( isDepth && targetLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ) { + targetLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + } + + uf::renderer::Texture::setImageLayout( commandBuffer, image, oldLayout, targetLayout, subresourceRange ); if ( mips > 1 ) { subresourceRange.baseMipLevel = 1; subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; - uf::renderer::Texture::setImageLayout( commandBuffer, image, initialLayout, descriptor.layout, subresourceRange ); + uf::renderer::Texture::setImageLayout( commandBuffer, image, initialLayout, targetLayout, subresourceRange ); } } } diff --git a/engine/src/ext/vulkan/shader.cpp b/engine/src/ext/vulkan/shader.cpp index a6d2b77a..ad864f46 100644 --- a/engine/src/ext/vulkan/shader.cpp +++ b/engine/src/ext/vulkan/shader.cpp @@ -518,7 +518,7 @@ void ext::vulkan::Shader::initialize( ext::vulkan::Device& device, const uf::stl .size = bufferSize, }; - if ( IS_DYNAMIC(name) ) { + if ( VK_UBO_USE_N_BUFFERS && IS_DYNAMIC(name) ) { descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; metadata.dynamicRanges.emplace_back( bufferSize ); } diff --git a/engine/src/utils/debug/draw.cpp b/engine/src/utils/debug/draw.cpp index ea816a0f..fc2ac8dc 100644 --- a/engine/src/utils/debug/draw.cpp +++ b/engine/src/utils/debug/draw.cpp @@ -274,7 +274,7 @@ void uf::debug::drawLines( float dt ) { // vertex shader { auto& shader = graphic.material.getShader("vertex"); - shader.aliasBuffer( storage.buffers.camera ); + shader.aliasBuffer( "camera", storage.buffers.camera ); #if UF_USE_VULKAN uint32_t maxPasses = 6; shader.setSpecializationConstants({ @@ -368,7 +368,7 @@ void uf::debug::drawTexts( float dt ) { // vertex shader { auto& shader = graphic.material.getShader("vertex"); - shader.aliasBuffer( storage.buffers.camera ); + shader.aliasBuffer( "camera", storage.buffers.camera ); #if UF_USE_VULKAN uint32_t maxPasses = 6; shader.setSpecializationConstants({