From 3f40ca65f7a85a2412135f28c1e7f340960216f9 Mon Sep 17 00:00:00 2001 From: Artur Wasmut <awasmut@uni-koblenz.de> Date: Sun, 20 Jun 2021 18:10:48 +0200 Subject: [PATCH] finalize bloom stuff. --- include/vkcv/DescriptorWrites.hpp | 5 +- projects/bloom/resources/shaders/blur.comp | 70 ++++++-- projects/bloom/resources/shaders/comp.spv | Bin 2228 -> 0 bytes .../resources/shaders/compositeBloom.comp | 2 +- .../bloom/resources/shaders/upsample.comp | 45 +++++ projects/bloom/src/main.cpp | 162 +++++++++++++++--- src/vkcv/DescriptorManager.cpp | 5 +- 7 files changed, 242 insertions(+), 47 deletions(-) delete mode 100644 projects/bloom/resources/shaders/comp.spv create mode 100644 projects/bloom/resources/shaders/upsample.comp diff --git a/include/vkcv/DescriptorWrites.hpp b/include/vkcv/DescriptorWrites.hpp index 7cc76c69..f28a6c91 100644 --- a/include/vkcv/DescriptorWrites.hpp +++ b/include/vkcv/DescriptorWrites.hpp @@ -4,9 +4,12 @@ namespace vkcv { struct SampledImageDescriptorWrite { - inline SampledImageDescriptorWrite(uint32_t binding, ImageHandle image) : binding(binding), image(image) {}; + inline SampledImageDescriptorWrite(uint32_t binding, ImageHandle image, uint32_t mipLevel = 0, bool useGeneralLayout = false) + : binding(binding), image(image), mipLevel(mipLevel), useGeneralLayout(useGeneralLayout) {}; uint32_t binding; ImageHandle image; + uint32_t mipLevel; + bool useGeneralLayout; }; struct StorageImageDescriptorWrite { diff --git a/projects/bloom/resources/shaders/blur.comp b/projects/bloom/resources/shaders/blur.comp index 9fd2ccbf..51834627 100644 --- a/projects/bloom/resources/shaders/blur.comp +++ b/projects/bloom/resources/shaders/blur.comp @@ -1,37 +1,77 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable -layout(set=0, binding=0) uniform texture2D inImage; +layout(set=0, binding=0) uniform texture2D inBlurImage; layout(set=0, binding=1) uniform sampler inImageSampler; -layout(set=0, binding=2, r11f_g11f_b10f) uniform writeonly image2D outImage; +layout(set=0, binding=2, r11f_g11f_b10f) uniform writeonly image2D outBlurImage; layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; void main() { - if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))){ + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outBlurImage)))){ return; } - const int kernel_size = 10; - const float kernel_weight = (2 * kernel_size + 1) * (2 * kernel_size + 1); ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); - vec2 pixel_size = vec2(1.0f) / textureSize(sampler2D(inImage, inImageSampler), 0); + vec2 pixel_size = vec2(1.0f) / imageSize(outBlurImage); vec2 UV = pixel_coord.xy * pixel_size; + vec2 UV_offset = UV + 0.5f * pixel_size; - vec4 sampled_color = vec4(0.0f); + vec2 color_fetches[13] = { + // center neighbourhood (RED) + vec2(-1, 1), // LT + vec2(-1, -1), // LB + vec2( 1, -1), // RB + vec2( 1, 1), // RT - for(int i = -kernel_size; i <= kernel_size; i++) + vec2(-2, 2), // LT + vec2( 0, 2), // CT + vec2( 2, 2), // RT + + vec2(0 ,-2), // LC + vec2(0 , 0), // CC + vec2(2, 0), // CR + + vec2(-2, -2), // LB + vec2(0 , -2), // CB + vec2(2 , -2) // RB + }; + + float color_weights[13] = { + // 0.5f + 1.f/8.f, + 1.f/8.f, + 1.f/8.f, + 1.f/8.f, + + // 0.125f + 1.f/32.f, + 1.f/16.f, + 1.f/32.f, + + // 0.25f + 1.f/16.f, + 1.f/8.f, + 1.f/16.f, + + // 0.125f + 1.f/32.f, + 1.f/16.f, + 1.f/32.f + }; + + vec3 sampled_color = vec3(0.0f); + + for(uint i = 0; i < 13; i++) { - for(int j = -kernel_size; j <= kernel_size; j++) - { - vec2 sample_coord = UV + vec2(j, i) * pixel_size + 0.5f * pixel_size * sign(vec2(j, i)); - sampled_color.rgb += texture(sampler2D(inImage, inImageSampler), sample_coord).rgb; - } + vec2 color_fetch = UV_offset + color_fetches[i] * pixel_size; + vec3 color = texture(sampler2D(inBlurImage, inImageSampler), color_fetch).rgb; + color *= color_weights[i]; + sampled_color += color; } - sampled_color /= kernel_weight; - imageStore(outImage, pixel_coord, sampled_color); + imageStore(outBlurImage, pixel_coord, vec4(sampled_color, 1.f)); } \ No newline at end of file diff --git a/projects/bloom/resources/shaders/comp.spv b/projects/bloom/resources/shaders/comp.spv deleted file mode 100644 index e0112a50deceb3b818434636e194d4a2f169184b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2228 zcmZvdU2{}L5Qc}{O(ckX$d`a0o1mf+A|fD)fn^2P)zu)8h<dSgvRT4fo1D~cuq?|9 zZ>;h+_#^xgUhD7CvdSu-XU-h!Xfdfb-Tih?PxqOgq+UDoL<oDrzOXlZ6^eB@)L=qT zzLDfb<db1G@}1slZ)v)_v2^M31$P_>bw%_YBxVM^0k*Qyw)`A81TS|O>`m~DIzKh! zuNVy?JH7tRyY2pTxRXt?-C=+FFxwbT`uY0T!@=$}kh}4p+%se||K_;g8RzTSxV!yL zKFD@Q`F8gf{0u*bkb``jPuh<jJQz+Q_dMLr=-c7AKgjdRM&w@nmz$2h8y0(Cg<Bs# znsm3aO(^1K!+GR*v^~rwtJ&7hn2hrEa0Pj9t%zAhALLs*`E*1V@$`5`8{T}0id+}Y zZ&h-g_$B<_Av_DT?O_49N_!sNZ>MdYRll}=mws!m`nC1**`xnUr01OkN62d4`FK0U z8OO*m3)cj$6W9z+<h=bRw)bj2`D@9ZOPulh(9L1)<K%t{*#sxK!2?Kb_g9y(<~9Gp z<S^F!+I|<8^*xPj0&{rY5u}_sj`I6>;!hye<<0vX(wdET-vZKFO8*&j{qpDV>w5`= z@WYKoZczSZASdkSEb?*vSewgpUn$fWYd_lFo9h&(pF<kA7#Ytl`qn<5xYK;c*U>%C z+U(ai>#~<?NbB(Jnd@Doy~vlnTt{~;;*q}r<f<@N3(j2SecwbkR|}N6?x6qvTW?w0 zyS@*+zta8!eGgc~>t5&17@_Y9-?N6aUYBS62{VKA8+lj0-J*v^@NRs!Roi!1+V<;P zENyFU|Ih9wdzt*k`3{`h6)b0Vf8yj_)^DtH=-)GE@FBMIb2M?^W4rfQ;(nn6=k6I` zKKY-CaSnZR@_*3nX{75feDbG&oZmqGZ%$X2E9-p$y;^S*TTZyF*LtjP9yGyq&hk5I zo98U@HQ@YdJ8Mh8Ia{N4XY2wnMt&phxtRD(V$A<0kaGreViu5>fc#IyXkP}t7kTUO z{^UH@9B%<@EB9YboPPJe4czl%%J&YsG4kf~{^iuYd*`|A@jdivkGHU^J>JGPM!r1n zeROl)XC8bX9blY%4fsx0nDQ3#1E62H{PsmmJ<gqe^Ex{p0%u3tzCQx?y-r`A(*wrH zm-#<VygpYoe-~T7`C7<NfLv*>ptr#t-d+p&Y1Fj~>`y)1#Wt_^Y!3GsYaZ)bjk^3? uv_DIn`x?Nv>f872mUm!``|bN4sP?^vZQo`7&!f)&PvVW$FX!xE1^)m5#+NAo diff --git a/projects/bloom/resources/shaders/compositeBloom.comp b/projects/bloom/resources/shaders/compositeBloom.comp index d01e7582..5435a240 100644 --- a/projects/bloom/resources/shaders/compositeBloom.comp +++ b/projects/bloom/resources/shaders/compositeBloom.comp @@ -23,7 +23,7 @@ void main() vec3 blur_color = texture(sampler2D(blurImage, linearSampler), UV).rgb; vec3 main_color = imageLoad(colorBuffer, pixel_coord).rgb; - composite_color.rgb = mix(main_color, blur_color, 0.1f); + composite_color.rgb = mix(main_color, blur_color, 0.25f); imageStore(colorBuffer, pixel_coord, composite_color); } \ No newline at end of file diff --git a/projects/bloom/resources/shaders/upsample.comp b/projects/bloom/resources/shaders/upsample.comp new file mode 100644 index 00000000..0ddeedb5 --- /dev/null +++ b/projects/bloom/resources/shaders/upsample.comp @@ -0,0 +1,45 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set=0, binding=0) uniform texture2D inUpsampleImage; +layout(set=0, binding=1) uniform sampler inImageSampler; +layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D outUpsampleImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main() +{ + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outUpsampleImage)))){ + return; + } + + + ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); + vec2 pixel_size = vec2(1.0f) / imageSize(outUpsampleImage); + vec2 UV = pixel_coord.xy * pixel_size; + + const float gauss_kernel[3] = {1.f, 2.f, 1.f}; + const float gauss_weight = 16.f; + + vec3 sampled_color = vec3(0.f); + + for(int i = -1; i <= 1; i++) + { + for(int j = -1; j <= 1; j++) + { + vec2 sample_location = UV + vec2(j, i) * pixel_size; + vec3 color = texture(sampler2D(inUpsampleImage, inImageSampler), sample_location).rgb; + color *= gauss_kernel[j+1]; + color *= gauss_kernel[i+1]; + color /= gauss_weight; + + sampled_color += color; + } + } + + //vec3 prev_color = imageLoad(outUpsampleImage, pixel_coord).rgb; + //float bloomRimStrength = 0.75f; // adjust this to change strength of bloom + //sampled_color = mix(prev_color, sampled_color, bloomRimStrength); + + imageStore(outUpsampleImage, pixel_coord, vec4(sampled_color, 1.f)); +} \ No newline at end of file diff --git a/projects/bloom/src/main.cpp b/projects/bloom/src/main.cpp index 6a56b768..d93cbc28 100644 --- a/projects/bloom/src/main.cpp +++ b/projects/bloom/src/main.cpp @@ -223,8 +223,8 @@ int main(int argc, const char** argv) { } vkcv::ImageHandle depthBuffer = core.createImage(depthBufferFormat, windowWidth, windowHeight).getHandle(); - vkcv::ImageHandle colorBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, true, true).getHandle(); - vkcv::ImageHandle blurBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, true, false).getHandle(); + vkcv::ImageHandle colorBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, false, true, true).getHandle(); + vkcv::Image blurBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, true, true, false); vkcv::SamplerHandle linearSampler = core.createSampler(vkcv::SamplerFilterType::LINEAR, vkcv::SamplerFilterType::LINEAR, vkcv::SamplerMipmapMode::LINEAR, @@ -279,9 +279,31 @@ int main(int argc, const char** argv) { { blurProgram.addShader(shaderStage, path); }); - vkcv::DescriptorSetHandle blurDescriptorSet = core.createDescriptorSet(blurProgram.getReflectedDescriptors()[0]); + // create descriptor sets for each mip level + std::vector<vkcv::DescriptorSetHandle> blurDescriptorSets; + for(uint32_t mipLevel = 0; mipLevel < blurBuffer.getMipCount(); mipLevel++) + { + blurDescriptorSets.push_back(core.createDescriptorSet(blurProgram.getReflectedDescriptors()[0])); + } vkcv::PipelineHandle blurPipeline = core.createComputePipeline(blurProgram, - { core.getDescriptorSet(blurDescriptorSet).layout }); + { core.getDescriptorSet(blurDescriptorSets[0]).layout }); + + // upsample compute shader + vkcv::ShaderProgram upsampleProgram; + compiler.compile(vkcv::ShaderStage::COMPUTE, + "resources/shaders/upsample.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) + { + upsampleProgram.addShader(shaderStage, path); + }); + // create descriptor sets for each mip level + std::vector<vkcv::DescriptorSetHandle> upsampleDescriptorSets; + for(uint32_t mipLevel = 0; mipLevel < blurBuffer.getMipCount(); mipLevel++) + { + upsampleDescriptorSets.push_back(core.createDescriptorSet(upsampleProgram.getReflectedDescriptors()[0])); + } + vkcv::PipelineHandle upsamplePipeline = core.createComputePipeline(upsampleProgram, + { core.getDescriptorSet(upsampleDescriptorSets[0]).layout }); // bloom composite shader vkcv::ShaderProgram compositeBloomProgram; @@ -338,7 +360,7 @@ int main(int argc, const char** argv) { if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) { depthBuffer = core.createImage(depthBufferFormat, swapchainWidth, swapchainHeight).getHandle(); colorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, true, true).getHandle(); - blurBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, true, false).getHandle(); + //blurBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, true, false).getHandle(); windowWidth = swapchainWidth; windowHeight = swapchainHeight; @@ -348,18 +370,11 @@ int main(int argc, const char** argv) { auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start); // update descriptor sets which use swapchain image - // blur - vkcv::DescriptorWrites blurDescriptorWrites; - blurDescriptorWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, colorBuffer)}; - blurDescriptorWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, linearSampler)}; - blurDescriptorWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, blurBuffer) }; - core.writeDescriptorSet(blurDescriptorSet, blurDescriptorWrites); - // composite bloom vkcv::DescriptorWrites compositeBloomDescriptorWrites; - compositeBloomDescriptorWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, blurBuffer)}; + compositeBloomDescriptorWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, blurBuffer.getHandle())}; compositeBloomDescriptorWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, linearSampler)}; - compositeBloomDescriptorWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, colorBuffer)}; + compositeBloomDescriptorWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, colorBuffer, 0)}; core.writeDescriptorSet(compositeBloomDescriptorSet, compositeBloomDescriptorWrites); // gamma correction @@ -431,26 +446,117 @@ int main(int argc, const char** argv) { drawcalls, renderTargets); - const uint32_t gammaCorrectionLocalGroupSize = 8; - const uint32_t gammaCorrectionDispatchCount[3] = { - static_cast<uint32_t>(glm::ceil(windowWidth / static_cast<float>(gammaCorrectionLocalGroupSize))), - static_cast<uint32_t>(glm::ceil(windowHeight / static_cast<float>(gammaCorrectionLocalGroupSize))), - 1 - }; - core.prepareImageForSampling(cmdStream, colorBuffer); - core.prepareImageForStorage(cmdStream, blurBuffer); + auto windowWidthByLocalGroup = static_cast<float>(windowWidth) / 8.0f; + auto windowHeightByLocalGroup = static_cast<float>(windowHeight) / 8.0f; + + uint32_t initialBlurDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(windowWidthByLocalGroup)), + static_cast<uint32_t>(glm::ceil(windowHeightByLocalGroup)), + 1 + }; // blur dispatch - core.recordComputeDispatchToCmdStream( - cmdStream, - blurPipeline, - gammaCorrectionDispatchCount, - {vkcv::DescriptorSetUsage(0, core.getDescriptorSet(blurDescriptorSet).vulkanHandle)}, + core.prepareImageForSampling(cmdStream, colorBuffer); + core.prepareImageForStorage(cmdStream, blurBuffer.getHandle()); + // blur dispatch of original color attachment + vkcv::DescriptorWrites firstBlurDescriptorWrites; + firstBlurDescriptorWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, colorBuffer)}; + firstBlurDescriptorWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, linearSampler)}; + firstBlurDescriptorWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, blurBuffer.getHandle(), 0) }; + core.writeDescriptorSet(blurDescriptorSets[0], firstBlurDescriptorWrites); + core.recordComputeDispatchToCmdStream( + cmdStream, + blurPipeline, + initialBlurDispatchCount, + {vkcv::DescriptorSetUsage(0, core.getDescriptorSet(blurDescriptorSets[0]).vulkanHandle)}, vkcv::PushConstantData(nullptr, 0)); + // blur dispatches of blur buffer's mip maps + for(uint32_t mipLevel = 1; mipLevel < blurBuffer.getMipCount(); mipLevel++) + { + // mip descriptor writes + vkcv::DescriptorWrites mipBlurDescriptorWrites; + mipBlurDescriptorWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, blurBuffer.getHandle(), mipLevel - 1, true)}; + mipBlurDescriptorWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, linearSampler)}; + mipBlurDescriptorWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, blurBuffer.getHandle(), mipLevel) }; + core.writeDescriptorSet(blurDescriptorSets[mipLevel], mipBlurDescriptorWrites); + + // mip dispatch calculation + windowWidthByLocalGroup /= 2.0f; + windowHeightByLocalGroup /= 2.0f; + + uint32_t mipBlurDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(windowWidthByLocalGroup)), + static_cast<uint32_t>(glm::ceil(windowHeightByLocalGroup)), + 1 + }; + + if(mipBlurDispatchCount[0] == 0) + mipBlurDispatchCount[0] = 1; + if(mipBlurDispatchCount[1] == 0) + mipBlurDispatchCount[1] = 1; + + // mip blur dispatch + core.recordComputeDispatchToCmdStream( + cmdStream, + blurPipeline, + mipBlurDispatchCount, + {vkcv::DescriptorSetUsage(0, core.getDescriptorSet(blurDescriptorSets[mipLevel]).vulkanHandle)}, + vkcv::PushConstantData(nullptr, 0)); + + // image barrier between mips + core.recordImageMemoryBarrier(cmdStream, blurBuffer.getHandle()); + } + + // upsample dispatch + + uint32_t upsampleMipLevels = std::min(blurBuffer.getMipCount(), static_cast<uint32_t>(5)); + + // upsample dispatch for each mip map + for(uint32_t mipLevel = upsampleMipLevels; mipLevel > 0; mipLevel--) + { + // mip descriptor writes + vkcv::DescriptorWrites mipUpsampleDescriptorWrites; + mipUpsampleDescriptorWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, blurBuffer.getHandle(), mipLevel, true)}; + mipUpsampleDescriptorWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, linearSampler)}; + mipUpsampleDescriptorWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, blurBuffer.getHandle(), mipLevel - 1) }; + core.writeDescriptorSet(upsampleDescriptorSets[mipLevel], mipUpsampleDescriptorWrites); + + auto mipDivisor = glm::pow(2.0f, static_cast<float>(mipLevel) - 1.0f); + + auto upsampleDispatchWidth = static_cast<float>(windowWidth) / mipDivisor; + auto upsampleDispatchHeight = static_cast<float>(windowHeight) / mipDivisor; + + upsampleDispatchWidth /= 8.0f; + upsampleDispatchHeight /= 8.0f; + + const uint32_t upsampleDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(upsampleDispatchWidth)), + static_cast<uint32_t>(glm::ceil(upsampleDispatchHeight)), + 1 + }; + + core.recordComputeDispatchToCmdStream( + cmdStream, + upsamplePipeline, + upsampleDispatchCount, + {vkcv::DescriptorSetUsage(0, core.getDescriptorSet(upsampleDescriptorSets[mipLevel]).vulkanHandle)}, + vkcv::PushConstantData(nullptr, 0) + ); + // image barrier between mips + core.recordImageMemoryBarrier(cmdStream, blurBuffer.getHandle()); + } + core.prepareImageForStorage(cmdStream, colorBuffer); - core.prepareImageForSampling(cmdStream, blurBuffer); + core.prepareImageForSampling(cmdStream, blurBuffer.getHandle()); + + const uint32_t gammaCorrectionLocalGroupSize = 8; + const uint32_t gammaCorrectionDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(static_cast<float>(windowWidth) / static_cast<float>(gammaCorrectionLocalGroupSize))), + static_cast<uint32_t>(glm::ceil(static_cast<float>(windowHeight) / static_cast<float>(gammaCorrectionLocalGroupSize))), + 1 + }; // bloom composite dispatch core.recordComputeDispatchToCmdStream(cmdStream, diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp index 26553223..8e565a76 100644 --- a/src/vkcv/DescriptorManager.cpp +++ b/src/vkcv/DescriptorManager.cpp @@ -107,10 +107,11 @@ namespace vkcv std::vector<WriteDescriptorSetInfo> writeInfos; for (const auto& write : writes.sampledImageWrites) { + vk::ImageLayout layout = write.useGeneralLayout ? vk::ImageLayout::eGeneral : vk::ImageLayout::eShaderReadOnlyOptimal; const vk::DescriptorImageInfo imageInfo( nullptr, - imageManager.getVulkanImageView(write.image), - vk::ImageLayout::eShaderReadOnlyOptimal + imageManager.getVulkanImageView(write.image, write.mipLevel), + layout ); imageInfos.push_back(imageInfo); -- GitLab