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)

This commit is contained in:
ecker 2026-08-15 23:36:26 -05:00
parent 08134804cf
commit a43b1567b3
27 changed files with 582 additions and 395 deletions

View File

@ -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": {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -115,6 +115,7 @@ namespace pod {
uf::stl::vector<uf::renderer::Texture2D> shadow2Ds;
uf::stl::vector<uf::renderer::TextureCube> shadowCubes;
uf::stl::vector<pod::Region> regions;
// flattened variants
uf::stl::vector<pod::Primitive> 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;

View File

@ -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 {

View File

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

View File

@ -4,6 +4,7 @@
#include <uf/utils/memory/vector.h>
#include <stdint.h>
#include <limits>
namespace pod {
template<typename T, typename U = uint16_t>
@ -20,8 +21,9 @@ namespace uf {
namespace rle {
template<typename T, typename U = uint16_t>
typename pod::RLE<T,U>::string_t encode( const uf::stl::vector<T>& );
template<typename T, typename U = uint16_t>
uf::stl::vector<T> decode( const pod::RLE<T,U>& );
uf::stl::vector<T> decode( const uf::stl::vector<pod::RLE<T,U>>& );
}
}

View File

@ -1,24 +1,34 @@
template<typename T, typename U>
typename pod::RLE<T,U>::string_t uf::rle::encode( const uf::stl::vector<T>& source ) {
typename pod::RLE<T,U>::string_t destination;
if ( source.empty() ) return destination;
destination.reserve( source.size() );
for ( std::size_t i = 0; i < source.size(); ++i ) {
pod::RLE<T,U> 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<T,U>{ 1, source[i] };
while ( i + 1 < source.size() && source[i] == source[i + 1] && reg.length < std::numeric_limits<U>::max() ) {
++reg.length;
++i;
}
destination.emplace_back( reg );
}
destination.shrink_to_fit();
return destination;
}
template<typename T, typename U>
uf::stl::vector<T> uf::rle::decode( const pod::RLE<T,U>& source ) {
uf::stl::vector<T> destination;
for ( auto& s : source ) {
template<typename T, typename U>
uf::stl::vector<T> uf::rle::decode( const uf::stl::vector<pod::RLE<T,U>>& source ) {
uf::stl::vector<T> 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();

View File

@ -208,27 +208,27 @@ void ext::RayTraceSceneBehavior::tick( uf::Object& self ) {
size_t maxTextures2D = uf::config["engine"]["scenes"]["textures"]["max"]["2D"].as<size_t>(512);
size_t maxTexturesCube = uf::config["engine"]["scenes"]["textures"]["max"]["cube"].as<size_t>(128);
size_t maxTextures3D = uf::config["engine"]["scenes"]["textures"]["max"]["3D"].as<size_t>(1);
size_t maxCascades = uf::config["engine"]["scenes"]["vxgi"]["cascades"].as<size_t>(16);
size_t maxRegions = uf::config["engine"]["scenes"]["vxgi"]["regions"].as<size_t>(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 },
});
}
}

View File

@ -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<uint32_t>( metadataVxgi.voxelSize.x, std::max<uint32_t>(metadataVxgi.voxelSize.y, metadataVxgi.voxelSize.z))),
.occlusionFalloff = metadataVxgi.occlusionFalloff,
.traceStartOffsetFactor = metadataVxgi.traceStartOffsetFactor,
.shadows = metadataVxgi.shadows,
};

View File

@ -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<size_t>(512);
size_t maxTexturesCube = uf::config["engine"]["scenes"]["textures"]["max"]["cube"].as<size_t>(128);
size_t maxTextures3D = uf::config["engine"]["scenes"]["textures"]["max"]["3D"].as<size_t>(1);
size_t maxCascades = uf::config["engine"]["scenes"]["vxgi"]["cascades"].as<size_t>(16);
size_t maxRegions = uf::config["engine"]["scenes"]["vxgi"]["regions"].as<size_t>(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<uf::renderer::RenderTargetRenderMode>() ) return;
// extremely cringe but the load placed on the GPU causes a nasty issue
vkDeviceWaitIdle(ext::vulkan::device.logicalDevice);
auto& metadata = this->getComponent<ext::VoxelizerSceneBehavior::Metadata>();
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
@ -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<float>(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<size_t>(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<uint32_t>( metadata.voxelSize.x, std::max<uint32_t>(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);

View File

@ -24,7 +24,7 @@ namespace ext {
uf::stl::vector<VkImageView> views;
#endif
size_t cascades = 0;
size_t regions = 0;
float cascadePower = 0;
float granularity = 0;
float voxelizeScale = 0;

View File

@ -16,6 +16,7 @@
#include <uf/utils/math/physics/broadphase/bvh.h>
#include <uf/engine/ext.h>
#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<size_t>(128);
size_t maxTextures3D = uf::config["engine"]["scenes"]["textures"]["max"]["3D"].as<size_t>(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<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max() };
pod::Vector3f max = {-std::numeric_limits<float>::max(),-std::numeric_limits<float>::max(),-std::numeric_limits<float>::max() };
bool initialized = false; // could probably just check if min/max are not max/-max'd
};
uf::stl::unordered_map<int32_t, RegionBounds> sectors;
uf::stl::unordered_map<int32_t, uint32_t> 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<uf::Object>();
auto& transform = entity.getComponent<pod::Transform<>>();
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
}

View File

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

View File

@ -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<VkSemaphore>, waitSemaphores);
STATIC_THREAD_LOCAL(uf::stl::vector<VkPipelineStageFlags>, 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;

View File

@ -342,20 +342,20 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
size_t maxTextures2D = uf::config["engine"]["scenes"]["textures"]["max"]["2D"].as<size_t>(512);
size_t maxTexturesCube = uf::config["engine"]["scenes"]["textures"]["max"]["cube"].as<size_t>(128);
size_t maxTextures3D = uf::config["engine"]["scenes"]["textures"]["max"]["3D"].as<size_t>(128);
size_t maxCascades = uf::config["engine"]["scenes"]["vxgi"]["cascades"].as<size_t>(16);
size_t maxRegions = uf::config["engine"]["scenes"]["vxgi"]["regions"].as<size_t>(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;

View File

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

View File

@ -8,7 +8,7 @@ namespace {
VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
) {
uf::stl::unordered_set<VkImage> 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 );
}
}
}

View File

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

View File

@ -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({