diff --git a/.gitmodules b/.gitmodules
index 1902397c4be138a3219453d01082c9b42c59e1bc..0f788015915ea014037a674cd19deccaea0279f2 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -8,7 +8,7 @@
 	branch = main
 [submodule "modules/asset_loader/lib/fx-gltf"]
 	path = modules/asset_loader/lib/fx-gltf
-	url = https://github.com/jessey-git/fx-gltf.git
+	url = https://github.com/TheJackiMonster/fx-gltf.git
 	branch = master
 [submodule "modules/asset_loader/lib/json"]
 	path = modules/asset_loader/lib/json
@@ -48,8 +48,8 @@
 	branch = main
 [submodule "lib/VulkanMemoryAllocator-Hpp"]
 	path = lib/VulkanMemoryAllocator-Hpp
-	url = https://github.com/TheJackiMonster/VulkanMemoryAllocator-Hpp.git
-	branch = fix
+	url = https://github.com/YaaZ/VulkanMemoryAllocator-Hpp.git
+	branch = master
 [submodule "modules/algorithm/lib/FidelityFX-SPD"]
 	path = modules/algorithm/lib/FidelityFX-SPD
 	url = https://github.com/GPUOpen-Effects/FidelityFX-SPD.git
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index 5939988c3dc41f4ea4c640ed1bb30795fec8a36c..b97cf4eea750cbcc038e06c1a38df2d1aaa96e8e 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -73,10 +73,6 @@ namespace vkcv {
 
 		Context m_Context;
 
-		std::unique_ptr<PassManager> m_PassManager;
-		std::unique_ptr<GraphicsPipelineManager> m_GraphicsPipelineManager;
-		std::unique_ptr<ComputePipelineManager> m_ComputePipelineManager;
-		std::unique_ptr<RayTracingPipelineManager> m_RayTracingPipelineManager;
 		std::unique_ptr<DescriptorSetLayoutManager> m_DescriptorSetLayoutManager;
 		std::unique_ptr<DescriptorSetManager> m_DescriptorSetManager;
 		std::unique_ptr<BufferManager> m_BufferManager;
@@ -86,11 +82,16 @@ namespace vkcv {
 		std::unique_ptr<CommandStreamManager> m_CommandStreamManager;
 		std::unique_ptr<WindowManager> m_WindowManager;
 		std::unique_ptr<SwapchainManager> m_SwapchainManager;
+		std::unique_ptr<PassManager> m_PassManager;
+		std::unique_ptr<GraphicsPipelineManager> m_GraphicsPipelineManager;
+		std::unique_ptr<ComputePipelineManager> m_ComputePipelineManager;
+		std::unique_ptr<RayTracingPipelineManager> m_RayTracingPipelineManager;
 		
 		Vector<vk::CommandPool> m_CommandPools;
 		vk::Semaphore m_RenderFinished;
-		vk::Semaphore m_SwapchainImageAcquired;
+		std::vector<vk::Semaphore> m_SwapchainImagesAcquired;
 		uint32_t m_currentSwapchainImageIndex;
+		uint32_t m_currentSwapchainSemaphoreIndex;
 
 		std::unique_ptr<Downsampler> m_downsampler;
 
diff --git a/include/vkcv/Event.hpp b/include/vkcv/Event.hpp
index 328b0e51ff4b3c78c3926083b71dbdcb6b98afa0..cb508d0c3fa7c739865348c4f7b25a2ab13ceeeb 100644
--- a/include/vkcv/Event.hpp
+++ b/include/vkcv/Event.hpp
@@ -5,6 +5,7 @@
  * @brief Template event struct to synchronize callbacks.
  */
 
+#include <algorithm>
 #include <functional>
 
 #ifndef __MINGW32__
diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp
index c338cac3b0ff9052f832229d4ee04cf0c629ec96..f35eeaa32b903ae65756635ffa34fe16919537a6 100644
--- a/include/vkcv/Handles.hpp
+++ b/include/vkcv/Handles.hpp
@@ -28,6 +28,11 @@ namespace vkcv {
 
 		HandleDestroyFunction m_destroy;
 
+		/**
+		 * @brief Private internal method to destroy handle.
+		 */
+		void destroy();
+
 	protected:
 		/**
 		 * @brief Constructor of an invalid handle
diff --git a/modules/gui/src/vkcv/gui/GUI.cpp b/modules/gui/src/vkcv/gui/GUI.cpp
index 15a1b049aa5496bdcb9ecb81c00f82b66ad86d71..7978b10a4a515b022868fb357b6aa3f123a3d7e0 100644
--- a/modules/gui/src/vkcv/gui/GUI.cpp
+++ b/modules/gui/src/vkcv/gui/GUI.cpp
@@ -90,7 +90,7 @@ namespace vkcv::gui {
 		init_info.MinImageCount = swapchainImageCount;
 		init_info.ImageCount = swapchainImageCount;
 		init_info.CheckVkResultFn = checkVulkanResult;
-		
+
 		const vk::AttachmentDescription attachment (
 				vk::AttachmentDescriptionFlags(),
 				m_core.getSwapchainFormat(swapchainHandle),
@@ -141,19 +141,13 @@ namespace vkcv::gui {
 				&dependency
 		);
 		
-		m_render_pass = m_context.getDevice().createRenderPass(passCreateInfo);
-		
-		ImGui_ImplVulkan_Init(&init_info, static_cast<VkRenderPass>(m_render_pass));
+		init_info.RenderPass = m_context.getDevice().createRenderPass(passCreateInfo);
 		
-		auto stream = m_core.createCommandStream(QueueType::Graphics);
-		
-		m_core.recordCommandsToStream(stream, [](const vk::CommandBuffer& commandBuffer) {
-			ImGui_ImplVulkan_CreateFontsTexture(static_cast<VkCommandBuffer>(commandBuffer));
-		}, []() {
-			ImGui_ImplVulkan_DestroyFontUploadObjects();
-		});
-		
-		m_core.submitCommandStream(stream, false);
+		ImGui_ImplVulkan_Init(&init_info);
+		ImGui_ImplVulkan_CreateFontsTexture();
+
+		m_render_pass = init_info.RenderPass;
+
 		m_context.getDevice().waitIdle();
 	}
 	
@@ -161,6 +155,7 @@ namespace vkcv::gui {
 		m_context.getDevice().waitIdle();
 		Window& window = m_core.getWindow(m_windowHandle);
 
+		ImGui_ImplVulkan_DestroyFontsTexture();
 		ImGui_ImplVulkan_Shutdown();
 		
 		m_context.getDevice().destroyRenderPass(m_render_pass);
@@ -188,6 +183,7 @@ namespace vkcv::gui {
 		
 		ImGui_ImplVulkan_NewFrame();
 		ImGui_ImplGlfw_NewFrame();
+		
 		ImGui::NewFrame();
 	}
 	
diff --git a/modules/shader_compiler/config/JSON-C.cmake b/modules/shader_compiler/config/JSON-C.cmake
index 498b933cafb5d8bd84faed49cf51e6eef9c7503b..eb8b6ab74a2614b80db033fd399ca10324ed0615 100644
--- a/modules/shader_compiler/config/JSON-C.cmake
+++ b/modules/shader_compiler/config/JSON-C.cmake
@@ -3,6 +3,8 @@ use_git_submodule("${vkcv_shader_compiler_lib_path}/json-c" json_c_status)
 
 if (${json_c_status})
 	add_subdirectory(${vkcv_shader_compiler_lib}/json-c)
+
+	set(JSON_C_INCLUDE_DIR ${vkcv_shader_compiler_lib_path})
 	
 	list(APPEND vkcv_shader_compiler_libraries json-c)
 	list(APPEND vkcv_shader_compiler_includes ${vkcv_shader_compiler_lib})
diff --git a/modules/shader_compiler/config/Shady.cmake b/modules/shader_compiler/config/Shady.cmake
index b5d3bf6ca2a04209806b571da740efd9ca5e0cdc..56da131a23e3680e487c678260de6af2c9c3aba6 100644
--- a/modules/shader_compiler/config/Shady.cmake
+++ b/modules/shader_compiler/config/Shady.cmake
@@ -6,6 +6,11 @@ if (${shady_status})
     set(EXTERNAL_SPIRV_HEADERS ON CACHE INTERNAL "")
     set(EXTERNAL_MURMUR3 ON CACHE INTERNAL "")
 
+	set(EXTERNAL_JSON_C_INCLUDE ${JSON_C_INCLUDE_DIR} CACHE INTERNAL "")
+
+	set(BUILD_RUNTIME OFF CACHE INTERNAL "")
+	set(BUILD_SAMPLES OFF CACHE INTERNAL "")
+
 	add_subdirectory(${vkcv_shader_compiler_lib}/shady)
 	
 	if (vkcv_build_attribute EQUAL "SHARED")
diff --git a/modules/shader_compiler/src/vkcv/shader/GlslangCompiler.cpp b/modules/shader_compiler/src/vkcv/shader/GlslangCompiler.cpp
index 6a16e2eba9db00d45c0802f865525875edd33123..2a2847ada699364c025f957b204b58b3190a8ed2 100644
--- a/modules/shader_compiler/src/vkcv/shader/GlslangCompiler.cpp
+++ b/modules/shader_compiler/src/vkcv/shader/GlslangCompiler.cpp
@@ -4,7 +4,8 @@
 #include <vkcv/File.hpp>
 #include <vkcv/Logger.hpp>
 
-#include <glslang/Public/ShaderLang.h>
+#include <glslang/SPIRV/GlslangToSpv.h>
+#include <glslang/glslang/Public/ShaderLang.h>
 
 namespace vkcv::shader {
 	
diff --git a/modules/shader_compiler/src/vkcv/shader/LLVMCompiler.cpp b/modules/shader_compiler/src/vkcv/shader/LLVMCompiler.cpp
index c53904d402efd7117b6fc73356284624445c4f5c..e415e19a70729e5288ce64f15a1e386cd7b963ca 100644
--- a/modules/shader_compiler/src/vkcv/shader/LLVMCompiler.cpp
+++ b/modules/shader_compiler/src/vkcv/shader/LLVMCompiler.cpp
@@ -47,7 +47,7 @@ namespace vkcv::shader {
 		DriverConfig config = default_driver_config();
 
         config.target = TgtSPV;
-		config.output_filename = tmp_path.c_str();
+		config.output_filename = tmp_path.string().c_str();
 
 		codes = driver_compile(&config, module);
 		destroy_driver_config(&config);
diff --git a/modules/shader_compiler/src/vkcv/shader/SlimCompiler.cpp b/modules/shader_compiler/src/vkcv/shader/SlimCompiler.cpp
index 475d867b883d82a5c811e7ea1081b8fc45dfa0c6..028d0618940d27cf00af920653e87d89f3ff7025 100644
--- a/modules/shader_compiler/src/vkcv/shader/SlimCompiler.cpp
+++ b/modules/shader_compiler/src/vkcv/shader/SlimCompiler.cpp
@@ -47,7 +47,7 @@ namespace vkcv::shader {
 		DriverConfig config = default_driver_config();
 
         config.target = TgtSPV;
-		config.output_filename = tmp_path.c_str();
+		config.output_filename = tmp_path.string().c_str();
 
 		codes = driver_compile(&config, module);
 		destroy_driver_config(&config);
diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp
index f98b6209cca74011ec8170236f0eb1d20c0c83ef..e01949c8bf89573983f188cb715ac0a8d9bdb6db 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -53,17 +53,15 @@ int main(int argc, const char** argv) {
 	// recreate copies of the bindings and the handles (to check whether they are properly reused instead of actually recreated)
 	const vkcv::DescriptorBindings& set0Bindings = firstMeshProgram.getReflectedDescriptors().at(0);
 
-	vkcv::DescriptorSetLayoutHandle setLayoutHandle = core.createDescriptorSetLayout(set0Bindings);
-	vkcv::DescriptorSetLayoutHandle setLayoutHandleCopy = core.createDescriptorSetLayout(set0Bindings);
-
-	vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(setLayoutHandle);
+	vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout(set0Bindings);
+	vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout);
 	
 	vkcv::GraphicsPipelineHandle firstMeshPipeline = core.createGraphicsPipeline(
 			vkcv::GraphicsPipelineConfig(
 					firstMeshProgram,
 					firstMeshPass,
 					{ firstMeshLayout },
-					{ setLayoutHandle }
+					{ descriptorSetLayout }
 			)
 	);
 	
diff --git a/projects/indirect_draw/src/main.cpp b/projects/indirect_draw/src/main.cpp
index 424f2e0708a38d60426aff78a61d4ea0af7bad5f..c24dd260812b7837742089eeedb8d3b3de2a42af 100644
--- a/projects/indirect_draw/src/main.cpp
+++ b/projects/indirect_draw/src/main.cpp
@@ -279,7 +279,6 @@ int main(int argc, const char** argv) {
     });
 
 	features.requireExtension(VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME);
-    features.requireExtension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
     features.requireExtensionFeature<vk::PhysicalDeviceDescriptorIndexingFeatures>(
             VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, [](vk::PhysicalDeviceDescriptorIndexingFeatures &features) {
                 // features.setShaderInputAttachmentArrayDynamicIndexing(true);
diff --git a/projects/mesh_shader/assets/shaders/shader.task b/projects/mesh_shader/assets/shaders/shader.task
index 5a104fd9803826cb52087039f3f1f72de577299d..58698733439c3987fb46a13e56b4df6255f0516d 100644
--- a/projects/mesh_shader/assets/shaders/shader.task
+++ b/projects/mesh_shader/assets/shaders/shader.task
@@ -14,8 +14,7 @@ layout( push_constant ) uniform constants {
     uint matrixIndex;
 };
 
-// TODO: reuse mesh stage binding at location 2 after required fix in framework
-layout(std430, set=0, binding = 5) readonly buffer meshletBuffer {
+layout(std430, set=0, binding = 2) readonly buffer meshletBuffer {
     Meshlet meshlets[];
 };
 
diff --git a/projects/mesh_shader/src/main.cpp b/projects/mesh_shader/src/main.cpp
index 72b15e3176a9a5e3614a9238fefe618a19070ba0..c248ac1c575613b273baf231d722253c731f9277 100644
--- a/projects/mesh_shader/src/main.cpp
+++ b/projects/mesh_shader/src/main.cpp
@@ -302,15 +302,13 @@ int main(int argc, const char** argv) {
 			2, meshletBuffer.getHandle()
 	).writeStorageBuffer(
 			4, matrixBuffer.getHandle()
-	).writeStorageBuffer(
-			5, meshletBuffer.getHandle()
 	);
 	
 	meshShaderWrites.writeUniformBuffer(3, cameraPlaneBuffer.getHandle());
 
-    core.writeDescriptorSet( meshShaderDescriptorSet, meshShaderWrites);
+  core.writeDescriptorSet( meshShaderDescriptorSet, meshShaderWrites);
 
-    vkcv::ImageHandle depthBuffer;
+  vkcv::ImageHandle depthBuffer;
 	vkcv::ImageHandle swapchainImageHandle = vkcv::ImageHandle::createSwapchainImageHandle();
 
 	vkcv::VertexData vertexData (vertexBufferBindings);
diff --git a/projects/particle_simulation/src/main.cpp b/projects/particle_simulation/src/main.cpp
index 09cb702809f9f7c48105d8b95b4f398f2bbeb014..3411cdf6cb107d9e3cf371e6a581a368e4e48f27 100644
--- a/projects/particle_simulation/src/main.cpp
+++ b/projects/particle_simulation/src/main.cpp
@@ -16,12 +16,15 @@ int main(int argc, const char **argv) {
 
     uint32_t windowWidth = 800;
     uint32_t windowHeight = 600;
+
+    vkcv::Features features;
+	features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
 	
     vkcv::Core core = vkcv::Core::create(
             applicationName,
             VK_MAKE_VERSION(0, 0, 1),
             {vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute},
-			{ VK_KHR_SWAPCHAIN_EXTENSION_NAME }
+			features
     );
 	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true);
     vkcv::Window& window = core.getWindow(windowHandle);
diff --git a/projects/path_tracer/src/main.cpp b/projects/path_tracer/src/main.cpp
index 1a87015e58aee80feebb4bc5fafac0a482da5743..21d83a689a442c90bf434597890e7e910767ca70 100644
--- a/projects/path_tracer/src/main.cpp
+++ b/projects/path_tracer/src/main.cpp
@@ -47,11 +47,14 @@ int main(int argc, const char** argv) {
 
 	const std::string applicationName = "Path Tracer";
 
+	vkcv::Features features;
+	features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+
 	vkcv::Core core = vkcv::Core::create(
 		applicationName,
 		VK_MAKE_VERSION(0, 0, 1),
 		{ vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute },
-		{ "VK_KHR_swapchain" }
+		features
 	);
 	
 	const int initialWidth = 1280;
diff --git a/projects/ray_tracer/src/main.cpp b/projects/ray_tracer/src/main.cpp
index 1a906d5d8141148acbbf767163fb173f8053d466..a920c3a55907f0587290b91cae64263a0f87c3b0 100644
--- a/projects/ray_tracer/src/main.cpp
+++ b/projects/ray_tracer/src/main.cpp
@@ -35,11 +35,14 @@ int main(int argc, const char** argv) {
 	const int windowWidth = 800;
 	const int windowHeight = 600;
 
+	vkcv::Features features;
+	features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+
 	vkcv::Core core = vkcv::Core::create(
 		applicationName,
 		VK_MAKE_VERSION(0, 0, 1),
 		{ vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute },
-		{ "VK_KHR_swapchain" }
+		features
 	);
 
 	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true);
diff --git a/projects/sph/src/main.cpp b/projects/sph/src/main.cpp
index 3d8cf07842e542036125c26a2f95f298ec043343..ac02278046260215f3ad2b1077c750f5d6961dd0 100644
--- a/projects/sph/src/main.cpp
+++ b/projects/sph/src/main.cpp
@@ -16,12 +16,15 @@
 int main(int argc, const char **argv) {
     const std::string applicationName = "SPH";
 
+    vkcv::Features features;
+	features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+
     // creating core object that will handle all vulkan objects
     vkcv::Core core = vkcv::Core::create(
         applicationName,
         VK_MAKE_VERSION(0, 0, 1),
         { vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute },
-        { VK_KHR_SWAPCHAIN_EXTENSION_NAME }
+        features
     );
 
     vkcv::WindowHandle windowHandle = core.createWindow(applicationName, 1280, 720, true);
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index 9a6492b80febb61e38ee3a90f29921d5ca7ed9ba..835bbf27f64e286ba1c097ecf137703fb73cb8b2 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -37,6 +37,7 @@ int main(int argc, const char** argv) {
 			VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME,
 			[](vk::PhysicalDeviceDescriptorIndexingFeatures& features) {
 				features.setDescriptorBindingPartiallyBound(true);
+				features.setDescriptorBindingVariableDescriptorCount(true);
 			}
 	);
 	
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 9f1274b2532fbea3da59466bf42360d83c9a0ec0..3cefdab895e1f5fb4fc34c2c1f4332f62e86464b 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -6,6 +6,9 @@
 
 #include <GLFW/glfw3.h>
 #include <cmath>
+#include <cstdint>
+#include <limits>
+#include <vkcv/Logger.hpp>
 
 #include "AccelerationStructureManager.hpp"
 #include "BufferManager.hpp"
@@ -91,10 +94,7 @@ namespace vkcv {
 	}
 
 	Core::Core(Context &&context) noexcept :
-		m_Context(std::move(context)), m_PassManager(std::make_unique<PassManager>()),
-		m_GraphicsPipelineManager(std::make_unique<GraphicsPipelineManager>()),
-		m_ComputePipelineManager(std::make_unique<ComputePipelineManager>()),
-		m_RayTracingPipelineManager(std::make_unique<RayTracingPipelineManager>()),
+		m_Context(std::move(context)),
 		m_DescriptorSetLayoutManager(std::make_unique<DescriptorSetLayoutManager>()),
 		m_DescriptorSetManager(std::make_unique<DescriptorSetManager>()),
 		m_BufferManager(std::make_unique<BufferManager>()),
@@ -103,18 +103,22 @@ namespace vkcv {
 		m_AccelerationStructureManager(std::make_unique<AccelerationStructureManager>()),
 		m_CommandStreamManager { std::make_unique<CommandStreamManager>() },
 		m_WindowManager(std::make_unique<WindowManager>()),
-		m_SwapchainManager(std::make_unique<SwapchainManager>()), m_CommandPools(),
-		m_RenderFinished(), m_SwapchainImageAcquired(), m_downsampler(nullptr) {
+		m_SwapchainManager(std::make_unique<SwapchainManager>()),
+		m_PassManager(std::make_unique<PassManager>()),
+		m_GraphicsPipelineManager(std::make_unique<GraphicsPipelineManager>()),
+		m_ComputePipelineManager(std::make_unique<ComputePipelineManager>()),
+		m_RayTracingPipelineManager(std::make_unique<RayTracingPipelineManager>()),
+		m_CommandPools(),
+		m_RenderFinished(),
+		m_SwapchainImagesAcquired(),
+		m_currentSwapchainImageIndex(std::numeric_limits<uint32_t>::max()),
+		m_currentSwapchainSemaphoreIndex(0),
+		m_downsampler(nullptr) {
 		m_CommandPools = createCommandPools(
 			m_Context.getDevice(), generateQueueFamilyIndexSet(m_Context.getQueueManager()));
 
 		m_RenderFinished = m_Context.getDevice().createSemaphore({});
-		m_SwapchainImageAcquired = m_Context.getDevice().createSemaphore({});
 
-		m_PassManager->init(*this);
-		m_GraphicsPipelineManager->init(*this);
-		m_ComputePipelineManager->init(*this);
-		m_RayTracingPipelineManager->init(*this);
 		m_DescriptorSetLayoutManager->init(*this);
 		m_DescriptorSetManager->init(*this, *m_DescriptorSetLayoutManager);
 		m_BufferManager->init(*this);
@@ -123,6 +127,10 @@ namespace vkcv {
 		m_AccelerationStructureManager->init(*this, *m_BufferManager);
 		m_CommandStreamManager->init(*this);
 		m_SwapchainManager->init(*this);
+		m_PassManager->init(*this);
+		m_GraphicsPipelineManager->init(*this);
+		m_ComputePipelineManager->init(*this);
+		m_RayTracingPipelineManager->init(*this);
 		m_downsampler = std::unique_ptr<Downsampler>(new BlitDownsampler(*this, *m_ImageManager));
 	}
 
@@ -134,7 +142,10 @@ namespace vkcv {
 		}
 
 		m_Context.getDevice().destroySemaphore(m_RenderFinished);
-		m_Context.getDevice().destroySemaphore(m_SwapchainImageAcquired);
+
+		for (auto& semaphore : m_SwapchainImagesAcquired) {
+			m_Context.getDevice().destroySemaphore(semaphore);
+		}
 	}
 
 	GraphicsPipelineHandle Core::createGraphicsPipeline(const GraphicsPipelineConfig &config) {
@@ -219,14 +230,25 @@ namespace vkcv {
 	}
 
 	Result Core::acquireSwapchainImage(const SwapchainHandle &swapchainHandle) {
-		uint32_t imageIndex;
+		uint32_t imageIndex, semaphoreIndex;
 		vk::Result result;
 
+		if (m_SwapchainImagesAcquired.size() <= 0) {
+			vkcv_log(LogLevel::ERROR, "Semaphores not available");
+			return Result::ERROR;
+		}
+
+		semaphoreIndex = m_currentSwapchainSemaphoreIndex % m_SwapchainImagesAcquired.size();
+
 		try {
 			result = m_Context.getDevice().acquireNextImageKHR(
 				m_SwapchainManager->getSwapchain(swapchainHandle).m_Swapchain,
-				std::numeric_limits<uint64_t>::max(), m_SwapchainImageAcquired, nullptr,
-				&imageIndex, {});
+				std::numeric_limits<uint64_t>::max(),
+				m_SwapchainImagesAcquired[semaphoreIndex],
+				nullptr,
+				&imageIndex,
+				{}
+			);
 		} catch (const vk::OutOfDateKHRError &e) {
 			result = vk::Result::eErrorOutOfDateKHR;
 		} catch (const vk::DeviceLostError &e) {
@@ -261,6 +283,17 @@ namespace vkcv {
 			setSwapchainImages(swapchainHandle);
 		}
 
+		const uint32_t count = m_SwapchainManager->getImageCount(swapchainHandle);
+		const uint32_t initialized = m_SwapchainImagesAcquired.size();
+
+		if (count > initialized) {
+			m_SwapchainImagesAcquired.resize(count);
+
+			for (uint32_t i = initialized; i < count; i++) {
+				m_SwapchainImagesAcquired[i] = m_Context.getDevice().createSemaphore({});
+			}
+		}
+
 		const auto &extent = m_SwapchainManager->getExtent(swapchainHandle);
 
 		width = extent.width;
@@ -276,9 +309,6 @@ namespace vkcv {
 			m_currentSwapchainImageIndex = std::numeric_limits<uint32_t>::max();
 		}
 
-		m_Context.getDevice().waitIdle(); // TODO: this is a sin against graphics programming, but
-										  // its getting late - Alex
-
 		m_ImageManager->setCurrentSwapchainImageIndex(m_currentSwapchainImageIndex);
 
 		return (m_currentSwapchainImageIndex != std::numeric_limits<uint32_t>::max());
@@ -286,8 +316,8 @@ namespace vkcv {
 
 	static std::array<uint32_t, 2>
 	getWidthHeightFromRenderTargets(const Vector<ImageHandle> &renderTargets,
-									const vk::Extent2D &swapchainExtent,
-									const ImageManager &imageManager) {
+                                  const vk::Extent2D &swapchainExtent,
+                                  const ImageManager &imageManager) {
 
 		std::array<uint32_t, 2> widthHeight;
 
@@ -831,17 +861,26 @@ namespace vkcv {
 	void Core::endFrame(const WindowHandle &windowHandle) {
 		SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchain();
 
-		if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
+		if ((m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) ||
+		    (m_SwapchainImagesAcquired.size() <= 0)) {
 			return;
 		}
 
-		const std::array<vk::Semaphore, 2> waitSemaphores { m_RenderFinished,
-															m_SwapchainImageAcquired };
+		const uint32_t semaphoreIndex = m_currentSwapchainSemaphoreIndex % m_SwapchainImagesAcquired.size();
+		m_currentSwapchainSemaphoreIndex = (m_currentSwapchainSemaphoreIndex + 1) % m_SwapchainImagesAcquired.size();
+
+		const std::array<vk::Semaphore, 2> waitSemaphores {
+			m_RenderFinished,
+			m_SwapchainImagesAcquired[semaphoreIndex]
+		};
 
 		const vk::SwapchainKHR &swapchain =
 			m_SwapchainManager->getSwapchain(swapchainHandle).m_Swapchain;
-		const vk::PresentInfoKHR presentInfo(waitSemaphores, swapchain,
-											 m_currentSwapchainImageIndex);
+		const vk::PresentInfoKHR presentInfo(
+			waitSemaphores,
+			swapchain,
+			m_currentSwapchainImageIndex
+		);
 
 		vk::Result result;
 
diff --git a/src/vkcv/DescriptorSetLayoutManager.cpp b/src/vkcv/DescriptorSetLayoutManager.cpp
index 6bbdb099ffbfe63bfaa6cead742d815202720b88..b0ed1064359e989236dfe4473be4e8b70c43adbb 100644
--- a/src/vkcv/DescriptorSetLayoutManager.cpp
+++ b/src/vkcv/DescriptorSetLayoutManager.cpp
@@ -34,8 +34,8 @@ namespace vkcv {
 
 	DescriptorSetLayoutManager::~DescriptorSetLayoutManager() noexcept {
 		for (uint64_t id = 0; id < getCount(); id++) {
-			// Resets the usage count to zero for destruction.
-			getById(id).layoutUsageCount = 0;
+			// Resets the usage count to one for destruction.
+			getById(id).layoutUsageCount = 1;
 		}
 
 		clear();
diff --git a/src/vkcv/DescriptorSetManager.cpp b/src/vkcv/DescriptorSetManager.cpp
index 4a3d4f5bbc1a785d4977ede468172ef7c5a54af1..d4ffce7480853bbd907ef3dc61ed317ae04e9220 100644
--- a/src/vkcv/DescriptorSetManager.cpp
+++ b/src/vkcv/DescriptorSetManager.cpp
@@ -1,6 +1,7 @@
 #include "DescriptorSetManager.hpp"
 
 #include "vkcv/Core.hpp"
+#include <vulkan/vulkan_core.h>
 
 namespace vkcv {
 
@@ -16,27 +17,48 @@ namespace vkcv {
 
 		m_DescriptorSetLayoutManager = &descriptorSetLayoutManager;
 
+		const auto& featureManager = core.getContext().getFeatureManager();
+
 		/**
 		 * Allocate the set size for the descriptor pools, namely 1000 units of each descriptor type
 		 * below. Finally, create an initial pool.
 		 */
 		m_PoolSizes.clear();
 		m_PoolSizes.emplace_back(vk::DescriptorType::eSampler, 1000);
+		m_PoolSizes.emplace_back(vk::DescriptorType::eCombinedImageSampler, 1000);
 		m_PoolSizes.emplace_back(vk::DescriptorType::eSampledImage, 1000);
+		m_PoolSizes.emplace_back(vk::DescriptorType::eStorageImage, 1000);
+		m_PoolSizes.emplace_back(vk::DescriptorType::eUniformTexelBuffer, 1000);
+		m_PoolSizes.emplace_back(vk::DescriptorType::eStorageTexelBuffer, 1000);
 		m_PoolSizes.emplace_back(vk::DescriptorType::eUniformBuffer, 1000);
 		m_PoolSizes.emplace_back(vk::DescriptorType::eStorageBuffer, 1000);
 		m_PoolSizes.emplace_back(vk::DescriptorType::eUniformBufferDynamic, 1000);
 		m_PoolSizes.emplace_back(vk::DescriptorType::eStorageBufferDynamic, 1000);
+		m_PoolSizes.emplace_back(vk::DescriptorType::eInputAttachment, 1000);
+
+		if (featureManager.isExtensionActive(VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME)) {
+			m_PoolSizes.emplace_back(vk::DescriptorType::eInlineUniformBlock, 1000);
+		}
 
-		if (core.getContext().getFeatureManager().isExtensionActive(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME)) {
+		if (featureManager.isExtensionActive(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME)) {
 			m_PoolSizes.emplace_back(vk::DescriptorType::eAccelerationStructureKHR, 1000);
 		}
 
 		m_PoolInfo = vk::DescriptorPoolCreateInfo(
-			vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, 1000,
-			static_cast<uint32_t>(m_PoolSizes.size()), m_PoolSizes.data()
+			vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet,
+			1000,
+			static_cast<uint32_t>(m_PoolSizes.size()),
+			m_PoolSizes.data()
 		);
 
+		if (featureManager.isExtensionActive(VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME)) {
+			m_InlineUniformBlockInfo = vk::DescriptorPoolInlineUniformBlockCreateInfo(
+				1000
+			);
+
+			m_PoolInfo.setPNext(&m_InlineUniformBlockInfo);
+		}
+
 		return allocateDescriptorPool();
 	}
 
@@ -50,19 +72,27 @@ namespace vkcv {
 	}
 
 	void DescriptorSetManager::destroyById(uint64_t id) {
+		const auto& device = getCore().getContext().getDevice();
 		auto &set = getById(id);
 
 		if (set.vulkanHandle) {
-			getCore().getContext().getDevice().freeDescriptorSets(m_Pools [set.poolIndex], 1,
-																  &(set.vulkanHandle));
-			set.setLayoutHandle = DescriptorSetLayoutHandle();
+			device.freeDescriptorSets(
+				m_Pools[set.poolIndex],
+				1,
+				&(set.vulkanHandle)
+			);
+
 			set.vulkanHandle = nullptr;
 		}
+
+		set.setLayoutHandle = DescriptorSetLayoutHandle();
 	}
 
 	bool DescriptorSetManager::allocateDescriptorPool() {
+		const auto& device = getCore().getContext().getDevice();
+
 		vk::DescriptorPool pool;
-		if (getCore().getContext().getDevice().createDescriptorPool(&m_PoolInfo, nullptr, &pool)
+		if (device.createDescriptorPool(&m_PoolInfo, nullptr, &pool)
 			!= vk::Result::eSuccess) {
 			vkcv_log(LogLevel::WARNING, "Failed to allocate descriptor pool");
 			return false;
@@ -76,11 +106,13 @@ namespace vkcv {
 		HandleManager<DescriptorSetEntry, DescriptorSetHandle>() {}
 
 	DescriptorSetManager::~DescriptorSetManager() noexcept {
+		const auto& device = getCore().getContext().getDevice();
+
 		clear();
 
 		for (const auto &pool : m_Pools) {
 			if (pool) {
-				getCore().getContext().getDevice().destroy(pool);
+				device.destroy(pool);
 			}
 		}
 	}
@@ -89,9 +121,15 @@ namespace vkcv {
 	DescriptorSetManager::createDescriptorSet(const DescriptorSetLayoutHandle &layout) {
 		// create and allocate the set based on the layout provided
 		const auto &setLayout = m_DescriptorSetLayoutManager->getDescriptorSetLayout(layout);
+		const auto &device = getCore().getContext().getDevice();
+
 
 		vk::DescriptorSet vulkanHandle;
-		vk::DescriptorSetAllocateInfo allocInfo(m_Pools.back(), 1, &setLayout.vulkanHandle);
+		vk::DescriptorSetAllocateInfo allocInfo(
+			m_Pools.back(),
+			1,
+			&setLayout.vulkanHandle
+		);
 
 		uint32_t sumVariableDescriptorCounts = 0;
 		for (auto bindingElem : setLayout.descriptorBindings) {
@@ -108,15 +146,21 @@ namespace vkcv {
 			allocInfo.setPNext(&variableAllocInfo);
 		}
 
-		auto result =
-			getCore().getContext().getDevice().allocateDescriptorSets(&allocInfo, &vulkanHandle);
+		auto result = device.allocateDescriptorSets(
+			&allocInfo,
+			&vulkanHandle
+		);
+
 		if (result != vk::Result::eSuccess) {
 			// create a new descriptor pool if the previous one ran out of memory
-			if (result == vk::Result::eErrorOutOfPoolMemory) {
-				allocateDescriptorPool();
+			if ((result == vk::Result::eErrorOutOfPoolMemory) &&
+			    (allocateDescriptorPool())) {
 				allocInfo.setDescriptorPool(m_Pools.back());
-				result = getCore().getContext().getDevice().allocateDescriptorSets(&allocInfo,
-																				   &vulkanHandle);
+
+				result = device.allocateDescriptorSets(
+					&allocInfo,
+					&vulkanHandle
+				);
 			}
 
 			if (result != vk::Result::eSuccess) {
diff --git a/src/vkcv/DescriptorSetManager.hpp b/src/vkcv/DescriptorSetManager.hpp
index 74cf75d9efd145132732fde03ca50d2f46baaded..6236cf9c37bee4a5a1973ae8baee5aa5ff642586 100644
--- a/src/vkcv/DescriptorSetManager.hpp
+++ b/src/vkcv/DescriptorSetManager.hpp
@@ -38,6 +38,7 @@ namespace vkcv {
 		Vector<vk::DescriptorPool> m_Pools;
 		Vector<vk::DescriptorPoolSize> m_PoolSizes;
 		vk::DescriptorPoolCreateInfo m_PoolInfo;
+		vk::DescriptorPoolInlineUniformBlockCreateInfo m_InlineUniformBlockInfo;
 		
 		bool init(Core &core) override;
 		bool init(Core &core, DescriptorSetLayoutManager &descriptorSetLayoutManager);
diff --git a/src/vkcv/Handles.cpp b/src/vkcv/Handles.cpp
index 64ab3080f5e6a649fa4fb0d8f772872713cb0140..578e467623830b8a1e6a612533011a8aa9bfc977 100644
--- a/src/vkcv/Handles.cpp
+++ b/src/vkcv/Handles.cpp
@@ -5,12 +5,7 @@
 
 namespace vkcv {
 
-	Handle::Handle() : m_id(std::numeric_limits<uint64_t>::max()), m_rc(nullptr), m_destroy(nullptr) {}
-
-	Handle::Handle(uint64_t id, const HandleDestroyFunction &destroy) :
-		m_id(id), m_rc(new uint64_t(1)), m_destroy(destroy) {}
-
-	Handle::~Handle() {
+	void Handle::destroy() {
 		if ((m_rc) && (*m_rc > 0) && (--(*m_rc) == 0)) {
 			if (m_destroy) {
 				m_destroy(m_id);
@@ -20,6 +15,15 @@ namespace vkcv {
 		}
 	}
 
+	Handle::Handle() : m_id(std::numeric_limits<uint64_t>::max()), m_rc(nullptr), m_destroy(nullptr) {}
+
+	Handle::Handle(uint64_t id, const HandleDestroyFunction &destroy) :
+		m_id(id), m_rc(new uint64_t(1)), m_destroy(destroy) {}
+
+	Handle::~Handle() {
+		destroy();
+	}
+
 	Handle::Handle(const Handle &other) :
 		m_id(other.m_id), m_rc(other.m_rc), m_destroy(other.m_destroy) {
 		if (m_rc) {
@@ -38,6 +42,8 @@ namespace vkcv {
 			return *this;
 		}
 
+		destroy();
+
 		m_id = other.m_id;
 		m_rc = other.m_rc;
 		m_destroy = other.m_destroy;
@@ -50,6 +56,8 @@ namespace vkcv {
 	}
 
 	Handle &Handle::operator=(Handle &&other) noexcept {
+		destroy();
+
 		m_id = other.m_id;
 		m_rc = other.m_rc;
 		m_destroy = other.m_destroy;
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index 619f16585ebbd0c5d4680e83e7b6ab5bf058d01b..79e18ade1978fa3c5f680822b413d1d930920c26 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -516,7 +516,7 @@ namespace vkcv {
 	}
 	
 	void ImageManager::switchImageLayoutImmediate(const ImageHandle &handle,
-												  vk::ImageLayout newLayout) {
+                                                vk::ImageLayout newLayout) {
 		auto &image = (*this) [handle];
 		const auto transitionBarriers = createImageLayoutTransitionBarriers(image, 0, 0, newLayout, false);
 		
@@ -540,7 +540,7 @@ namespace vkcv {
 				);
 
 				if (vk::Result::eSuccess != result) {
-					// TODO: warning?
+					vkcv_log(LogLevel::WARNING, "Transition to new layout failed");
 					break;
 				}
 			}
diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp
index 2deeeb813ad51e60b98a0c74f78b0cb0afa73039..bc478788855705d985c163f101008d321498fae6 100644
--- a/src/vkcv/ShaderProgram.cpp
+++ b/src/vkcv/ShaderProgram.cpp
@@ -8,6 +8,9 @@
 
 #include "vkcv/File.hpp"
 #include "vkcv/Logger.hpp"
+#include <cstddef>
+#include <cstdint>
+#include <limits>
 
 namespace vkcv {
 
@@ -79,164 +82,71 @@ namespace vkcv {
 			return true;
 	}
 
-	void ShaderProgram::reflectShader(ShaderStage shaderStage) {
-		auto shaderCode = m_Shaders.at(shaderStage);
-
-		spirv_cross::Compiler comp(shaderCode);
-		spirv_cross::ShaderResources resources = comp.get_shader_resources();
-
-		// reflect vertex input
-		if (shaderStage == ShaderStage::VERTEX) {
-			// spirv-cross API (hopefully) returns the stage_inputs in order
-			for (uint32_t i = 0; i < resources.stage_inputs.size(); i++) {
-				// spirv-cross specific objects
-				auto &stage_input = resources.stage_inputs [i];
-				const spirv_cross::SPIRType &base_type = comp.get_type(stage_input.base_type_id);
-
-				// vertex input location
-				const uint32_t attachment_loc =
-					comp.get_decoration(stage_input.id, spv::DecorationLocation);
-				// vertex input name
-				const std::string attachment_name = stage_input.name;
-				// vertex input format (implies its size)
-				const VertexAttachmentFormat attachment_format =
-					convertFormat(base_type.basetype, base_type.vecsize);
-
-				m_VertexAttachments.push_back(
-					{ attachment_loc, attachment_name, attachment_format, 0 });
-			}
+	static void reflectShaderDescriptorSets(Dictionary<uint32_t, DescriptorBindings> &descriptorSets,
+	                                        ShaderStage shaderStage,
+	                                        DescriptorType descriptorType,
+	                                        const spirv_cross::Compiler &comp,
+	                                        const spirv_cross::ShaderResources &resources) {
+		const spirv_cross::SmallVector<spirv_cross::Resource> *res = nullptr;
+
+		switch (descriptorType) {
+			case DescriptorType::UNIFORM_BUFFER:
+			  res = &(resources.uniform_buffers);
+			  break;
+			case DescriptorType::STORAGE_BUFFER:
+			  res = &(resources.storage_buffers);
+			  break;
+			case DescriptorType::SAMPLER:
+			  res = &(resources.separate_samplers);
+			  break;
+			case DescriptorType::IMAGE_SAMPLED:
+			  res = &(resources.separate_images);
+			  break;
+			case DescriptorType::IMAGE_STORAGE:
+			  res = &(resources.storage_images);
+			  break;
+			case DescriptorType::UNIFORM_BUFFER_DYNAMIC:
+			  res = &(resources.uniform_buffers);
+			  break;
+			case DescriptorType::STORAGE_BUFFER_DYNAMIC:
+			  res = &(resources.storage_buffers);
+			  break;
+			case DescriptorType::ACCELERATION_STRUCTURE_KHR:
+			  res = &(resources.acceleration_structures);
+			  break;
+			default:
+			  break;
 		}
 
-		// reflect descriptor sets (uniform buffer, storage buffer, sampler, sampled image, storage
-		// image)
-		Vector<std::pair<uint32_t, DescriptorBinding>> bindings;
-
-		for (uint32_t i = 0; i < resources.uniform_buffers.size(); i++) {
-			auto &u = resources.uniform_buffers [i];
-			const spirv_cross::SPIRType &base_type = comp.get_type(u.base_type_id);
-			const spirv_cross::SPIRType &type = comp.get_type(u.type_id);
-
-			uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
-			uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding);
-
-			uint32_t descriptorCount = base_type.vecsize;
-			bool variableCount = false;
-			// query whether reflected resources are qualified as one-dimensional array
-			if (type.array_size_literal [0]) {
-				descriptorCount = type.array [0];
-				if (type.array [0] == 0)
-					variableCount = true;
-			}
-
-			DescriptorBinding binding {
-				bindingID,     DescriptorType::UNIFORM_BUFFER, descriptorCount, shaderStage,
-				variableCount,
-				variableCount // partialBinding == variableCount
-			};
-
-			auto insertionResult =
-				m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding));
-			if (!insertionResult.second) {
-				insertionResult.first->second.shaderStages |= shaderStage;
-				
-				vkcv_log(LogLevel::WARNING,
-						 "Attempting to overwrite already existing binding %u at set ID %u.",
-						 bindingID, setID);
-			}
+		if (nullptr == res) {
+			return;
 		}
 
-		for (uint32_t i = 0; i < resources.storage_buffers.size(); i++) {
-			auto &u = resources.storage_buffers [i];
+		for (uint32_t i = 0; i < res->size(); i++) {
+			const spirv_cross::Resource &u = (*res)[i];
 			const spirv_cross::SPIRType &base_type = comp.get_type(u.base_type_id);
-			const spirv_cross::SPIRType &type = comp.get_type(u.type_id);
 
 			uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
 			uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding);
 
 			uint32_t descriptorCount = base_type.vecsize;
-			bool variableCount = false;
-			// query whether reflected resources are qualified as one-dimensional array
-			if (type.array_size_literal [0]) {
-				descriptorCount = type.array [0];
-				if (type.array [0] == 0)
-					variableCount = true;
-			}
-
-			DescriptorBinding binding {
-				bindingID,     DescriptorType::STORAGE_BUFFER, descriptorCount, shaderStage,
-				variableCount,
-				variableCount // partialBinding == variableCount
-			};
-
-			auto insertionResult =
-				m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding));
-			if (!insertionResult.second) {
-				insertionResult.first->second.shaderStages |= shaderStage;
-				
-				vkcv_log(LogLevel::WARNING,
-						 "Attempting to overwrite already existing binding %u at set ID %u.",
-						 bindingID, setID);
-			}
-		}
-
-		for (uint32_t i = 0; i < resources.separate_samplers.size(); i++) {
-			auto &u = resources.separate_samplers [i];
-			const spirv_cross::SPIRType &base_type = comp.get_type(u.base_type_id);
-			const spirv_cross::SPIRType &type = comp.get_type(u.type_id);
 
-			uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
-			uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding);
-
-			uint32_t descriptorCount = base_type.vecsize;
 			bool variableCount = false;
 			// query whether reflected resources are qualified as one-dimensional array
-			if (type.array_size_literal [0]) {
-				descriptorCount = type.array [0];
-				if (type.array [0] == 0)
-					variableCount = true;
+			if (descriptorCount == 0) {
+				variableCount = true;
 			}
 
 			DescriptorBinding binding {
-				bindingID,    DescriptorType::SAMPLER, descriptorCount, shaderStage, variableCount,
-				variableCount // partialBinding == variableCount
-			};
-
-			auto insertionResult =
-				m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding));
-			if (!insertionResult.second) {
-				insertionResult.first->second.shaderStages |= shaderStage;
-				
-				vkcv_log(LogLevel::WARNING,
-						 "Attempting to overwrite already existing binding %u at set ID %u.",
-						 bindingID, setID);
-			}
-		}
-
-		for (uint32_t i = 0; i < resources.separate_images.size(); i++) {
-			auto &u = resources.separate_images [i];
-			const spirv_cross::SPIRType &base_type = comp.get_type(u.base_type_id);
-			const spirv_cross::SPIRType &type = comp.get_type(u.type_id);
-
-			uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
-			uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding);
-
-			uint32_t descriptorCount = base_type.vecsize;
-			bool variableCount = false;
-			// query whether reflected resources are qualified as one-dimensional array
-			if (type.array_size_literal [0]) {
-				descriptorCount = type.array [0];
-				if (type.array [0] == 0)
-					variableCount = true;
-			}
-
-			DescriptorBinding binding {
-				bindingID,     DescriptorType::IMAGE_SAMPLED, descriptorCount, shaderStage,
+				bindingID,
+				descriptorType,
+				descriptorCount,
+				shaderStage,
 				variableCount,
 				variableCount // partialBinding == variableCount
 			};
 
-			auto insertionResult =
-				m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding));
+			auto insertionResult = descriptorSets[setID].insert(std::make_pair(bindingID, binding));
 			if (!insertionResult.second) {
 				insertionResult.first->second.shaderStages |= shaderStage;
 				
@@ -245,63 +155,96 @@ namespace vkcv {
 						 bindingID, setID);
 			}
 		}
+	}
 
-		for (uint32_t i = 0; i < resources.storage_images.size(); i++) {
-			auto &u = resources.storage_images [i];
-			const spirv_cross::SPIRType &base_type = comp.get_type(u.base_type_id);
-			const spirv_cross::SPIRType &type = comp.get_type(u.type_id);
+	void ShaderProgram::reflectShader(ShaderStage shaderStage) {
+		auto shaderCode = m_Shaders.at(shaderStage);
 
-			uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
-			uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding);
+		spirv_cross::Compiler comp(shaderCode);
+		spirv_cross::ShaderResources resources = comp.get_shader_resources();
 
-			uint32_t descriptorCount = base_type.vecsize;
-			bool variableCount = false;
-			// query whether reflected resources are qualified as one-dimensional array
-			if (type.array_size_literal [0]) {
-				descriptorCount = type.array [0];
-				if (type.array [0] == 0)
-					variableCount = true;
-			}
+		// reflect vertex input
+		if (shaderStage == ShaderStage::VERTEX) {
+			// spirv-cross API (hopefully) returns the stage_inputs in order
+			for (uint32_t i = 0; i < resources.stage_inputs.size(); i++) {
+				// spirv-cross specific objects
+				auto &stage_input = resources.stage_inputs [i];
+				const spirv_cross::SPIRType &base_type = comp.get_type(stage_input.base_type_id);
 
-			DescriptorBinding binding {
-				bindingID,     DescriptorType::IMAGE_STORAGE, descriptorCount, shaderStage,
-				variableCount,
-				variableCount // partialBinding == variableCount
-			};
+				// vertex input location
+				const uint32_t attachment_loc =
+					comp.get_decoration(stage_input.id, spv::DecorationLocation);
+				// vertex input name
+				const std::string attachment_name = stage_input.name;
+				// vertex input format (implies its size)
+				const VertexAttachmentFormat attachment_format =
+					convertFormat(base_type.basetype, base_type.vecsize);
 
-			auto insertionResult =
-				m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding));
-			if (!insertionResult.second) {
-				insertionResult.first->second.shaderStages |= shaderStage;
-				
-				vkcv_log(LogLevel::WARNING,
-						 "Attempting to overwrite already existing binding %u at set ID %u.",
-						 bindingID, setID);
+				m_VertexAttachments.push_back(
+					{ attachment_loc, attachment_name, attachment_format, 0 });
 			}
 		}
 
-		// Used to reflect acceleration structure bindings for RTX.
-		for (uint32_t i = 0; i < resources.acceleration_structures.size(); i++) {
-			auto &u = resources.acceleration_structures [i];
-			const spirv_cross::SPIRType &base_type = comp.get_type(u.base_type_id);
+		reflectShaderDescriptorSets(
+			m_DescriptorSets,
+			shaderStage,
+			DescriptorType::UNIFORM_BUFFER,
+			comp,
+			resources
+		);
+
+		reflectShaderDescriptorSets(
+			m_DescriptorSets,
+			shaderStage,
+			DescriptorType::STORAGE_BUFFER,
+			comp,
+			resources
+		);
+
+		reflectShaderDescriptorSets(
+			m_DescriptorSets,
+			shaderStage,
+			DescriptorType::SAMPLER,
+			comp,
+			resources
+		);
+
+		reflectShaderDescriptorSets(
+			m_DescriptorSets,
+			shaderStage,
+			DescriptorType::IMAGE_SAMPLED,
+			comp,
+			resources
+		);
+
+		reflectShaderDescriptorSets(
+			m_DescriptorSets,
+			shaderStage,
+			DescriptorType::IMAGE_STORAGE,
+			comp,
+			resources
+		);
+
+		reflectShaderDescriptorSets(
+			m_DescriptorSets,
+			shaderStage,
+			DescriptorType::ACCELERATION_STRUCTURE_KHR,
+			comp,
+			resources
+		);
+
+		for (auto &descriptorSet : m_DescriptorSets) {
+			uint32_t maxVariableBindingID = 0;
+
+			for (const auto &binding : descriptorSet.second) {
+				maxVariableBindingID = std::max(maxVariableBindingID, binding.first);
+			}
 
-			uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
-			uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding);
-			auto binding = DescriptorBinding { bindingID,
-											   DescriptorType::ACCELERATION_STRUCTURE_KHR,
-											   base_type.vecsize,
-											   shaderStage,
-											   false,
-											   false };
-
-			auto insertionResult =
-				m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding));
-			if (!insertionResult.second) {
-				insertionResult.first->second.shaderStages |= shaderStage;
-				
-				vkcv_log(LogLevel::WARNING,
-						 "Attempting to overwrite already existing binding %u at set ID %u.",
-						 bindingID, setID);
+			for (auto &binding : descriptorSet.second) {
+				if (binding.first < maxVariableBindingID) {
+					binding.second.variableCount &= false;
+					binding.second.partialBinding &= false;
+				}
 			}
 		}
 
diff --git a/src/vkcv/SwapchainManager.cpp b/src/vkcv/SwapchainManager.cpp
index c89845302c408faf05d38bc356f648ce822f2cd3..03dbdc0175b393565bc4235e99f5cb02dd176db5 100644
--- a/src/vkcv/SwapchainManager.cpp
+++ b/src/vkcv/SwapchainManager.cpp
@@ -207,7 +207,7 @@ namespace vkcv {
 			vk::CompositeAlphaFlagBitsKHR::eOpaque, chosenPresentMode, true, entry.m_Swapchain);
 
 		entry.m_Swapchain = device.createSwapchainKHR(swapchainCreateInfo);
-		return true;
+		return entry.m_Swapchain? true : false;
 	}
 
 	SwapchainHandle SwapchainManager::createSwapchain(Window &window) {
diff --git a/src/vkcv/Window.cpp b/src/vkcv/Window.cpp
index 78afe0f2175e6b3c7355e5f7863ae425069adacd..d710766014f21671a32b720421c734ddf12a317f 100644
--- a/src/vkcv/Window.cpp
+++ b/src/vkcv/Window.cpp
@@ -122,6 +122,9 @@ namespace vkcv {
 		Window::e_char.unlock();
 		Window::e_gamepad.unlock();
 		Window::e_resize.remove(m_resizeHandle);
+
+		m_swapchainHandle = SwapchainHandle();
+
 		if (m_window) {
 			s_Windows.erase(std::find(s_Windows.begin(), s_Windows.end(), m_window));
 			glfwDestroyWindow(m_window);