diff --git a/bin/data/config.json b/bin/data/config.json index eeb969ab..c8e01f76 100644 --- a/bin/data/config.json +++ b/bin/data/config.json @@ -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, diff --git a/bin/data/entities/light.json b/bin/data/entities/light.json index 172f76ee..35efce45 100644 --- a/bin/data/entities/light.json +++ b/bin/data/entities/light.json @@ -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 diff --git a/bin/data/shaders/display/vxgi/comp.glsl b/bin/data/shaders/display/vxgi/comp.glsl index fce6de7e..87950ef5 100644 --- a/bin/data/shaders/display/vxgi/comp.glsl +++ b/bin/data/shaders/display/vxgi/comp.glsl @@ -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; diff --git a/bin/data/shaders/display/vxgi/mip.gaussian.comp.glsl b/bin/data/shaders/display/vxgi/mip.gaussian.comp.glsl deleted file mode 100644 index eca7cd3c..00000000 --- a/bin/data/shaders/display/vxgi/mip.gaussian.comp.glsl +++ /dev/null @@ -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)); -} \ No newline at end of file diff --git a/bin/data/shaders/display/vxgi/mips.comp.glsl b/bin/data/shaders/display/vxgi/mips.comp.glsl new file mode 100644 index 00000000..d4b00c93 --- /dev/null +++ b/bin/data/shaders/display/vxgi/mips.comp.glsl @@ -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(); + } +} \ No newline at end of file diff --git a/bin/data/shaders/graph/voxelize/frag.glsl b/bin/data/shaders/graph/voxelize/frag.glsl index 7ae35cec..6fb0022c 100644 --- a/bin/data/shaders/graph/voxelize/frag.glsl +++ b/bin/data/shaders/graph/voxelize/frag.glsl @@ -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); + } } \ No newline at end of file diff --git a/engine/inc/uf/engine/graph/scene.inl b/engine/inc/uf/engine/graph/scene.inl index d9fdef51..bb8155d3 100644 --- a/engine/inc/uf/engine/graph/scene.inl +++ b/engine/inc/uf/engine/graph/scene.inl @@ -9,6 +9,8 @@ namespace pod { uf::stl::vector normal; uf::stl::vector radiance; uf::stl::vector output; + + uf::stl::vector outputMipmaps; } voxels; }; } \ No newline at end of file diff --git a/engine/src/engine/ext/light/behavior.cpp b/engine/src/engine/ext/light/behavior.cpp index 283c17d7..6080ff33 100644 --- a/engine/src/engine/ext/light/behavior.cpp +++ b/engine/src/engine/ext/light/behavior.cpp @@ -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(); // 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; diff --git a/engine/src/engine/ext/scene/behavior.cpp b/engine/src/engine/ext/scene/behavior.cpp index ca029d60..d4b2769c 100644 --- a/engine/src/engine/ext/scene/behavior.cpp +++ b/engine/src/engine/ext/scene/behavior.cpp @@ -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 ) { diff --git a/engine/src/engine/ext/voxelizer/behavior.cpp b/engine/src/engine/ext/voxelizer/behavior.cpp index 442c4472..0efd0b2d 100644 --- a/engine/src/engine/ext/voxelizer/behavior.cpp +++ b/engine/src/engine/ext/voxelizer/behavior.cpp @@ -19,11 +19,25 @@ #include #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(); auto& metadataJson = this->getComponent(); @@ -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(512); size_t maxTextures2D = uf::config["engine"]["scenes"]["textures"]["max"]["2D"].as(512); size_t maxTexturesCube = uf::config["engine"]["scenes"]["textures"]["max"]["cube"].as(128); size_t maxTextures3D = uf::config["engine"]["scenes"]["textures"]["max"]["3D"].as(1); + size_t maxCascades = uf::config["engine"]["scenes"]["vxgi"]["cascades"].as(16); + size_t 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(); + + 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(); + + 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(); 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() ) continue; - auto& graphic = entity->getComponent(); - 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(); + 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(); - for ( auto& t : sceneTextures.voxels.output ) { + auto& metadata = this->getComponent(); + 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; diff --git a/engine/src/engine/ext/voxelizer/behavior.h b/engine/src/engine/ext/voxelizer/behavior.h index 7eddde44..cd949e46 100644 --- a/engine/src/engine/ext/voxelizer/behavior.h +++ b/engine/src/engine/ext/voxelizer/behavior.h @@ -6,6 +6,7 @@ #include #include #include +#include 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 views; size_t cascades = 0; float cascadePower = 0; diff --git a/engine/src/engine/scene/scene.cpp b/engine/src/engine/scene/scene.cpp index 3bd0d9f4..1d16e6c6 100644 --- a/engine/src/engine/scene/scene.cpp +++ b/engine/src/engine/scene/scene.cpp @@ -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(); } - +*/ #if !UF_SCENE_GLOBAL_GRAPH auto& metadata = this->getComponent(); #endif diff --git a/engine/src/ext/vulkan/rendermode.cpp b/engine/src/ext/vulkan/rendermode.cpp index 516efba7..c7d8a374 100644 --- a/engine/src/ext/vulkan/rendermode.cpp +++ b/engine/src/ext/vulkan/rendermode.cpp @@ -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() { diff --git a/engine/src/ext/vulkan/rendermodes/deferred.cpp b/engine/src/ext/vulkan/rendermodes/deferred.cpp index 9896e211..a0f62322 100644 --- a/engine/src/ext/vulkan/rendermodes/deferred.cpp +++ b/engine/src/ext/vulkan/rendermodes/deferred.cpp @@ -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 = ""; diff --git a/engine/src/ext/vulkan/rendermodes/rendertarget.cpp b/engine/src/ext/vulkan/rendermodes/rendertarget.cpp index b09d4380..b019eded 100644 --- a/engine/src/ext/vulkan/rendermodes/rendertarget.cpp +++ b/engine/src/ext/vulkan/rendermodes/rendertarget.cpp @@ -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() && !metadata.json["shaders"].as() ) { + } else if ( metadata.json["shaders"].is() && !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(); } 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(512); - size_t maxTextures2D = uf::config["engine"]["scenes"]["textures"]["max"]["2D"].as(512); - size_t maxTexturesCube = uf::config["engine"]["scenes"]["textures"]["max"]["cube"].as(128); - size_t maxTextures3D = uf::config["engine"]["scenes"]["textures"]["max"]["3D"].as(1); - size_t maxCascades = uf::config["engine"]["scenes"]["vxgi"]["cascades"].as(16); - - 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(); - - 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() { diff --git a/engine/src/ext/vulkan/texture.cpp b/engine/src/ext/vulkan/texture.cpp index ce6c8d1d..dfb2bee4 100644 --- a/engine/src/ext/vulkan/texture.cpp +++ b/engine/src/ext/vulkan/texture.cpp @@ -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(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); + } } }