From 1a8cf1e6a3a993132fbbef9c663b3a0787637f39 Mon Sep 17 00:00:00 2001
From: Jens Koenen <koenen@vr.rwth-aachen.de>
Date: Wed, 14 Sep 2022 23:23:42 +0200
Subject: [PATCH] The scene is now voxelized and later used during the
 propagation of the light.

---
 liblava/block/pipeline.hpp                |   6 +
 res/dpr/utility/indirect_geometry.frag    |  26 +-
 res/dpr/utility/indirect_geometry.geom    |  36 ++-
 res/dpr/utility/indirect_propagation.comp |  33 ++-
 src/utility/indirect_cache.cpp            | 331 ++++++++++------------
 src/utility/indirect_cache.hpp            |   9 +-
 src/vr_application.cpp                    |   1 +
 7 files changed, 232 insertions(+), 210 deletions(-)

diff --git a/liblava/block/pipeline.hpp b/liblava/block/pipeline.hpp
index 49e9713f..a3b8fa14 100644
--- a/liblava/block/pipeline.hpp
+++ b/liblava/block/pipeline.hpp
@@ -284,6 +284,9 @@ namespace lava {
         void set_viewport(VkViewport value) {
             viewport = value;
         }
+        void set_viewport_count(uint32_t count){
+            info.viewport_state.viewportCount = count;
+        }
 
         VkRect2D get_scissor() const {
             return scissor;
@@ -291,6 +294,9 @@ namespace lava {
         void set_scissor(VkRect2D value) {
             scissor = value;
         }
+        void set_scissor_count(uint32_t count) {
+            info.viewport_state.scissorCount = count;
+        }
 
         size_type get_size_type() const {
             return size_type;
diff --git a/res/dpr/utility/indirect_geometry.frag b/res/dpr/utility/indirect_geometry.frag
index 3aa83d02..13f593f0 100644
--- a/res/dpr/utility/indirect_geometry.frag
+++ b/res/dpr/utility/indirect_geometry.frag
@@ -1,6 +1,5 @@
 #version 450 core
 #extension GL_GOOGLE_include_directive : require
-#extension GL_EXT_shader_atomic_float : require
 
 #include "indirect_data.inc"
 #include "indirect_library.glsl"
@@ -10,10 +9,8 @@ layout(set = 1, binding = 0) uniform IndirectDomainBuffer
     IndirectDomain indirect_domain;
 };
 
-layout(set = 1, binding = 1, r32f) uniform image3D image_geometry_x_distribution;
-layout(set = 1, binding = 2, r32f) uniform image3D image_geometry_y_distribution;
-layout(set = 1, binding = 3, r32f) uniform image3D image_geometry_z_distribution;
-layout(set = 1, binding = 4, r32f) uniform image3D image_geometry_w_distribution;
+layout(set = 1, binding = 1, r32ui) uniform uimage3D image_geometry_xy_distribution;
+layout(set = 1, binding = 2, r32ui) uniform uimage3D image_geometry_zw_distribution;
 
 layout(push_constant) uniform Constants
 {
@@ -23,6 +20,19 @@ layout(push_constant) uniform Constants
 layout(location = 0) in vec3 inCoord;
 layout(location = 1) in vec3 inNormal;
 
+#define atomicAdd(image, coord, value)                                                                       \
+{                                                                                                            \
+    uint new_value = packHalf2x16(value);                                                                    \
+    uint previous_value = 0;                                                                                 \
+    uint current_value = 0;                                                                                  \
+                                                                                                             \
+    while((current_value = imageAtomicCompSwap(image, coord, previous_value, new_value)) != previous_value)  \
+    {                                                                                                        \
+        previous_value = current_value;                                                                      \
+        new_value = packHalf2x16(value + unpackHalf2x16(current_value));                                     \
+    }                                                                                                        \
+}
+
 void main()
 {
     ivec3 cell_coord = ivec3(inCoord * indirect_domain.geometry_resolution);
@@ -51,8 +61,6 @@ void main()
     //NOTE: Where opacity is in percent
     vec4 geometry_distribution = opacity * distribution;
 
-    imageAtomicAdd(image_geometry_x_distribution, cell_coord, geometry_distribution.x);
-    imageAtomicAdd(image_geometry_y_distribution, cell_coord, geometry_distribution.y);
-    imageAtomicAdd(image_geometry_z_distribution, cell_coord, geometry_distribution.z);
-    imageAtomicAdd(image_geometry_w_distribution, cell_coord, geometry_distribution.w);
+    atomicAdd(image_geometry_xy_distribution, cell_coord, geometry_distribution.xy);
+    atomicAdd(image_geometry_zw_distribution, cell_coord, geometry_distribution.zw);
 }
\ No newline at end of file
diff --git a/res/dpr/utility/indirect_geometry.geom b/res/dpr/utility/indirect_geometry.geom
index b63bf132..1227568c 100644
--- a/res/dpr/utility/indirect_geometry.geom
+++ b/res/dpr/utility/indirect_geometry.geom
@@ -22,45 +22,57 @@ void main()
     vec3 position2 = gl_in[1].gl_Position.xyz;
     vec3 position3 = gl_in[2].gl_Position.xyz;
 
+    vec3 coord1 = (position1 - indirect_domain.min) / (indirect_domain.max - indirect_domain.min);
+    vec3 coord2 = (position2 - indirect_domain.min) / (indirect_domain.max - indirect_domain.min);
+    vec3 coord3 = (position3 - indirect_domain.min) / (indirect_domain.max - indirect_domain.min);
+
+    vec2 screen_coord1 = vec2(0.0);
+    vec2 screen_coord2 = vec2(0.0);
+    vec2 screen_coord3 = vec2(0.0);
+
     vec3 normal_abs = abs(cross(position2 - position1, position3 - position1));
     float normal_max = max(normal_abs.x, max(normal_abs.y, normal_abs.z));
 
     if(normal_abs.x == normal_max)
     {
-        position1 = position1.yzx;
-        position2 = position2.yzx;
-        position3 = position3.yzx;
+        screen_coord1 = coord1.yz;
+        screen_coord2 = coord2.yz;
+        screen_coord3 = coord3.yz;
 
         gl_ViewportIndex = 0;
     }
 
     else if(normal_abs.y == normal_max)
     {
-        position1 = position1.xzy;
-        position2 = position2.xzy;
-        position3 = position3.xzy;
+        screen_coord1 = coord1.xz;
+        screen_coord2 = coord2.xz;
+        screen_coord3 = coord3.xz;
 
         gl_ViewportIndex = 1;
     }
 
     else
     {
+        screen_coord1 = coord1.xy;
+        screen_coord2 = coord2.xy;
+        screen_coord3 = coord3.xy;
+
         gl_ViewportIndex = 2;
     }
 
-    outCoord = (position1 - indirect_domain.min) / (indirect_domain.max - indirect_domain.min);
+    outCoord = coord1;
     outNormal = inNormal[0];
-    gl_Position = vec4(outCoord.xy * 2.0 - 1.0, 0.5, 1.0);
+    gl_Position = vec4(screen_coord1 * 2.0 - 1.0, 0.5, 1.0);
     EmitVertex();
 
-    outCoord = (position2 - indirect_domain.min) / (indirect_domain.max - indirect_domain.min);
+    outCoord = coord2;
     outNormal = inNormal[1];
-    gl_Position = vec4(outCoord.xy * 2.0 - 1.0, 0.5, 1.0);
+    gl_Position = vec4(screen_coord2 * 2.0 - 1.0, 0.5, 1.0);
     EmitVertex();
 
-    outCoord = (position3 - indirect_domain.min) / (indirect_domain.max - indirect_domain.min);
+    outCoord = coord3;
     outNormal = inNormal[2];
-    gl_Position = vec4(outCoord.xy * 2.0 - 1.0, 0.5, 1.0);
+    gl_Position = vec4(screen_coord3 * 2.0 - 1.0, 0.5, 1.0);
     EmitVertex();
 
     EndPrimitive();
diff --git a/res/dpr/utility/indirect_propagation.comp b/res/dpr/utility/indirect_propagation.comp
index f77b617b..62cb6356 100644
--- a/res/dpr/utility/indirect_propagation.comp
+++ b/res/dpr/utility/indirect_propagation.comp
@@ -18,7 +18,10 @@ layout(set = 0, binding = 6, rgba16f) uniform image3D image_red_indirect;
 layout(set = 0, binding = 7, rgba16f) uniform image3D image_green_indirect;
 layout(set = 0, binding = 8, rgba16f) uniform image3D image_blue_indirect;
 
-layout(set = 0, binding = 9) uniform IndirectDomainBuffer
+layout(set = 0, binding = 9) uniform sampler3D sampler_geometry_xy_distribution;
+layout(set = 0, binding = 10) uniform sampler3D sampler_geometry_zy_distribution;
+
+layout(set = 0, binding = 11) uniform IndirectDomainBuffer
 {
     IndirectDomain indirect_domain;
 };
@@ -73,6 +76,11 @@ bool check_bounds(ivec3 coord)
     return all(lessThanEqual(ivec3(0), coord)) && all(lessThan(coord, indirect_domain.indirect_resolution));
 }
 
+vec4 load_geometry_distribution(vec3 geometry_coord)
+{
+    return vec4(0.0); //TODO: !!!
+}
+
 void propagate_neighbour(ivec3 cell_coord, uint neighbour, inout vec4 red_distribution, inout vec4 green_distribution, inout vec4 blue_distribution)
 {
     ivec3 neighbour_coord = cell_coord + neighbour_offsets[neighbour];
@@ -83,12 +91,19 @@ void propagate_neighbour(ivec3 cell_coord, uint neighbour, inout vec4 red_distri
         return;
     }
 
+    //----- Visibility ----------------------------------------------
+
+    vec3 geometry_coord = (vec3(cell_coord) + vec3(neighbour_coord)) / 2.0;
+    vec4 geometry_distribution = load_geometry_distribution(geometry_coord);
+
+    float visibility = 1.0; //TODO:!!!
+
+    //----- Side Faces ----------------------------------------------
+
     vec4 neighbour_red_distribution = imageLoad(image_src_red_distribution, neighbour_coord);
     vec4 neighbour_green_distribution = imageLoad(image_src_green_distribution, neighbour_coord);
     vec4 neighbour_blue_distribution = imageLoad(image_src_blue_distribution, neighbour_coord);
 
-    //----- Side Faces ----------------------------------------------
-
     for(uint side = 0; side < 4; side++)
     {
         vec3 eval_direction = neighbour_orientation * side_eval_directions[side];
@@ -100,9 +115,9 @@ void propagate_neighbour(ivec3 cell_coord, uint neighbour, inout vec4 red_distri
         intensity.z = max(0.0, spherical_harmonic_evaluate(eval_direction, neighbour_blue_distribution));
 
         //NOTE: Where side_solid_angle * intensity is in watt
-        red_distribution += (side_solid_angle * intensity.x) * spherical_harmonic_cosine_lobe(emit_direction);
-        green_distribution += (side_solid_angle * intensity.y) * spherical_harmonic_cosine_lobe(emit_direction);
-        blue_distribution += (side_solid_angle * intensity.z) * spherical_harmonic_cosine_lobe(emit_direction);
+        red_distribution += (side_solid_angle * intensity.x) * visibility * spherical_harmonic_cosine_lobe(emit_direction);
+        green_distribution += (side_solid_angle * intensity.y) * visibility * spherical_harmonic_cosine_lobe(emit_direction);
+        blue_distribution += (side_solid_angle * intensity.z) * visibility * spherical_harmonic_cosine_lobe(emit_direction);
     }
 
     //----- Direct Face -----------------------------------------------
@@ -116,9 +131,9 @@ void propagate_neighbour(ivec3 cell_coord, uint neighbour, inout vec4 red_distri
     intensity.z = max(0.0, spherical_harmonic_evaluate(eval_direction, neighbour_blue_distribution));
 
     //NOTE: Where side_solid_angle * intensity is in watt
-    red_distribution += (direct_solid_angle * intensity.x) * spherical_harmonic_cosine_lobe(emit_direction);
-    green_distribution += (direct_solid_angle * intensity.y) * spherical_harmonic_cosine_lobe(emit_direction);
-    blue_distribution += (direct_solid_angle * intensity.z) * spherical_harmonic_cosine_lobe(emit_direction);
+    red_distribution += (direct_solid_angle * intensity.x) * visibility * spherical_harmonic_cosine_lobe(emit_direction);
+    green_distribution += (direct_solid_angle * intensity.y) * visibility * spherical_harmonic_cosine_lobe(emit_direction);
+    blue_distribution += (direct_solid_angle * intensity.z) * visibility * spherical_harmonic_cosine_lobe(emit_direction);
 
     //-----------------------------------------------------------------
 }
diff --git a/src/utility/indirect_cache.cpp b/src/utility/indirect_cache.cpp
index b13858db..a5634a7d 100644
--- a/src/utility/indirect_cache.cpp
+++ b/src/utility/indirect_cache.cpp
@@ -198,10 +198,14 @@ void IndirectCache::compute_indirect(VkCommandBuffer command_buffer, lava::index
 {
     const std::vector<SceneLight>& lights = this->scene->get_lights();
 
-    VkImageMemoryBarrier clear_geometry_begin_barrier = IndirectCache::build_barrier(0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
-    clear_geometry_begin_barrier.image = this->distribution_geometry_image;
+    std::vector<VkImageMemoryBarrier> clear_begin_barriers = IndirectCache::build_barriers(5, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+    clear_begin_barriers[0].image = this->distribution_red_image[0];
+    clear_begin_barriers[1].image = this->distribution_green_image[0];
+    clear_begin_barriers[2].image = this->distribution_blue_image[0];
+    clear_begin_barriers[3].image = this->distribution_geometry_image[0];
+    clear_begin_barriers[4].image = this->distribution_geometry_image[1];
 
-    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &clear_geometry_begin_barrier);
+    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, clear_begin_barriers.size(), clear_begin_barriers.data());
 
     VkClearColorValue clear_color;
     clear_color.float32[0] = 0.0f;
@@ -216,36 +220,33 @@ void IndirectCache::compute_indirect(VkCommandBuffer command_buffer, lava::index
     subresource_range.baseArrayLayer = 0;
     subresource_range.layerCount = 1;
 
-    vkCmdClearColorImage(command_buffer, this->distribution_geometry_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range);
-
-    VkImageMemoryBarrier clear_geometry_end_barrier = IndirectCache::build_barrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
-    clear_geometry_end_barrier.image = this->distribution_geometry_image;
-
-    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &clear_geometry_end_barrier);
-
-    this->geometry_pass->process(command_buffer, 0);
-
-
-    //TODO: Another layout transition to shader ead only optimal
-
-    std::array<VkImageMemoryBarrier, 3> clear_begin_barriers = IndirectCache::build_barriers(0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
-    clear_begin_barriers[0].image = this->distribution_red_image[0];
-    clear_begin_barriers[1].image = this->distribution_green_image[0];
-    clear_begin_barriers[2].image = this->distribution_blue_image[0];
-
-    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, clear_begin_barriers.size(), clear_begin_barriers.data());
-
     vkCmdClearColorImage(command_buffer, this->distribution_red_image[0], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range);
     vkCmdClearColorImage(command_buffer, this->distribution_green_image[0], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range);
     vkCmdClearColorImage(command_buffer, this->distribution_blue_image[0], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range);
+    vkCmdClearColorImage(command_buffer, this->distribution_geometry_image[0], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range);
+    vkCmdClearColorImage(command_buffer, this->distribution_geometry_image[1], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range);
 
-    std::array<VkImageMemoryBarrier, 3> clear_end_barriers = IndirectCache::build_barriers(VK_ACCESS_TRANSFER_WRITE_BIT, 0, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
+    std::vector<VkImageMemoryBarrier> clear_end_barriers = IndirectCache::build_barriers(3, VK_ACCESS_TRANSFER_WRITE_BIT, 0, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
     clear_end_barriers[0].image = this->distribution_red_image[0];
     clear_end_barriers[1].image = this->distribution_green_image[0];
     clear_end_barriers[2].image = this->distribution_blue_image[0];
 
     vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, clear_end_barriers.size(), clear_end_barriers.data());
 
+    std::vector<VkImageMemoryBarrier> geometry_begin_barriers = IndirectCache::build_barriers(2, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
+    geometry_begin_barriers[0].image = this->distribution_geometry_image[0];
+    geometry_begin_barriers[1].image = this->distribution_geometry_image[1];
+    
+    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, geometry_begin_barriers.size(), geometry_begin_barriers.data());
+
+    this->geometry_pass->process(command_buffer, 0);
+
+    std::vector<VkImageMemoryBarrier> geometry_end_barriers = IndirectCache::build_barriers(2, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+    geometry_end_barriers[0].image = this->distribution_geometry_image[0];
+    geometry_end_barriers[1].image = this->distribution_geometry_image[1];
+
+    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, geometry_end_barriers.size(), geometry_end_barriers.data());
+
     for (uint32_t index = 0; index < lights.size(); index++)
     {
         const SceneLight& light = lights[index];
@@ -264,7 +265,7 @@ void IndirectCache::compute_indirect(VkCommandBuffer command_buffer, lava::index
     //NOTE: Memory barrier for source image provieded by injection render pass
     //      The final image layout of distribution_..._image[0] has to be VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL
 
-    std::array<VkImageMemoryBarrier, 3> copy_barriers = IndirectCache::build_barriers(0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
+    std::vector<VkImageMemoryBarrier> copy_barriers = IndirectCache::build_barriers(3, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
     copy_barriers[0].image = this->distribution_red_image[2];
     copy_barriers[1].image = this->distribution_green_image[2];
     copy_barriers[2].image = this->distribution_blue_image[2];
@@ -298,21 +299,21 @@ void IndirectCache::compute_indirect(VkCommandBuffer command_buffer, lava::index
     vkCmdCopyImage(command_buffer, this->distribution_green_image[0], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, this->distribution_green_image[2], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
     vkCmdCopyImage(command_buffer, this->distribution_blue_image[0], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, this->distribution_blue_image[2], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
 
-    std::array<VkImageMemoryBarrier, 3> setup_src_barriers = IndirectCache::build_barriers(VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
+    std::vector<VkImageMemoryBarrier> setup_src_barriers = IndirectCache::build_barriers(3, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
     setup_src_barriers[0].image = this->distribution_red_image[0];
     setup_src_barriers[1].image = this->distribution_green_image[0];
     setup_src_barriers[2].image = this->distribution_blue_image[0];
 
     vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, setup_src_barriers.size(), setup_src_barriers.data());
 
-    std::array<VkImageMemoryBarrier, 3> setup_dst_barriers = IndirectCache::build_barriers(0, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
+    std::vector<VkImageMemoryBarrier> setup_dst_barriers = IndirectCache::build_barriers(3, 0, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
     setup_dst_barriers[0].image = this->distribution_red_image[1];
     setup_dst_barriers[1].image = this->distribution_green_image[1];
     setup_dst_barriers[2].image = this->distribution_blue_image[1];
 
     vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, setup_dst_barriers.size(), setup_dst_barriers.data());
 
-    std::array<VkImageMemoryBarrier, 3> setup_indirect_barriers = IndirectCache::build_barriers(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
+    std::vector<VkImageMemoryBarrier> setup_indirect_barriers = IndirectCache::build_barriers(3, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL);
     setup_indirect_barriers[0].image = this->distribution_red_image[2];
     setup_indirect_barriers[1].image = this->distribution_green_image[2];
     setup_indirect_barriers[2].image = this->distribution_blue_image[2];
@@ -330,21 +331,21 @@ void IndirectCache::compute_indirect(VkCommandBuffer command_buffer, lava::index
 
         vkCmdDispatch(command_buffer, this->domain_work_groups.x, this->domain_work_groups.y, this->domain_work_groups.z);
         
-        std::array<VkImageMemoryBarrier, 3> compue_src_barriers = IndirectCache::build_barriers(VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL);
+        std::vector<VkImageMemoryBarrier> compue_src_barriers = IndirectCache::build_barriers(3, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL);
         compue_src_barriers[0].image = this->distribution_red_image[src_index];
         compue_src_barriers[1].image = this->distribution_green_image[src_index];
         compue_src_barriers[2].image = this->distribution_blue_image[src_index];
 
         vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, compue_src_barriers.size(), compue_src_barriers.data());
 
-        std::array<VkImageMemoryBarrier, 3> compute_dst_barriers = IndirectCache::build_barriers(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL);
+        std::vector<VkImageMemoryBarrier> compute_dst_barriers = IndirectCache::build_barriers(3, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL);
         compute_dst_barriers[0].image = this->distribution_red_image[dst_index];
         compute_dst_barriers[1].image = this->distribution_green_image[dst_index];
         compute_dst_barriers[2].image = this->distribution_blue_image[dst_index];
 
         vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, compute_dst_barriers.size(), compute_dst_barriers.data());
 
-        std::array<VkImageMemoryBarrier, 3> compute_indirect_barriers = IndirectCache::build_barriers(VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL);
+        std::vector<VkImageMemoryBarrier> compute_indirect_barriers = IndirectCache::build_barriers(3, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL);
         compute_indirect_barriers[0].image = this->distribution_red_image[2];
         compute_indirect_barriers[1].image = this->distribution_green_image[2];
         compute_indirect_barriers[2].image = this->distribution_blue_image[2];
@@ -352,7 +353,7 @@ void IndirectCache::compute_indirect(VkCommandBuffer command_buffer, lava::index
         vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, compute_indirect_barriers.size(), compute_indirect_barriers.data());
     }
 
-    std::array<VkImageMemoryBarrier, 3> final_barriers = IndirectCache::build_barriers(VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+    std::vector<VkImageMemoryBarrier> final_barriers = IndirectCache::build_barriers(3, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
     final_barriers[0].image = this->distribution_red_image[2];
     final_barriers[1].image = this->distribution_green_image[2];
     final_barriers[2].image = this->distribution_blue_image[2];
@@ -540,173 +541,127 @@ bool IndirectCache::create_images(lava::device_ptr device, const IndirectCacheSe
 
     for (uint32_t index = 0; index < this->distribution_red_image.size(); index++)
     {
-        VmaAllocation red_image_memory = VK_NULL_HANDLE;
-        VmaAllocation green_image_memory = VK_NULL_HANDLE;
-        VmaAllocation blue_image_memory = VK_NULL_HANDLE;
-
-        VkImage red_image = VK_NULL_HANDLE;
-        VkImage green_image = VK_NULL_HANDLE;
-        VkImage blue_image = VK_NULL_HANDLE;
-
-        if (vmaCreateImage(device->get_allocator()->get(), &image_create_info, &allocation_info, &red_image, &red_image_memory, nullptr) != VK_SUCCESS)
+        if (vmaCreateImage(device->get_allocator()->get(), &image_create_info, &allocation_info, &this->distribution_red_image[index], &this->distribution_red_image_memory[index], nullptr) != VK_SUCCESS)
         {
             lava::log()->error("Can't create red distribution image for indirect cache!");
 
             return false;
         }
 
-        if (vmaCreateImage(device->get_allocator()->get(), &image_create_info, &allocation_info, &green_image, &green_image_memory, nullptr) != VK_SUCCESS)
+        if (vmaCreateImage(device->get_allocator()->get(), &image_create_info, &allocation_info, &this->distribution_green_image[index], &this->distribution_green_image_memory[index], nullptr) != VK_SUCCESS)
         {
             lava::log()->error("Can't create green distribution image for indirect cache!");
 
             return false;
         }
 
-        if (vmaCreateImage(device->get_allocator()->get(), &image_create_info, &allocation_info, &blue_image, &blue_image_memory, nullptr) != VK_SUCCESS)
+        if (vmaCreateImage(device->get_allocator()->get(), &image_create_info, &allocation_info, &this->distribution_blue_image[index], &this->distribution_blue_image_memory[index], nullptr) != VK_SUCCESS)
         {
             lava::log()->error("Can't create blue distribution image for indirect cache!");
 
             return false;
         }
 
-        VkImageView red_image_view = VK_NULL_HANDLE;
-        VkImageView green_image_view = VK_NULL_HANDLE;
-        VkImageView blue_image_view = VK_NULL_HANDLE;
-
-        VkImageView red_image_array_view = VK_NULL_HANDLE;
-        VkImageView green_image_array_view = VK_NULL_HANDLE;
-        VkImageView blue_image_array_view = VK_NULL_HANDLE;
-
-        view_create_info.image = red_image;
-        array_view_create_info.image = red_image;
+        view_create_info.image = this->distribution_red_image[index];
+        array_view_create_info.image = this->distribution_red_image[index];
 
-        if (vkCreateImageView(device->get(), &view_create_info, lava::memory::alloc(), &red_image_view) != VK_SUCCESS)
+        if (vkCreateImageView(device->get(), &view_create_info, lava::memory::alloc(), &this->distribution_red_image_view[index]) != VK_SUCCESS)
         {
             lava::log()->error("Can't create red distribution image view for indirect cache!");
 
             return false;
         }
 
-        if (vkCreateImageView(device->get(), &array_view_create_info, lava::memory::alloc(), &red_image_array_view) != VK_SUCCESS)
+        if (vkCreateImageView(device->get(), &array_view_create_info, lava::memory::alloc(), &this->distribution_red_image_array_view[index]) != VK_SUCCESS)
         {
             lava::log()->error("Can't create red distribution image array view for indirect cache!");
 
             return false;
         }
 
-        view_create_info.image = green_image;
-        array_view_create_info.image = green_image;
+        view_create_info.image = this->distribution_green_image[index];
+        array_view_create_info.image = this->distribution_green_image[index];
 
-        if (vkCreateImageView(device->get(), &view_create_info, lava::memory::alloc(), &green_image_view) != VK_SUCCESS)
+        if (vkCreateImageView(device->get(), &view_create_info, lava::memory::alloc(), &this->distribution_green_image_view[index]) != VK_SUCCESS)
         {
             lava::log()->error("Can't create green distribution image view for indirect cache!");
 
             return false;
         }
 
-        if (vkCreateImageView(device->get(), &array_view_create_info, lava::memory::alloc(), &green_image_array_view) != VK_SUCCESS)
+        if (vkCreateImageView(device->get(), &array_view_create_info, lava::memory::alloc(), &this->distribution_green_image_array_view[index]) != VK_SUCCESS)
         {
             lava::log()->error("Can't create green distribution image array view for indirect cache!");
 
             return false;
         }
 
-        view_create_info.image = blue_image;
-        array_view_create_info.image = blue_image;
+        view_create_info.image = this->distribution_blue_image[index];
+        array_view_create_info.image = this->distribution_blue_image[index];
 
-        if (vkCreateImageView(device->get(), &view_create_info, lava::memory::alloc(), &blue_image_view) != VK_SUCCESS)
+        if (vkCreateImageView(device->get(), &view_create_info, lava::memory::alloc(), &this->distribution_blue_image_view[index]) != VK_SUCCESS)
         {
             lava::log()->error("Can't create blue distribution image view for indirect cache!");
 
             return false;
         }
 
-        if (vkCreateImageView(device->get(), &array_view_create_info, lava::memory::alloc(), &blue_image_array_view) != VK_SUCCESS)
+        if (vkCreateImageView(device->get(), &array_view_create_info, lava::memory::alloc(), &this->distribution_blue_image_array_view[index]) != VK_SUCCESS)
         {
             lava::log()->error("Can't create blue distribution image array view for indirect cache!");
 
             return false;
         }
-
-        this->distribution_red_image_memory[index] = red_image_memory;
-        this->distribution_green_image_memory[index] = green_image_memory;
-        this->distribution_blue_image_memory[index] = blue_image_memory;
-        
-        this->distribution_red_image[index] = red_image;
-        this->distribution_green_image[index] = green_image;
-        this->distribution_blue_image[index] = blue_image;
-        
-        this->distribution_red_image_view[index] = red_image_view;
-        this->distribution_green_image_view[index] = green_image_view;
-        this->distribution_blue_image_view[index] = blue_image_view;
-
-        this->distribution_red_image_array_view[index] = red_image_array_view;
-        this->distribution_green_image_array_view[index] = green_image_array_view;
-        this->distribution_blue_image_array_view[index] = blue_image_array_view;
     }
 
-    image_create_info.format = VK_FORMAT_R32_SFLOAT;
+    image_create_info.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
+    image_create_info.format = VK_FORMAT_R16G16_SFLOAT;
     image_create_info.extent.width = this->domain_geometry_resolution.x;
     image_create_info.extent.height = this->domain_geometry_resolution.y;
     image_create_info.extent.depth = this->domain_geometry_resolution.z;
+    image_create_info.usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
     
-    for (uint32_t index = 0; index < this->distribution_geometry_image.size(); index++)
-    {
-        
-    }
+    view_create_info.format = VK_FORMAT_R16G16_SFLOAT;
+
+    VkImageViewCreateInfo integer_view_create_info;
+    integer_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+    integer_view_create_info.pNext = nullptr;
+    integer_view_create_info.flags = 0;
+    integer_view_create_info.image = VK_NULL_HANDLE;
+    integer_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_3D;
+    integer_view_create_info.format = VK_FORMAT_R32_UINT;
+    integer_view_create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+    integer_view_create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+    integer_view_create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+    integer_view_create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+    integer_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    integer_view_create_info.subresourceRange.baseMipLevel = 0;
+    integer_view_create_info.subresourceRange.levelCount = 1;
+    integer_view_create_info.subresourceRange.baseArrayLayer = 0;
+    integer_view_create_info.subresourceRange.layerCount = 1;
 
-    if (vmaCreateImage(device->get_allocator()->get(), &image_create_info, &allocation_info, &this->distribution_geometry_image, &this->distribution_geometry_image_memory, nullptr) != VK_SUCCESS)
+    for (uint32_t index = 0; index < this->distribution_geometry_image.size(); index++)
     {
-        lava::log()->error("Can't create geometry distribution image for indirect cache!");
+        if (vmaCreateImage(device->get_allocator()->get(), &image_create_info, &allocation_info, &this->distribution_geometry_image[index], &this->distribution_geometry_image_memory[index], nullptr) != VK_SUCCESS)
+        {
+            lava::log()->error("Can't create geometry distribution image for indirect cache!");
 
-        return false;
-    }
+            return false;
+        }
 
-    view_create_info.image = this->distribution_geometry_image;
- 
-    if (vkCreateImageView(device->get(), &view_create_info, lava::memory::alloc(), &this->distribution_geometry_image_view) != VK_SUCCESS)
-    {
-        lava::log()->error("Can't create geometry distribution image view for indirect cache!");
+        view_create_info.image = this->distribution_geometry_image[index];
+        integer_view_create_info.image = this->distribution_geometry_image[index];
 
-        return false;
-    }
+        if (vkCreateImageView(device->get(), &view_create_info, lava::memory::alloc(), &this->distribution_geometry_image_view[index]) != VK_SUCCESS)
+        {
+            lava::log()->error("Can't create geometry distribution image view for indirect cache!");
 
-    VkImageViewCreateInfo component_view_create_info;
-    component_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
-    component_view_create_info.pNext = nullptr;
-    component_view_create_info.flags = 0;
-    component_view_create_info.image = this->distribution_geometry_image;
-    component_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_3D;
-    component_view_create_info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
-    component_view_create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
-    component_view_create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
-    component_view_create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
-    component_view_create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
-    component_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-    component_view_create_info.subresourceRange.baseMipLevel = 0;
-    component_view_create_info.subresourceRange.levelCount = 1;
-    component_view_create_info.subresourceRange.baseArrayLayer = 0;
-    component_view_create_info.subresourceRange.layerCount = 1;
-
-    VkComponentMapping zero_component;
-    zero_component.r = VK_COMPONENT_SWIZZLE_ZERO;
-    zero_component.g = VK_COMPONENT_SWIZZLE_ZERO;
-    zero_component.b = VK_COMPONENT_SWIZZLE_ZERO;
-    zero_component.a = VK_COMPONENT_SWIZZLE_ZERO;
-
-    std::vector<VkComponentMapping> component_list(4, zero_component);
-    component_list[0].r = VK_COMPONENT_SWIZZLE_R;
-    component_list[1].r = VK_COMPONENT_SWIZZLE_G;
-    component_list[2].r = VK_COMPONENT_SWIZZLE_B;
-    component_list[3].r = VK_COMPONENT_SWIZZLE_A;
-    
-    for (uint32_t index = 0; index < this->distribution_geometry_image_component_view.size(); index++)
-    {
-        component_view_create_info.components = component_list[index];
+            return false;
+        }
 
-        if (vkCreateImageView(device->get(), &component_view_create_info, lava::memory::alloc(), &this->distribution_geometry_image_component_view[index]) != VK_SUCCESS)
+        if (vkCreateImageView(device->get(), &integer_view_create_info, lava::memory::alloc(), &this->distribution_geometry_image_integer_view[index]) != VK_SUCCESS)
         {
-            lava::log()->error("Can't create geometry distribution image component view for indirect cache!");
+            lava::log()->error("Can't create geometry distribution image integer view for indirect cache!");
 
             return false;
         }
@@ -770,8 +725,8 @@ bool IndirectCache::create_descriptors(lava::device_ptr device)
     lava::VkDescriptorPoolSizes descriptor_type_count =
     {
         { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 5 },
-        { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 6 },
-        { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 22 }
+        { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 10 },
+        { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 20 }
     };
 
     this->descriptor_pool = lava::make_descriptor_pool();
@@ -799,11 +754,9 @@ bool IndirectCache::create_descriptors(lava::device_ptr device)
     this->geometry_descriptor = lava::make_descriptor();
     this->geometry_descriptor->add_binding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); //descriptor-binding index 0: indirect domain
 
-    this->geometry_descriptor->add_binding(1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_FRAGMENT_BIT);                                 //descriptor-binding index 1: geometry distribution x
-    this->geometry_descriptor->add_binding(2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_FRAGMENT_BIT);                                 //descriptor-binding index 2: geometry distribution y
-    this->geometry_descriptor->add_binding(3, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_FRAGMENT_BIT);                                 //descriptor-binding index 3: geometry distribution z
-    this->geometry_descriptor->add_binding(4, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_FRAGMENT_BIT);                                 //descriptor-binding index 4: geometry distribution w
-
+    this->geometry_descriptor->add_binding(1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_FRAGMENT_BIT);                                 //descriptor-binding index 1: geometry distribution xy
+    this->geometry_descriptor->add_binding(2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_FRAGMENT_BIT);                                 //descriptor-binding index 2: geometry distribution zw
+    
     if (!this->geometry_descriptor->create(device))
     {
         lava::log()->error("Can't create geometry descriptor set layout for indirect cache!");
@@ -825,19 +778,22 @@ bool IndirectCache::create_descriptors(lava::device_ptr device)
     }
 
     this->propagation_descriptor = lava::make_descriptor();
-    this->propagation_descriptor->add_binding(0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);  //descriptor-binding index 0: image red source distribution
-    this->propagation_descriptor->add_binding(1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);  //descriptor-binding index 1: image green source distribution
-    this->propagation_descriptor->add_binding(2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);  //descriptor-binding index 2: image blue source distribution
-
-    this->propagation_descriptor->add_binding(3, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);  //descriptor-binding index 3: image red destination distribution
-    this->propagation_descriptor->add_binding(4, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);  //descriptor-binding index 4: image green destination distribution
-    this->propagation_descriptor->add_binding(5, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);  //descriptor-binding index 5: image blue destination distribution
+    this->propagation_descriptor->add_binding(0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);           //descriptor-binding index 0: image red source distribution
+    this->propagation_descriptor->add_binding(1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);           //descriptor-binding index 1: image green source distribution
+    this->propagation_descriptor->add_binding(2, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);           //descriptor-binding index 2: image blue source distribution
+        
+    this->propagation_descriptor->add_binding(3, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);           //descriptor-binding index 3: image red destination distribution
+    this->propagation_descriptor->add_binding(4, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);           //descriptor-binding index 4: image green destination distribution
+    this->propagation_descriptor->add_binding(5, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);           //descriptor-binding index 5: image blue destination distribution
     
-    this->propagation_descriptor->add_binding(6, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);  //descriptor-binding index 6: image red indirect
-    this->propagation_descriptor->add_binding(7, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);  //descriptor-binding index 7: image green indirect
-    this->propagation_descriptor->add_binding(8, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);  //descriptor-binding index 8: image blue indirect
+    this->propagation_descriptor->add_binding(6, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);           //descriptor-binding index 6: image red indirect
+    this->propagation_descriptor->add_binding(7, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);           //descriptor-binding index 7: image green indirect
+    this->propagation_descriptor->add_binding(8, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT);           //descriptor-binding index 8: image blue indirect
 
-    this->propagation_descriptor->add_binding(9, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT); //descriptor-binding index 9: indirect domain
+    this->propagation_descriptor->add_binding(9, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_COMPUTE_BIT);  //descriptor-binding index 9: image xz geometrz distribution
+    this->propagation_descriptor->add_binding(10, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_COMPUTE_BIT); //descriptor-binding index 10: image yw geometrz distribution
+
+    this->propagation_descriptor->add_binding(11, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT);         //descriptor-binding index 11: indirect domain
     
     if (!this->propagation_descriptor->create(device))
     {
@@ -855,8 +811,8 @@ bool IndirectCache::create_descriptors(lava::device_ptr device)
     std::vector<VkWriteDescriptorSet> descriptor_writes;
     std::vector<VkDescriptorImageInfo> image_infos;
 
-    descriptor_writes.reserve(33);
-    image_infos.reserve(28);
+    descriptor_writes.reserve(35);
+    image_infos.reserve(30);
 
     std::vector<VkImageView> indirect_images = 
     {
@@ -885,11 +841,11 @@ bool IndirectCache::create_descriptors(lava::device_ptr device)
         descriptor_write.pTexelBufferView = nullptr;
     }
 
-    for (uint32_t index = 0; index < this->distribution_geometry_image_component_view.size(); index++)
+    for (uint32_t index = 0; index < this->distribution_geometry_image_integer_view.size(); index++)
     {
         VkDescriptorImageInfo& image_info = image_infos.emplace_back();
         image_info.sampler = nullptr;
-        image_info.imageView = this->distribution_geometry_image_component_view[index];
+        image_info.imageView = this->distribution_geometry_image_integer_view[index];
         image_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
 
         VkWriteDescriptorSet& descriptor_write = descriptor_writes.emplace_back();
@@ -974,11 +930,31 @@ bool IndirectCache::create_descriptors(lava::device_ptr device)
             descriptor_write.pTexelBufferView = nullptr;
         }
 
+        for (uint32_t image_index = 0; image_index < this->distribution_geometry_image_view.size(); image_index++)
+        {
+            VkDescriptorImageInfo& image_info = image_infos.emplace_back();
+            image_info.sampler = this->geometry_sampler;
+            image_info.imageView = this->distribution_geometry_image_view[image_index];
+            image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+            VkWriteDescriptorSet& descriptor_write = descriptor_writes.emplace_back();
+            descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+            descriptor_write.pNext = nullptr;
+            descriptor_write.dstSet = this->propagation_descriptor_set[set_index];
+            descriptor_write.dstBinding = 9 + image_index;
+            descriptor_write.dstArrayElement = 0;
+            descriptor_write.descriptorCount = 1;
+            descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+            descriptor_write.pImageInfo = &image_info;
+            descriptor_write.pBufferInfo = nullptr;
+            descriptor_write.pTexelBufferView = nullptr;
+        }
+
         VkWriteDescriptorSet& propagation_domain_write = descriptor_writes.emplace_back();
         propagation_domain_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
         propagation_domain_write.pNext = nullptr;
         propagation_domain_write.dstSet = this->propagation_descriptor_set[set_index];
-        propagation_domain_write.dstBinding = 9;
+        propagation_domain_write.dstBinding = 11;
         propagation_domain_write.dstArrayElement = 0;
         propagation_domain_write.descriptorCount = 1;
         propagation_domain_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
@@ -1127,6 +1103,8 @@ bool IndirectCache::create_geometry_pipeline(lava::device_ptr device)
     this->geometry_pipeline = lava::make_graphics_pipeline(device);
     this->geometry_pipeline->set_layout(this->geometry_layout);
     this->geometry_pipeline->set_auto_size(false);
+    this->geometry_pipeline->set_viewport_count(3);
+    this->geometry_pipeline->set_scissor_count(3);
 
     scene->set_vertex_input(this->geometry_pipeline.get());
 
@@ -1449,29 +1427,33 @@ void IndirectCache::pipeline_geometry(VkCommandBuffer command_buffer)
 
     vkCmdPushConstants(command_buffer, this->geometry_layout->get(), VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(uint32_t) * push_constants.size(), push_constants.data());
 
+    std::array<glm::uvec2, 3> resolutions = 
+    {
+        glm::uvec2(this->domain_voxel_resolution.y, this->domain_voxel_resolution.z),
+        glm::uvec2(this->domain_voxel_resolution.x, this->domain_voxel_resolution.z),
+        glm::uvec2(this->domain_voxel_resolution.x, this->domain_voxel_resolution.y)
+    };
+
     std::array<VkViewport, 3> viewports;
-    viewports[0].x = 0.0f;
-    viewports[0].y = 0.0f;
-    viewports[0].width = this->domain_voxel_resolution.y;
-    viewports[0].height = this->domain_voxel_resolution.z;
-    viewports[0].minDepth = 0.0f;
-    viewports[0].maxDepth = 1.0f;
-
-    viewports[1].x = 0.0f;
-    viewports[1].y = 0.0f;
-    viewports[1].width = this->domain_voxel_resolution.x;
-    viewports[1].height = this->domain_voxel_resolution.z;
-    viewports[1].minDepth = 0.0f;
-    viewports[1].maxDepth = 1.0f;
-
-    viewports[2].x = 0.0f;
-    viewports[2].y = 0.0f;
-    viewports[2].width = this->domain_voxel_resolution.x;
-    viewports[2].height = this->domain_voxel_resolution.y;
-    viewports[2].minDepth = 0.0f;
-    viewports[2].maxDepth = 1.0f;
+    std::array<VkRect2D, 3> scissors;
+
+    for (uint32_t index = 0; index < resolutions.size(); index++)
+    {
+        viewports[index].x = 0.0f;
+        viewports[index].y = 0.0f;
+        viewports[index].width = resolutions[index].x;
+        viewports[index].height = resolutions[index].y;
+        viewports[index].minDepth = 0.0f;
+        viewports[index].maxDepth = 1.0f;
+
+        scissors[index].offset.x = 0;
+        scissors[index].offset.y = 0;
+        scissors[index].extent.width = resolutions[index].x;
+        scissors[index].extent.height = resolutions[index].y;
+    }
 
     vkCmdSetViewport(command_buffer, 0, 3, viewports.data());
+    vkCmdSetScissor(command_buffer, 0, 3, scissors.data());
 
     this->geometry_layout->bind(command_buffer, this->geometry_descriptor_set, 1);
 
@@ -1554,12 +1536,9 @@ VkImageMemoryBarrier IndirectCache::build_barrier(VkAccessFlags src_access, VkAc
     return barrier;
 }
 
-std::array<VkImageMemoryBarrier, 3> IndirectCache::build_barriers(VkAccessFlags src_access, VkAccessFlags dst_access, VkImageLayout old_layout, VkImageLayout new_layout)
+std::vector<VkImageMemoryBarrier> IndirectCache::build_barriers(uint32_t count, VkAccessFlags src_access, VkAccessFlags dst_access, VkImageLayout old_layout, VkImageLayout new_layout)
 {
-    std::array<VkImageMemoryBarrier, 3> barriers;
-    barriers.fill(build_barrier(src_access, dst_access, old_layout, new_layout));
-
-    return barriers;
+    return std::vector<VkImageMemoryBarrier>(count, IndirectCache::build_barrier(src_access, dst_access, old_layout, new_layout));
 }
 
 IndirectCache::Ptr make_indirect_cache()
diff --git a/src/utility/indirect_cache.hpp b/src/utility/indirect_cache.hpp
index 5cc5ccc9..1b469cb2 100644
--- a/src/utility/indirect_cache.hpp
+++ b/src/utility/indirect_cache.hpp
@@ -54,7 +54,7 @@ private:
     void pipeline_injection(VkCommandBuffer command_buffer);
 
     static VkImageMemoryBarrier build_barrier(VkAccessFlags src_access, VkAccessFlags dst_access, VkImageLayout old_layout, VkImageLayout new_layout);
-    static std::array<VkImageMemoryBarrier, 3> build_barriers(VkAccessFlags src_access, VkAccessFlags dst_access, VkImageLayout old_layout, VkImageLayout new_layout);
+    static std::vector<VkImageMemoryBarrier> build_barriers(uint32_t count, VkAccessFlags src_access, VkAccessFlags dst_access, VkImageLayout old_layout, VkImageLayout new_layout);
 
 private:
     Scene::Ptr scene;
@@ -109,21 +109,22 @@ private:
     std::array<VmaAllocation, 3> distribution_red_image_memory;
     std::array<VmaAllocation, 3> distribution_green_image_memory;
     std::array<VmaAllocation, 3> distribution_blue_image_memory;
-    std::array<VmaAllocation, 4> distribution_geometry_image_memory;
+    std::array<VmaAllocation, 2> distribution_geometry_image_memory;
 
     std::array<VkImage, 3> distribution_red_image;
     std::array<VkImage, 3> distribution_green_image;
     std::array<VkImage, 3> distribution_blue_image;
-    std::array<VkImage, 4> distribution_geometry_image;
+    std::array<VkImage, 2> distribution_geometry_image;
 
     std::array<VkImageView, 3> distribution_red_image_view;
     std::array<VkImageView, 3> distribution_green_image_view;
     std::array<VkImageView, 3> distribution_blue_image_view;
-    std::array<VkImageView, 4> distribution_geometry_image_view;
+    std::array<VkImageView, 2> distribution_geometry_image_view;
 
     std::array<VkImageView, 3> distribution_red_image_array_view;
     std::array<VkImageView, 3> distribution_green_image_array_view;
     std::array<VkImageView, 3> distribution_blue_image_array_view;
+    std::array<VkImageView, 2> distribution_geometry_image_integer_view;
     
     lava::buffer::ptr domain_buffer;
 };
diff --git a/src/vr_application.cpp b/src/vr_application.cpp
index 98860f5b..8ef1c8e3 100644
--- a/src/vr_application.cpp
+++ b/src/vr_application.cpp
@@ -68,6 +68,7 @@ bool VRApplication::setup(lava::name name, argh::parser cmd_line)
         parameters.features.geometryShader = true;
         parameters.features.tessellationShader = true;
         parameters.features.imageCubeArray = true;
+        parameters.features.fragmentStoresAndAtomics = true;
     };
 
 	this->app->on_create = [this]()
-- 
GitLab