diff --git a/src/utility/indirect_cache.cpp b/src/utility/indirect_cache.cpp
index 37205c4990c55d427faccbe18acdbc2bdbc485ca..1c0de87ec877d9a65a899bb6a15b24a6af82e5a1 100644
--- a/src/utility/indirect_cache.cpp
+++ b/src/utility/indirect_cache.cpp
@@ -20,6 +20,16 @@ bool IndirectCache::create(Scene::Ptr scene, const IndirectCacheSettings& settin
         return false;
     }
 
+    if (!this->create_command_buffer(device))
+    {
+        return false;
+    }
+
+    if (!this->create_fence(device))
+    {
+        return false;
+    }
+
     if (!this->create_buffers(device, settings))
     {
         return false;
@@ -192,6 +202,14 @@ void IndirectCache::destroy()
         this->domain_buffer->destroy();
     }
 
+    if (this->command_buffer != VK_NULL_HANDLE)
+    {
+        vkFreeCommandBuffers(device->get(), this->command_pool, 1, &this->command_buffer);
+    }
+
+    vkDestroyFence(device->get(), this->fence, lava::memory::alloc());
+    vkDestroyCommandPool(device->get(), this->command_pool, lava::memory::alloc());
+
     vkDestroySampler(device->get(), this->indirect_sampler, lava::memory::alloc());
     vkDestroySampler(device->get(), this->geometry_sampler, lava::memory::alloc());
     vkDestroySampler(device->get(), this->injection_sampler, lava::memory::alloc());
@@ -228,163 +246,24 @@ void IndirectCache::destroy()
 
 void IndirectCache::compute_indirect(VkCommandBuffer command_buffer, lava::index frame)
 {
-    const std::vector<SceneLight>& lights = this->scene->get_lights();
-
-    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, clear_begin_barriers.size(), clear_begin_barriers.data());
-
-    VkClearColorValue clear_color;
-    clear_color.float32[0] = 0.0f;
-    clear_color.float32[1] = 0.0f;
-    clear_color.float32[2] = 0.0f;
-    clear_color.float32[3] = 0.0f;
-
-    VkImageSubresourceRange subresource_range;
-    subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-    subresource_range.baseMipLevel = 0;
-    subresource_range.levelCount = 1;
-    subresource_range.baseArrayLayer = 0;
-    subresource_range.layerCount = 1;
-
-    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::vector<VkImageMemoryBarrier> clear_end_barriers = IndirectCache::build_barriers(3, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, 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_TRANSFER_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->submit_setup();
 
-    this->geometry_pass->process(command_buffer, 0);
+    uint32_t batch_size = 100;
+    uint32_t batch_count = this->propagation_iterations / batch_size;
+    uint32_t batch_rest = this->propagation_iterations % batch_size;
 
-    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++)
+    for (uint32_t index = 0; index < batch_count; index++)
     {
-        const SceneLight& light = lights[index];
-       
-        if (light.data.type != LIGHT_TYPE_DIRECTIONAL)
-        {
-            continue;
-        }
-        
-        this->frame = frame;
-        this->light_index = index;
-        this->capture_pass->process(command_buffer, 0);
-        this->injection_pass->process(command_buffer, 0);
-    }
+        lava::log()->debug("{}", 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::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];
-
-    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, copy_barriers.size(), copy_barriers.data());
-
-    VkImageSubresourceLayers subresource_layers;
-    subresource_layers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-    subresource_layers.mipLevel = 0;
-    subresource_layers.baseArrayLayer = 0;
-    subresource_layers.layerCount = 1;
-
-    VkOffset3D offset;
-    offset.x = 0;
-    offset.y = 0;
-    offset.z = 0;
-
-    VkExtent3D extend;
-    extend.width = this->domain_indirect_resolution.x;
-    extend.height = this->domain_indirect_resolution.y;
-    extend.depth = this->domain_indirect_resolution.z;
-
-    VkImageCopy image_copy;
-    image_copy.srcSubresource = subresource_layers;
-    image_copy.srcOffset = offset;
-    image_copy.dstSubresource = subresource_layers;
-    image_copy.dstOffset = offset;
-    image_copy.extent = extend;
-
-    vkCmdCopyImage(command_buffer, this->distribution_red_image[0], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, this->distribution_red_image[2], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
-    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::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::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::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];
-
-    vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, setup_indirect_barriers.size(), setup_indirect_barriers.data());
-
-    this->propagation_pipeline->bind(command_buffer);
+        this->submit_propagation(batch_size);    
+    }
 
-    for (uint32_t index = 0; index < this->propagation_iterations; index++)
+    if (batch_rest > 0)
     {
-        uint32_t src_index = index % 2;
-        uint32_t dst_index = (index + 1) % 2;
-
-        this->propagation_layout->bind(command_buffer, this->propagation_descriptor_set[src_index], 0, {}, VK_PIPELINE_BIND_POINT_COMPUTE);
-
-        vkCmdDispatch(command_buffer, this->domain_work_groups.x, this->domain_work_groups.y, this->domain_work_groups.z);
-        
-        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::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::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];
-
-        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());
+        this->submit_propagation(batch_rest);
     }
-
+    
     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];
@@ -452,6 +331,55 @@ bool IndirectCache::compute_domain(Scene::Ptr scene, const IndirectCacheSettings
     return true;
 }
 
+bool IndirectCache::create_command_buffer(lava::device_ptr device)
+{
+    VkCommandPoolCreateInfo pool_info;
+    pool_info.sType  = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+    pool_info.pNext = nullptr;
+    pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+    pool_info.queueFamilyIndex = device->get_graphics_queue().family;
+
+    if(vkCreateCommandPool(device->get(), &pool_info, lava::memory::alloc(), &this->command_pool) != VK_SUCCESS)
+    {
+        lava::log()->error("Can't create command pool for indirect cache!");
+
+        return false;
+    }
+
+    VkCommandBufferAllocateInfo buffer_info;
+    buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+    buffer_info.pNext = nullptr;
+    buffer_info.commandPool = this->command_pool;
+    buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+    buffer_info.commandBufferCount = 1;
+
+    if (vkAllocateCommandBuffers(device->get(), &buffer_info, &this->command_buffer) != VK_SUCCESS)
+    {
+        lava::log()->error("Can't create command buffer for indirect cache!");
+
+        return false;
+    }
+
+    return true;
+}
+
+bool IndirectCache::create_fence(lava::device_ptr device)
+{
+    VkFenceCreateInfo fence_info;
+    fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+    fence_info.pNext = nullptr;
+    fence_info.flags = 0;
+
+    if (vkCreateFence(device->get(), &fence_info, lava::memory::alloc(), &this->fence) != VK_SUCCESS)
+    {
+        lava::log()->error("Can't create fence for indirect cache!");
+
+        return false;
+    }
+
+    return true;
+}
+
 bool IndirectCache::create_buffers(lava::device_ptr device, const IndirectCacheSettings& settings)
 {
     glsl::IndirectDomain indirect_domain;
@@ -1450,6 +1378,281 @@ bool IndirectCache::create_propagation_pipeline(lava::device_ptr device)
     return true;
 }
 
+bool IndirectCache::submit_setup()
+{
+    VkCommandBufferBeginInfo begin_info;
+    begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+    begin_info.pNext = nullptr;
+    begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+    begin_info.pInheritanceInfo = nullptr;
+
+    if (vkBeginCommandBuffer(this->command_buffer, &begin_info) != VK_SUCCESS)
+    {
+        lava::log()->error("Can't begin command buffer during setup of indirect cache!");
+
+        return false;
+    }
+
+    const std::vector<SceneLight>& lights = this->scene->get_lights();
+
+    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(this->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;
+    clear_color.float32[1] = 0.0f;
+    clear_color.float32[2] = 0.0f;
+    clear_color.float32[3] = 0.0f;
+
+    VkImageSubresourceRange subresource_range;
+    subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    subresource_range.baseMipLevel = 0;
+    subresource_range.levelCount = 1;
+    subresource_range.baseArrayLayer = 0;
+    subresource_range.layerCount = 1;
+
+    vkCmdClearColorImage(this->command_buffer, this->distribution_red_image[0], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range);
+    vkCmdClearColorImage(this->command_buffer, this->distribution_green_image[0], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range);
+    vkCmdClearColorImage(this->command_buffer, this->distribution_blue_image[0], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range);
+    vkCmdClearColorImage(this->command_buffer, this->distribution_geometry_image[0], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range);
+    vkCmdClearColorImage(this->command_buffer, this->distribution_geometry_image[1], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &subresource_range);
+
+    std::vector<VkImageMemoryBarrier> clear_end_barriers = IndirectCache::build_barriers(3, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, 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(this->command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_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(this->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(this->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(this->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];
+       
+        if (light.data.type != LIGHT_TYPE_DIRECTIONAL)
+        {
+            continue;
+        }
+        
+        this->frame = frame;
+        this->light_index = index;
+        this->capture_pass->process(this->command_buffer, 0);
+        this->injection_pass->process(this->command_buffer, 0);
+    }
+
+    //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::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];
+
+    vkCmdPipelineBarrier(this->command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, copy_barriers.size(), copy_barriers.data());
+
+    VkImageSubresourceLayers subresource_layers;
+    subresource_layers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    subresource_layers.mipLevel = 0;
+    subresource_layers.baseArrayLayer = 0;
+    subresource_layers.layerCount = 1;
+
+    VkOffset3D offset;
+    offset.x = 0;
+    offset.y = 0;
+    offset.z = 0;
+
+    VkExtent3D extend;
+    extend.width = this->domain_indirect_resolution.x;
+    extend.height = this->domain_indirect_resolution.y;
+    extend.depth = this->domain_indirect_resolution.z;
+
+    VkImageCopy image_copy;
+    image_copy.srcSubresource = subresource_layers;
+    image_copy.srcOffset = offset;
+    image_copy.dstSubresource = subresource_layers;
+    image_copy.dstOffset = offset;
+    image_copy.extent = extend;
+
+    vkCmdCopyImage(this->command_buffer, this->distribution_red_image[0], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, this->distribution_red_image[2], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
+    vkCmdCopyImage(this->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(this->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::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(this->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::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(this->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::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];
+
+    vkCmdPipelineBarrier(this->command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, setup_indirect_barriers.size(), setup_indirect_barriers.data());
+
+    if (vkEndCommandBuffer(this->command_buffer) != VK_SUCCESS)
+    {
+        lava::log()->error("Can't end command buffer during setup of indirect cache!");
+
+        return false;
+    }
+
+    lava::device_ptr device = this->scene->get_light_descriptor()->get_device();
+
+    if (vkResetFences(device->get(), 1, &this->fence) != VK_SUCCESS)
+    {
+        lava::log()->error("Can't reset fence during setup of indirect cache!");
+
+        return false;
+    }
+
+    VkSubmitInfo submit_info;
+    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+    submit_info.pNext = nullptr;
+    submit_info.waitSemaphoreCount = 0;
+    submit_info.pWaitSemaphores = nullptr;
+    submit_info.pWaitDstStageMask = nullptr;
+    submit_info.commandBufferCount = 1;
+    submit_info.pCommandBuffers = &this->command_buffer;
+    submit_info.signalSemaphoreCount = 0;
+    submit_info.pSignalSemaphores = nullptr;
+
+    if (vkQueueSubmit(device->get_graphics_queue().vk_queue, 1, &submit_info, this->fence) != VK_SUCCESS)
+    {
+        lava::log()->error("Can't submit command buffer during setup of indirect cache!");
+
+        return false;
+    }
+
+    if (vkWaitForFences(device->get(), 1, &this->fence, VK_TRUE, std::numeric_limits<uint64_t>::max()) != VK_SUCCESS)
+    {
+        lava::log()->error("Can't wait for completion indirect cache setup!");
+
+        return false;
+    }
+
+    return true;
+}
+
+bool IndirectCache::submit_propagation(uint32_t iterations)
+{
+    VkCommandBufferBeginInfo begin_info;
+    begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+    begin_info.pNext = nullptr;
+    begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+    begin_info.pInheritanceInfo = nullptr;
+
+    if (vkBeginCommandBuffer(this->command_buffer, &begin_info) != VK_SUCCESS)
+    {
+        lava::log()->error("Can't begin command buffer during propagation of indirect cache!");
+
+        return false;
+    }
+
+    this->propagation_pipeline->bind(this->command_buffer);
+
+    for (uint32_t index = 0; index < iterations; index++)
+    {
+        uint32_t src_index = index % 2;
+        uint32_t dst_index = (index + 1) % 2;
+
+        this->propagation_layout->bind(this->command_buffer, this->propagation_descriptor_set[src_index], 0, {}, VK_PIPELINE_BIND_POINT_COMPUTE);
+
+        vkCmdDispatch(this->command_buffer, this->domain_work_groups.x, this->domain_work_groups.y, this->domain_work_groups.z);
+        
+        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(this->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::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(this->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::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];
+
+        vkCmdPipelineBarrier(this->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());
+    }
+
+    if (vkEndCommandBuffer(this->command_buffer) != VK_SUCCESS)
+    {
+        lava::log()->error("Can't end command buffer during propagation of indirect cache!");
+
+        return false;
+    }
+
+    lava::device_ptr device = this->scene->get_light_descriptor()->get_device();
+
+    if (vkResetFences(device->get(), 1, &this->fence) != VK_SUCCESS)
+    {
+        lava::log()->error("Can't reset fence during propagation of indirect cache!");
+
+        return false;
+    }
+
+    VkSubmitInfo submit_info;
+    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+    submit_info.pNext = nullptr;
+    submit_info.waitSemaphoreCount = 0;
+    submit_info.pWaitSemaphores = nullptr;
+    submit_info.pWaitDstStageMask = nullptr;
+    submit_info.commandBufferCount = 1;
+    submit_info.pCommandBuffers = &this->command_buffer;
+    submit_info.signalSemaphoreCount = 0;
+    submit_info.pSignalSemaphores = nullptr;
+
+    if (vkQueueSubmit(device->get_graphics_queue().vk_queue, 1, &submit_info, this->fence) != VK_SUCCESS)
+    {
+        lava::log()->error("Can't submit command buffer during propagation of indirect cache!");
+
+        return false;
+    }
+
+    if (vkWaitForFences(device->get(), 1, &this->fence, VK_TRUE, std::numeric_limits<uint64_t>::max()) != VK_SUCCESS)
+    {
+        lava::log()->error("Can't wait for completion indirect propagation setup!");
+
+        return false;
+    }
+
+    return true;
+}
+
 void IndirectCache::pipeline_geometry(VkCommandBuffer command_buffer)
 {
     std::array<uint32_t, 3> push_constants;
diff --git a/src/utility/indirect_cache.hpp b/src/utility/indirect_cache.hpp
index 1f5563eb04e9fa4f5f50d9f0b32b2dc2c2a9724c..e6cebb150b30e17e7186ce57dbd70a06ba7c4762 100644
--- a/src/utility/indirect_cache.hpp
+++ b/src/utility/indirect_cache.hpp
@@ -45,7 +45,7 @@ struct IndirectCacheSettings
 {
     uint32_t capture_resolution = 1024;
     uint32_t voxel_resolution_scale = 1; //Needs to be greater or equal to one
-    float cell_size = 1.0f; //Where cell size is in meters
+    float cell_size = 0.5f; //Where cell size is in meters
 };
 
 class IndirectCache
@@ -59,7 +59,7 @@ public:
     bool create(Scene::Ptr scene, const IndirectCacheSettings& settings);
     void destroy();
 
-    void compute_indirect(VkCommandBuffer command_buffer, lava::index frame);
+    void compute_indirect(VkCommandBuffer comannd_buffer, lava::index frame);
 
     lava::descriptor::ptr get_descriptor() const;
     VkDescriptorSet get_descriptor_set() const;
@@ -69,6 +69,8 @@ public:
 private:
     bool compute_domain(Scene::Ptr scene, const IndirectCacheSettings& settings);
 
+    bool create_command_buffer(lava::device_ptr device);
+    bool create_fence(lava::device_ptr device);
     bool create_buffers(lava::device_ptr device, const IndirectCacheSettings& settings);
     bool create_images(lava::device_ptr device, const IndirectCacheSettings& settings);
     bool create_samplers(lava::device_ptr device);
@@ -82,6 +84,9 @@ private:
     bool create_injection_pipeline(lava::device_ptr device);
     bool create_propagation_pipeline(lava::device_ptr device);
    
+    bool submit_setup();
+    bool submit_propagation(uint32_t iterations); //NOTE: Iterations needs to be multiple of two
+
     void pipeline_geometry(VkCommandBuffer command_buffer);
     void pipeline_capture(VkCommandBuffer command_buffer);
     void pipeline_injection(VkCommandBuffer command_buffer);
@@ -105,6 +110,10 @@ private:
     glm::uvec3 domain_voxel_resolution = glm::uvec3(0);
     glm::uvec3 domain_work_groups = glm::uvec3(0);
 
+    VkCommandPool command_pool = VK_NULL_HANDLE;
+    VkCommandBuffer command_buffer = VK_NULL_HANDLE;
+    VkFence fence = VK_NULL_HANDLE;
+
     lava::descriptor::pool::ptr descriptor_pool;
     lava::descriptor::ptr indirect_descriptor;
     lava::descriptor::ptr geometry_descriptor;