alleged optimizations to vxgi (maybe), fixed crash with enabling shadowmapped lights (but they're broken still for some reason)

This commit is contained in:
ecker 2026-05-08 21:09:22 -05:00
parent 682dd2c794
commit a10ed8bc60
16 changed files with 444 additions and 216 deletions

View File

@ -4,13 +4,13 @@
"start": "StartMenu",
"matrix": { "reverseInfinite": true },
"meshes": { "interleaved": false },
"lights": { "enabled": false,
"lights": { "enabled": true,
"useLightmaps": false,
"max": 16,
"max": 32,
"shadows": {
"enabled": false,
"enabled": false, // to-do: fix
"update": 4,
"max": 16,
"max": 8,
"samples": 2
},
"bloom": {
@ -29,13 +29,14 @@
}
},
"vxgi": {
// "limiter": 0,
"limiter": 0.0125,
"limiter": 0,
// "limiter": 0.0125,
// "limiter": 5,
"size": 256,
"dispatch": 16,
"cascades": 3,
"cascadePower": 2,
"granularity": 10,
"cascadePower": 1.5,
"granularity": 32,
"voxelizeScale": 1,
"occlusionFalloff": 2,
"traceStartOffsetFactor": 1,
@ -117,7 +118,7 @@
"gui": true,
"vsync": true, // vsync on vulkan side rather than engine-side
"hdr": true,
"vxgi": true,
"vxgi": false,
"culling": false,
"bloom": false,
"dof": false,

View File

@ -8,10 +8,10 @@
},
"system": {
"renderer": {
// "limiter": 128
"limiter": 128
// "mode": "round robin"
// "mode": "once"
"mode": "in-range"
// "mode": "in-range"
},
"hot reload": {
"enabled": true

View File

@ -104,7 +104,7 @@ void main() {
vec4 A = imageLoad(voxelOutput[CASCADE], ivec3(tUvw) );
#else
vec4 A = unpackUnorm4x8(imageLoad(voxelRadiance[CASCADE], ivec3(tUvw)).r);
A.a = 1.0;
A.a = float(uint(A.a * 255.0 + 0.5) & 0xF) / 15.0;
#endif
surface.material.albedo = A;

View File

@ -1,71 +0,0 @@
#version 450
#pragma shader_stage(compute)
#extension GL_EXT_samplerless_texture_functions : require
layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in;
layout (constant_id = 0) const uint CASCADES = 16;
layout (constant_id = 1) const uint MIPS = 16;
layout( push_constant ) uniform PushBlock {
uint cascade;
uint mip;
} PushConstant;
#if VXGI_HDR
layout (binding = 1, rgba32f) uniform volatile coherent image3D voxelRadiance[CASCADES * MIPS];
#else
layout (binding = 1, rgba16f) uniform volatile coherent image3D voxelRadiance[CASCADES * MIPS];
#endif
const float gaussianWeights[] = {
//Top slice
1 / 64.0f,
1 / 32.0f,
1 / 64.0f,
1 / 32.0f,
1 / 16.0f,
1 / 32.0f,
1 / 64.0f,
1 / 32.0f,
1 / 64.0f,
//Center slice
1 / 32.0f,
1 / 16.0f,
1 / 32.0f,
1 / 16.0f,
1 / 4.0f,
1 / 16.0f,
1 / 32.0f,
1 / 16.0f,
1 / 32.0f,
//Bottom slice
1 / 64.0f,
1 / 32.0f,
1 / 64.0f,
1 / 32.0f,
1 / 16.0f,
1 / 32.0f,
1 / 64.0f,
1 / 32.0f,
1 / 64.0f,
};
void main() {
const ivec3 inUVW = ivec3(gl_GlobalInvocationID.xyz) * 2;
const ivec3 outUVW = ivec3(gl_GlobalInvocationID.xyz);
const uint CASCADE_IN = PushConstant.cascade * CASCADES + PushConstant.mip;
const uint CASCADE_OUT = PushConstant.cascade * CASCADES + (PushConstant.mip + 1);
vec4 color = vec4(0);
for ( int z = -1; z <= 1; ++z ) {
for ( int y = -1; y <= 1; ++y ) {
for ( int x = -1; x <= 1; ++x ) {
color += imageLoad( voxelRadiance[CASCADE_IN], inUVW + ivec3(x,y,z) ) * gaussianWeights[x + 1 + (y + 1) * 3 + (z + 1) * 9];
}
}
}
imageStore(voxelRadiance[CASCADE_OUT], ivec3(outUVW), vec4(color));
}

View File

@ -0,0 +1,165 @@
#version 450
#pragma shader_stage(compute)
#extension GL_KHR_shader_subgroup_quad : require
#extension GL_KHR_shader_subgroup_arithmetic : require
#extension GL_EXT_samplerless_texture_functions : enable
#define COMPUTE 1
#define SPD 1
#include "../../common/macros.h"
#include "../../common/structs.h"
#include "../../common/functions.h"
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 = 1) const uint MIPS = 9; // 256^3 texture = 9 mips
layout(push_constant) uniform PushBlock {
uint mips;
uint cascade;
uint numWorkGroups;
uint workGroupOffset;
} PushConstant_;
layout (binding = 0) uniform sampler3D voxelRadiance[CASCADES];
layout (binding = 1, rgba8) coherent uniform image3D voxelMips[CASCADES * (MIPS - 1)];
layout (binding = 2, std430) buffer AtomicCounter {
uint counter;
} spdCounter;
// 8^3 = 512 threads
shared vec4 s_colorAlpha[512];
shared uint s_isLastWG;
vec4 reduce8(vec4 v[8]) {
vec3 color = vec3(0.0);
float alpha = 0.0;
for( int i = 0; i < 8; ++i ) {
float a = float(uint(v[i].a * 255.0 + 0.5) & 0xF) / 15.0;
color += v[i].rgb * a;
alpha += a;
}
if ( alpha > 0.001 ) color /= alpha;
alpha /= 8.0;
uint lum4 = uint(clamp(luma(color), 0.0, 1.0) * 15.0) & 0xF;
uint alpha4 = uint(clamp(alpha, 0.0, 1.0) * 15.0) & 0xF;
return vec4(color, float((lum4 << 4) | alpha4) / 255.0);
}
ivec3 index3D(uint idx, uint sizeX, uint sizeY) {
return ivec3( idx % sizeX, (idx / sizeX) % sizeY, idx / (sizeX * sizeY) );
}
vec4 reduceFromShared( uint lid, uint dst ) {
ivec3 pos = index3D(lid, dst, dst) * 2;
vec4 v[8];
uint src = dst * 2;
for ( int z = 0; z < 2; ++z ) {
for ( int y = 0; y < 2; ++y ) {
for ( int x = 0; x < 2; ++x ) {
ivec3 p = pos + ivec3(x,y,z);
uint flatIdx = p.x + p.y * src + p.z * src * src;
v[x + y*2 + z*4] = s_colorAlpha[flatIdx];
}
}
}
return reduce8(v);
}
void main() {
uint lid = gl_LocalInvocationIndex;
ivec3 wgID = ivec3(gl_WorkGroupID);
// mip 0 => 1
if ( 1 < PushConstant_.mips ) {
ivec3 gid = wgID * 8 + index3D(lid, 8, 8);
ivec3 pos = gid * 2;
vec4 v[8];
v[0] = texelFetch(voxelRadiance[PushConstant_.cascade], pos + ivec3(0,0,0), 0);
v[1] = texelFetch(voxelRadiance[PushConstant_.cascade], pos + ivec3(1,0,0), 0);
v[2] = texelFetch(voxelRadiance[PushConstant_.cascade], pos + ivec3(0,1,0), 0);
v[3] = texelFetch(voxelRadiance[PushConstant_.cascade], pos + ivec3(1,1,0), 0);
v[4] = texelFetch(voxelRadiance[PushConstant_.cascade], pos + ivec3(0,0,1), 0);
v[5] = texelFetch(voxelRadiance[PushConstant_.cascade], pos + ivec3(1,0,1), 0);
v[6] = texelFetch(voxelRadiance[PushConstant_.cascade], pos + ivec3(0,1,1), 0);
v[7] = texelFetch(voxelRadiance[PushConstant_.cascade], pos + ivec3(1,1,1), 0);
vec4 color = reduce8(v);
imageStore(voxelMips[PushConstant_.cascade + PushConstant_.mips * 0], gid, color);
s_colorAlpha[lid] = color;
}
memoryBarrierShared(); barrier();
// mip 1 => 2 => 3 => 4
int threads[3] = { 64, 8, 1 };
int edges[3] = { 4, 2, 1 };
{
for ( int i = 0; i < 3; ++i ) {
uint mipLevel = i + 2;
if ( mipLevel < PushConstant_.mips ) {
if ( lid < threads[i] ) {
vec4 color = reduceFromShared(lid, edges[i]);
ivec3 pos = wgID * edges[i] + index3D(lid, edges[i], edges[i]);
imageStore(voxelMips[PushConstant_.cascade + PushConstant_.mips * (mipLevel - 1)], pos, color);
s_colorAlpha[lid] = color;
}
}
memoryBarrierShared(); barrier();
}
}
if ( PushConstant_.mips <= 5 ) return; // bail if 16^3 or smaller
// atomic sync
{
if ( lid == 0 ) {
uint ticket = atomicAdd(spdCounter.counter, 1);
s_isLastWG = (ticket == PushConstant_.numWorkGroups - 1) ? 1 : 0;
}
memoryBarrierShared(); barrier();
if ( s_isLastWG == 0 ) return; // use last workgroup
if ( lid == 0 ) spdCounter.counter = 0; // reset for next frame
}
// mip 4 => 5
if ( 5 < PushConstant_.mips ) {
ivec3 pos = index3D(lid, 8, 8) * 2;
uint m4_idx = PushConstant_.cascade + PushConstant_.mips * 3;
vec4 v[8];
v[0] = imageLoad(voxelMips[m4_idx], pos + ivec3(0,0,0));
v[1] = imageLoad(voxelMips[m4_idx], pos + ivec3(1,0,0));
v[2] = imageLoad(voxelMips[m4_idx], pos + ivec3(0,1,0));
v[3] = imageLoad(voxelMips[m4_idx], pos + ivec3(1,1,0));
v[4] = imageLoad(voxelMips[m4_idx], pos + ivec3(0,0,1));
v[5] = imageLoad(voxelMips[m4_idx], pos + ivec3(1,0,1));
v[6] = imageLoad(voxelMips[m4_idx], pos + ivec3(0,1,1));
v[7] = imageLoad(voxelMips[m4_idx], pos + ivec3(1,1,1));
vec4 color = reduce8(v);
imageStore(voxelMips[PushConstant_.cascade + PushConstant_.mips * 4], pos / 2, color);
s_colorAlpha[lid] = color;
}
memoryBarrierShared(); barrier();
// mip 5 => 6 => 7 => 8
for ( int i = 0; i < 3; ++i ) {
uint mipLevel = i + 6; // Mips 6, 7, 8
if ( mipLevel < PushConstant_.mips ) {
if ( lid < threads[i] ) {
vec4 color = reduceFromShared(lid, edges[i]);
ivec3 pos = index3D(lid, edges[i], edges[i]);
imageStore(voxelMips[PushConstant_.cascade + PushConstant_.mips * (mipLevel - 1)], pos, color);
s_colorAlpha[lid] = color;
}
}
memoryBarrierShared(); barrier();
}
}

View File

@ -116,13 +116,20 @@ void main() {
const ivec3 uvw = ivec3(P * imageSize(voxelOutput[CASCADE]));
uint packedId = ( instanceID + 1 ) << 16 | ( drawID + 1 );
imageAtomicMax(voxelId[CASCADE], ivec3(uvw), packedId);
vec2 N_E = encodeNormals( normalize( N ) );
uint packedNormal = packHalf2x16(N_E);
imageAtomicMin(voxelNormal[CASCADE], uvw, packedNormal);
uint packedRadiance = packUnorm4x8(vec4(A.rgb, luminance(A.rgb)));
imageAtomicMax(voxelRadiance[CASCADE], uvw, packedRadiance);
{
uint packedId = ( instanceID + 1 ) << 16 | ( drawID + 1 );
imageAtomicMax(voxelId[CASCADE], ivec3(uvw), packedId);
}
{
vec2 N_E = encodeNormals( normalize( N ) );
uint packedNormal = packHalf2x16(N_E);
imageAtomicMin(voxelNormal[CASCADE], 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;
float packedLumaAlpha = float((l << 4) | a) / 255.0;
uint packedRadiance = packUnorm4x8(vec4(A.rgb, packedLumaAlpha));
imageAtomicMax(voxelRadiance[CASCADE], uvw, packedRadiance);
}
}

View File

@ -9,6 +9,8 @@ namespace pod {
uf::stl::vector<uf::renderer::Texture3D> normal;
uf::stl::vector<uf::renderer::Texture3D> radiance;
uf::stl::vector<uf::renderer::Texture3D> output;
uf::stl::vector<uf::renderer::Texture3D> outputMipmaps;
} voxels;
};
}

View File

@ -99,6 +99,7 @@ void ext::LightBehavior::initialize( uf::Object& self ) {
uf::stl::string name = "RT:" + std::to_string((int) this->getUid());
renderMode.blitter.process = false;
renderMode.execute = false;
renderMode.width = size.x;
renderMode.height = size.y;
renderMode.metadata.name = name;
@ -182,6 +183,7 @@ void ext::LightBehavior::tick( uf::Object& self ) {
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
// enable renderer every X seconds
if ( metadata.renderer.limiter > 0 ) {
UF_MSG_DEBUG("limiter={}, timer={}, execute={}", metadata.renderer.limiter, metadata.renderer.timer, renderMode.execute );
if ( metadata.renderer.timer > metadata.renderer.limiter ) {
metadata.renderer.timer = 0;
renderMode.execute = true;

View File

@ -1111,16 +1111,22 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, uf::renderer::Graphic
// bind scene textures
for ( auto& key : storage.texture2Ds.keys ) textures2D.emplace_back().aliasTexture( storage.texture2Ds.map[key] );
// bind shadow maps
for ( auto& texture : storage.shadow2Ds ) textures2D.emplace_back().aliasTexture(texture);
for ( auto& texture : storage.shadowCubes ) texturesCube.emplace_back().aliasTexture(texture);
size_t indexSkybox = 0;
size_t indexNoise = 0;
// bind skybox
size_t indexSkybox = texturesCube.size();
texturesCube.emplace_back().aliasTexture(sceneTextures.skybox);
// bind noise texture
size_t indexNoise = textures3D.size();
textures3D.emplace_back().aliasTexture(sceneTextures.noise);
// bind only if this is the deferred rendermode
if ( shaderPipeline == "deferred" ) {
// bind shadow maps
for ( auto& texture : storage.shadow2Ds ) textures2D.emplace_back().aliasTexture(texture);
for ( auto& texture : storage.shadowCubes ) texturesCube.emplace_back().aliasTexture(texture);
// bind skybox
indexSkybox = texturesCube.size();
texturesCube.emplace_back().aliasTexture(sceneTextures.skybox);
// bind noise texture
indexNoise = textures3D.size();
textures3D.emplace_back().aliasTexture(sceneTextures.noise);
}
// attach VXGI voxels
if ( uf::renderer::settings::pipelines::vxgi ) {

View File

@ -19,11 +19,25 @@
#include <uf/engine/ext.h>
#define ALIAS_OUTPUT_TO_RADIANCE 1
#define COMPUTE_MIPMAP_GENERATION 1
namespace {
struct AtomicCounter {
uint32_t counter;
};
struct PushConstants {
uint32_t mips;
uint32_t cascade;
uint32_t numWorkGroups;
uint32_t workGroupOffset;
};
}
UF_BEHAVIOR_REGISTER_CPP(ext::VoxelizerSceneBehavior)
UF_BEHAVIOR_TRAITS_CPP(ext::VoxelizerSceneBehavior, ticks = true, renders = false, thread = "")
#define this (&self)
void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) {
if ( this->getName() == "Main Menu" ) return; // do not setup
#if UF_USE_VULKAN
auto& metadata = this->getComponent<ext::VoxelizerSceneBehavior::Metadata>();
auto& metadataJson = this->getComponent<uf::Serializer>();
@ -34,30 +48,29 @@ 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 ) {
const bool HDR = false;
auto& id = sceneTextures.voxels.id.emplace_back();
id.sampler.descriptor.filter.min = uf::renderer::enums::Filter::NEAREST;
id.sampler.descriptor.filter.mag = uf::renderer::enums::Filter::NEAREST;
id.mips = 0;
id.fromBuffers( NULL, 0, uf::renderer::enums::Format::R32_UINT, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL );
auto& normal = sceneTextures.voxels.normal.emplace_back();
normal.mips = 0;
normal.fromBuffers( NULL, 0, uf::renderer::enums::Format::R32_UINT, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL );
auto& radiance = sceneTextures.voxels.radiance.emplace_back();
radiance.fromBuffers( NULL, 0, uf::renderer::enums::Format::R32_UINT, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL );
radiance.mips = ALIAS_OUTPUT_TO_RADIANCE ? mips : 0;
radiance.fromBuffers( NULL, 0, uf::renderer::enums::Format::R32_UINT, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL, ALIAS_OUTPUT_TO_RADIANCE ? VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT : VkImageCreateFlags{} );
auto& output = sceneTextures.voxels.output.emplace_back();
/*
if ( metadata.filtering == "NEAREST" ) {
output.sampler.descriptor.filter.min = uf::renderer::enums::Filter::NEAREST;
output.sampler.descriptor.filter.mag = uf::renderer::enums::Filter::NEAREST;
}
*/
// output.fromBuffers( NULL, 0, uf::renderer::settings::pipelines::hdr ? uf::renderer::enums::Format::HDR : uf::renderer::enums::Format::SDR, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL );
output.mips = mips;
#if ALIAS_OUTPUT_TO_RADIANCE
output.aliasTexture( radiance );
{
output.aliasTexture( radiance );
VkImageViewCreateInfo viewCreateInfo = {};
viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewCreateInfo.image = radiance.image;
@ -66,16 +79,39 @@ void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) {
viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
viewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewCreateInfo.subresourceRange.baseMipLevel = 0;
viewCreateInfo.subresourceRange.levelCount = radiance.mips;
viewCreateInfo.subresourceRange.levelCount = mips;
viewCreateInfo.subresourceRange.baseArrayLayer = 0;
viewCreateInfo.subresourceRange.layerCount = 1;
VK_CHECK_RESULT(vkCreateImageView(uf::renderer::device.logicalDevice, &viewCreateInfo, nullptr, &output.view));
VK_REGISTER_HANDLE( output.view );
metadata.views.emplace_back( output.view );
}
#else
output.fromBuffers( NULL, 0, uf::renderer::enums::Format::R8G8B8A8_UNORM, metadata.voxelSize.x, metadata.voxelSize.y, metadata.voxelSize.z, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL );
#endif
#if COMPUTE_MIPMAP_GENERATION
for ( auto i = 1; i < mips; ++i ) {
auto& mip = sceneTextures.voxels.outputMipmaps.emplace_back();
mip.aliasTexture( output );
VkImageViewCreateInfo viewCreateInfo = {};
viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewCreateInfo.image = output.image;
viewCreateInfo.viewType = output.viewType;
viewCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
viewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewCreateInfo.subresourceRange.baseMipLevel = i;
viewCreateInfo.subresourceRange.levelCount = 1;
viewCreateInfo.subresourceRange.baseArrayLayer = 0;
viewCreateInfo.subresourceRange.layerCount = 1;
VK_CHECK_RESULT(vkCreateImageView(uf::renderer::device.logicalDevice, &viewCreateInfo, nullptr, &mip.view));
VK_REGISTER_HANDLE( mip.view );
metadata.views.emplace_back( mip.view );
}
#endif
}
// initialize render mode
{
@ -87,6 +123,7 @@ void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) {
renderMode.metadata.name = metadata.renderModeName;
if ( uf::renderer::settings::experimental::registerRenderMode ) uf::renderer::addRenderMode( &renderMode, metadata.renderModeName );
auto& blitter = renderMode.blitter;
renderMode.metadata.type = uf::renderer::settings::pipelines::names::vxgi;
renderMode.metadata.pipeline = uf::renderer::settings::pipelines::names::vxgi;
if ( uf::renderer::settings::pipelines::culling ) {
@ -95,38 +132,85 @@ 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.blitter.device = &uf::renderer::device;
renderMode.width = metadata.fragmentSize.x;
renderMode.height = metadata.fragmentSize.y;
// renderMode.metadata.limiter.frequency = metadata.limiter.frequency;
blitter.device = &uf::renderer::device;
blitter.material.device = &uf::renderer::device;
uf::stl::string computeShaderFilename = "/shaders/display/vxgi/comp.spv";
if ( renderMode.metadata.samples > 1 ) {
computeShaderFilename = uf::string::replace( computeShaderFilename, "comp", "msaa.comp" );
}
renderMode.metadata.json["shaders"]["compute"] = computeShaderFilename;
renderMode.blitter.descriptor.renderMode = metadata.renderModeName;
renderMode.blitter.descriptor.subpass = -1;
renderMode.blitter.descriptor.bind.width = metadata.voxelSize.x;
renderMode.blitter.descriptor.bind.height = metadata.voxelSize.y;
renderMode.blitter.descriptor.bind.depth = metadata.voxelSize.z;
renderMode.blitter.descriptor.bind.point = VK_PIPELINE_BIND_POINT_COMPUTE;
renderMode.blitter.process = true;
blitter.descriptor.renderMode = metadata.renderModeName;
blitter.descriptor.subpass = -1;
blitter.descriptor.bind.width = metadata.voxelSize.x;
blitter.descriptor.bind.height = metadata.voxelSize.y;
blitter.descriptor.bind.depth = metadata.voxelSize.z;
blitter.descriptor.bind.point = VK_PIPELINE_BIND_POINT_COMPUTE;
blitter.process = true;
size_t maxLights = uf::config["engine"]["scenes"]["lights"]["max"].as<size_t>(512);
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 maxMips = uf::vector::mips( pod::Vector3ui{ 256, 256, 256 } ); // log2(256) = 9
for ( size_t i = 0; i < maxTextures2D; ++i ) renderMode.blitter.material.textures.emplace_back().aliasTexture(uf::renderer::Texture2D::empty);
for ( size_t i = 0; i < maxTexturesCube; ++i ) renderMode.blitter.material.textures.emplace_back().aliasTexture(uf::renderer::TextureCube::empty);
for ( size_t i = 0; i < maxTextures3D; ++i ) renderMode.blitter.material.textures.emplace_back().aliasTexture(uf::renderer::Texture3D::empty);
for ( auto& t : sceneTextures.voxels.id ) renderMode.blitter.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.normal ) renderMode.blitter.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.radiance ) renderMode.blitter.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.output ) renderMode.blitter.material.textures.emplace_back().aliasTexture(t);
renderMode.metadata.json["shaders"] = true;
{
blitter.material.attachShader( uf::io::root+"/shaders/display/vxgi/comp.spv", uf::renderer::enums::Shader::COMPUTE, "" );
auto& shader = blitter.material.getShader("compute", "");
shader.setSpecializationConstants({
{ "TEXTURES", maxTextures2D },
{ "CUBEMAPS", maxTexturesCube },
{ "CASCADES", maxCascades },
});
shader.setDescriptorCounts({
{ "samplerTextures", maxTextures2D },
{ "samplerCubemaps", maxTexturesCube },
{ "voxelId", maxCascades },
{ "voxelNormal", maxCascades },
{ "voxelRadiance", maxCascades },
{ "voxelOutput", maxCascades },
});
auto& scene = uf::scene::getCurrentScene();
auto& sceneTextures = scene.getComponent<pod::SceneTextures>();
shader.textures.clear();
for ( auto& t : sceneTextures.voxels.id ) shader.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.normal ) shader.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.radiance ) shader.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.output ) shader.textures.emplace_back().aliasTexture(t);
}
#if COMPUTE_MIPMAP_GENERATION
{
blitter.material.attachShader( uf::io::root+"/shaders/display/vxgi/mips.comp.spv", uf::renderer::enums::Shader::COMPUTE, "mipmap" );
auto& shader = blitter.material.getShader("compute", "mipmap");
shader.setSpecializationConstants({
{ "TEXTURES", maxTextures2D },
{ "CUBEMAPS", maxTexturesCube },
{ "CASCADES", maxCascades },
{ "MIPS", maxMips },
});
shader.setDescriptorCounts({
{ "voxelRadiance", maxCascades },
{ "voxelMips", maxCascades * (maxMips - 1) },
});
auto& scene = uf::scene::getCurrentScene();
auto& sceneTextures = scene.getComponent<pod::SceneTextures>();
shader.textures.clear();
for ( auto& t : sceneTextures.voxels.output ) shader.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.outputMipmaps ) shader.textures.emplace_back().aliasTexture(t);
metadata.atomicCounter.initialize( (const void*) nullptr, sizeof(::AtomicCounter) * 1, uf::renderer::enums::Buffer::STORAGE );
shader.aliasBuffer("atomicCounter", metadata.atomicCounter);
}
#endif
renderMode.bindCallback( renderMode.CALLBACK_BEGIN, [&]( VkCommandBuffer commandBuffer, size_t _ ){
// clear textures
@ -147,12 +231,43 @@ void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) {
//
renderMode.bindCallback( renderMode.CALLBACK_END, [&]( VkCommandBuffer commandBuffer, size_t _ ){
// parse voxel lighting
if ( renderMode.blitter.initialized ) {
auto& pipeline = renderMode.blitter.getPipeline();
pipeline.record(renderMode.blitter, commandBuffer);
if ( blitter.initialized ) {
auto descriptor = blitter.descriptor;
//descriptor.pipeline = "lighting";
auto& pipeline = blitter.getPipeline( descriptor );
pipeline.record( blitter, commandBuffer );
}
// generate mipmaps
#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 ) {
vkCmdFillBuffer(commandBuffer, metadata.atomicCounter.buffer, 0, 4, 0);
VkMemoryBarrier counterBarrier{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);
auto& pushConstant = shader.pushConstants.front().get<::PushConstants>();
pushConstant = {
.mips = mips,
.cascade = cascade,
.numWorkGroups = 0,
.workGroupOffset = 0,
};
auto descriptor = blitter.descriptor;
descriptor.pipeline = "mipmap";
auto& pipeline = blitter.getPipeline( descriptor );
pipeline.record( blitter, commandBuffer );
}
}
#else
VkImageSubresourceRange subresourceRange = {};
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresourceRange.baseMipLevel = 0;
@ -160,22 +275,11 @@ void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) {
subresourceRange.layerCount = 1;
for ( auto& t : sceneTextures.voxels.output ) {
subresourceRange.levelCount = t.mips;
t.setImageLayout(
commandBuffer,
t.image,
t.layout,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
subresourceRange
);
t.setImageLayout( commandBuffer, t.image, t.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange );
t.generateMipmaps( commandBuffer, 0 );
t.setImageLayout(
commandBuffer,
t.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
t.layout,
subresourceRange
);
t.setImageLayout( commandBuffer, t.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, t.layout, subresourceRange );
}
#endif
});
}
#endif
@ -215,14 +319,22 @@ void ext::VoxelizerSceneBehavior::tick( uf::Object& self ) {
auto& controller = scene.getController();
auto& camera = scene.getCamera( controller ); // controller.getComponent<uf::Camera>();
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;
controllerPosition.x = floor(controllerPosition.x);
controllerPosition.y = floor(controllerPosition.y);
controllerPosition.z = floor(controllerPosition.z);
controllerPosition.x = std::floor(controllerPosition.x / voxelWorldSizeX) * voxelWorldSizeX;
controllerPosition.y = std::floor(controllerPosition.y / voxelWorldSizeY) * voxelWorldSizeY;
controllerPosition.z = std::floor(controllerPosition.z / voxelWorldSizeZ) * voxelWorldSizeZ;
controllerPosition += metadata.extents.min;
controllerPosition.x = floor(controllerPosition.x);
controllerPosition.y = floor(controllerPosition.y);
controllerPosition.z = -floor(controllerPosition.z);
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 min = metadata.extents.min + controllerPosition;
pod::Vector3f max = metadata.extents.max + controllerPosition;
@ -232,9 +344,9 @@ void ext::VoxelizerSceneBehavior::tick( uf::Object& self ) {
auto/*&*/ graph = scene.getGraph();
for ( auto entity : graph ) {
if ( !entity->hasComponent<uf::Graphic>() ) continue;
auto& graphic = entity->getComponent<uf::Graphic>();
if ( graphic.material.hasShader("geometry", uf::renderer::settings::pipelines::names::vxgi) ) {
auto& shader = graphic.material.getShader("geometry", uf::renderer::settings::pipelines::names::vxgi);
auto& blitter = entity->getComponent<uf::Graphic>();
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;
@ -278,17 +390,16 @@ void ext::VoxelizerSceneBehavior::tick( uf::Object& self ) {
}
void ext::VoxelizerSceneBehavior::render( uf::Object& self ){}
void ext::VoxelizerSceneBehavior::destroy( uf::Object& self ){
#if ALIAS_OUTPUT_TO_RADIANCE
auto& sceneTextures = this->getComponent<pod::SceneTextures>();
for ( auto& t : sceneTextures.voxels.output ) {
auto& metadata = this->getComponent<ext::VoxelizerSceneBehavior::Metadata>();
metadata.atomicCounter.destroy(false);
for ( auto& view : metadata.views ) {
ext::vulkan::mutex.lock();
auto& texture = uf::renderer::device.transient.textures.emplace_back();
ext::vulkan::mutex.unlock();
texture.device = &uf::renderer::device;
texture.view = t.view;
texture.view = view;
}
#endif
}
void ext::VoxelizerSceneBehavior::Metadata::serialize( uf::Object& self, uf::Serializer& serializer ) {
serializer["vxgi"]["size"] = /*this->*/voxelSize.x;

View File

@ -6,6 +6,7 @@
#include <uf/engine/scene/scene.h>
#include <uf/utils/math/vector.h>
#include <uf/utils/math/matrix.h>
#include <uf/utils/renderer/renderer.h>
namespace ext {
namespace VoxelizerSceneBehavior {
@ -17,6 +18,9 @@ namespace ext {
pod::Vector3ui voxelSize = { 0, 0, 0 };
pod::Vector3ui dispatchSize = { 0, 0, 0 };
uf::stl::string renderModeName = "VXGI";
uf::renderer::Buffer atomicCounter;
uf::stl::vector<VkImageView> views;
size_t cascades = 0;
float cascadePower = 0;

View File

@ -66,10 +66,11 @@ const uf::Entity& uf::Scene::getController() const {
uf::Camera& uf::Scene::getCamera(uf::Entity& controller) {
// ???
/*
if ( auto currentRenderMode = uf::renderer::getCurrentRenderMode(); currentRenderMode && !currentRenderMode->getName().empty() ) {
return controller.getComponent<uf::Camera>();
}
*/
#if !UF_SCENE_GLOBAL_GRAPH
auto& metadata = this->getComponent<uf::SceneBehavior::Metadata>();
#endif

View File

@ -232,7 +232,7 @@ void ext::vulkan::RenderMode::createCommandBuffers() {
graphics.emplace_back(&graphic);
}
this->synchronize();
// this->synchronize();
// bindPipelines( graphics );
//lockMutex();
@ -327,7 +327,7 @@ void ext::vulkan::RenderMode::bindPipelines() {
// if ( graphic.descriptor.renderMode != "" && graphic.descriptor.renderMode != this->getName() ) continue;
graphics.emplace_back(&graphic);
}
this->synchronize();
//this->synchronize();
this->bindPipelines( graphics );
this->execute = true;
}
@ -432,7 +432,7 @@ void ext::vulkan::RenderMode::tick() {
cleanupAllCommands();
}
this->synchronize();
//this->synchronize();
if ( metadata.limiter.frequency > 0 ) {
if ( metadata.limiter.timer > metadata.limiter.frequency ) {
@ -446,7 +446,7 @@ void ext::vulkan::RenderMode::tick() {
}
void ext::vulkan::RenderMode::render() {
this->synchronize();
//this->synchronize();
}
void ext::vulkan::RenderMode::destroy() {

View File

@ -884,6 +884,12 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
#if 1
if ( settings::pipelines::deferred && DEFERRED_MODE == "compute" && blitter.material.hasShader(DEFERRED_MODE, "deferred") ) {
VkMemoryBarrier computeBarrier = {};
computeBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
computeBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
computeBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier( commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, &computeBarrier, 0, nullptr, 0, nullptr );
auto& shader = blitter.material.getShader(DEFERRED_MODE, "deferred");
ext::vulkan::GraphicDescriptor descriptor = blitter.descriptor;
descriptor.renderMode = "";

View File

@ -278,6 +278,7 @@ void ext::vulkan::RenderTargetRenderMode::initialize( Device& device ) {
else if ( sType == "geometry" ) type = ext::vulkan::enums::Shader::GEOMETRY;
else if ( sType == "compute" ) type = ext::vulkan::enums::Shader::COMPUTE;
}
if ( filename == "" ) return;
blitter.material.attachShader( uf::io::root+filename, type, pipeline );
});
} else if ( ext::json::isObject( metadata.json["shaders"] ) ) {
@ -295,11 +296,12 @@ void ext::vulkan::RenderTargetRenderMode::initialize( Device& device ) {
else if ( key == "fragment" ) type = ext::vulkan::enums::Shader::FRAGMENT;
else if ( key == "geometry" ) type = ext::vulkan::enums::Shader::GEOMETRY;
else if ( key == "compute" ) type = ext::vulkan::enums::Shader::COMPUTE;
if ( filename == "" ) return;
blitter.material.attachShader( uf::io::root+filename, type, pipeline );
});
} else if ( metadata.json["shaders"].is<bool>() && !metadata.json["shaders"].as<bool>() ) {
} else if ( metadata.json["shaders"].is<bool>() && !ext::json::isNull( metadata.json["shaders"] ) ) {
// do not attach if we're requesting no blitter shaders
blitter.process = false;
blitter.process = metadata.json["shaders"].as<bool>();
} else {
uf::stl::string vertexShaderFilename = uf::io::root+"/shaders/display/renderTarget/vert.spv";
uf::stl::string fragmentShaderFilename = uf::io::root+"/shaders/display/renderTarget/frag.spv"; {
@ -318,43 +320,10 @@ void ext::vulkan::RenderTargetRenderMode::initialize( Device& device ) {
}
if ( metadata.type == uf::renderer::settings::pipelines::names::vxgi ) {
auto& shader = blitter.material.getShader("compute");
size_t maxLights = uf::config["engine"]["scenes"]["lights"]["max"].as<size_t>(512);
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);
shader.setSpecializationConstants({
{ "TEXTURES", maxTextures2D },
{ "CUBEMAPS", maxTexturesCube },
{ "CASCADES", maxCascades },
});
shader.setDescriptorCounts({
{ "samplerTextures", maxTextures2D },
{ "samplerCubemaps", maxTexturesCube },
{ "voxelId", maxCascades },
{ "voxelNormal", maxCascades },
{ "voxelRadiance", maxCascades },
{ "voxelOutput", maxCascades },
});
auto& scene = uf::scene::getCurrentScene();
auto& sceneTextures = scene.getComponent<pod::SceneTextures>();
shader.textures.clear();
for ( auto& t : sceneTextures.voxels.id ) shader.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.normal ) shader.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.radiance ) shader.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.output ) shader.textures.emplace_back().aliasTexture(t);
// handled by its rendermode
} else if ( metadata.type == uf::renderer::settings::pipelines::names::rt ) {
#if 0
auto& shader = blitter.material.getShader("fragment");
shader.aliasAttachment("output", this);
#endif
} else {
// handled by its rendermode
} else {
auto& shader = blitter.material.getShader("fragment");
for ( auto i = 0; i < renderTarget.attachments.size(); ++i ) {
if ( !(renderTarget.attachments[i].descriptor.usage & VK_IMAGE_USAGE_SAMPLED_BIT) ) continue;
@ -398,7 +367,7 @@ void ext::vulkan::RenderTargetRenderMode::build( bool resized ) {
}
// (re)initialize pipelines
{
if ( blitter.process ) {
blitter.descriptor.bind.width = width;
blitter.descriptor.bind.height = height;
@ -408,6 +377,30 @@ void ext::vulkan::RenderTargetRenderMode::build( bool resized ) {
blitter.getPipeline( blitter.descriptor ).update( blitter, blitter.descriptor );
}
}
if ( metadata.type == uf::renderer::settings::pipelines::names::vxgi ) {
auto descriptor = blitter.descriptor;
//descriptor.pipeline = "lighting";
descriptor.subpass = -1;
descriptor.bind.point = VK_PIPELINE_BIND_POINT_COMPUTE;
if ( blitter.hasPipeline( descriptor ) ) {
blitter.getPipeline( descriptor ).update( blitter, descriptor );
} else {
blitter.initializePipeline( descriptor );
}
}
if ( metadata.type == uf::renderer::settings::pipelines::names::vxgi ) {
auto descriptor = blitter.descriptor;
descriptor.pipeline = "mipmap";
descriptor.subpass = -1;
descriptor.bind.point = VK_PIPELINE_BIND_POINT_COMPUTE;
if ( blitter.hasPipeline( descriptor ) ) {
blitter.getPipeline( descriptor ).update( blitter, descriptor );
} else {
blitter.initializePipeline( descriptor );
}
}
}
void ext::vulkan::RenderTargetRenderMode::tick() {

View File

@ -519,15 +519,16 @@ void ext::vulkan::Texture::fromBuffers(
if ( this->mips == 0 ) {
this->mips = 1;
// } else if ( this->depth == 1 ) {
} else {
// this->mips = static_cast<uint32_t>(std::floor(std::log2(std::max(texWidth, texHeight)))) + 1;
this->mips = uf::vector::mips( pod::Vector3ui{ texWidth, texHeight, texDepth } );
VkFormatProperties formatProperties;
vkGetPhysicalDeviceFormatProperties(device.physicalDevice, format, &formatProperties);
if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
this->mips = 1;
// VK_VALIDATION_MESSAGE("Texture image format {} does not support linear blitting", format);
// cringe override for 3D textures that explicitly request mipmapping
if ( texDepth <= 1 ) {
VkFormatProperties formatProperties;
vkGetPhysicalDeviceFormatProperties(device.physicalDevice, format, &formatProperties);
if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
this->mips = 1;
// VK_VALIDATION_MESSAGE("Texture image format {} does not support linear blitting", format);
}
}
}