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