diff --git a/include/vkcv/BufferManager.hpp b/include/vkcv/BufferManager.hpp
index 7bec33d8c4fa752be2487a849c16eaeeea0e6237..e4e1e4019f982908ad592466f342b84d3ef52d9a 100644
--- a/include/vkcv/BufferManager.hpp
+++ b/include/vkcv/BufferManager.hpp
@@ -13,7 +13,8 @@ namespace vkcv
 		VERTEX,
 		UNIFORM,
 		STORAGE,
-		STAGING
+		STAGING,
+		INDIRECT
 	};
 	
 	enum class BufferMemoryType {
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index 028f8bcc10c3483c417462bc9382e45ae24e74d6..b89dbae2f9af909ab2f1319994e9bb00fb87f98e 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -331,6 +331,18 @@ namespace vkcv
 			const std::vector<ImageHandle>  &renderTargets,
 			const WindowHandle              &windowHandle);
 
+		void recordIndexedIndirectDrawcallsToCmdStream(
+				const CommandStreamHandle                           cmdStreamHandle,
+				const PassHandle                                    renderpassHandle,
+				const GraphicsPipelineHandle                        &pipelineHandle,
+				const PushConstants                                 &pushConstantData,
+                const vkcv::DescriptorSetHandle                     &compiledDescriptorSet,
+				const vkcv::Mesh                                    &compiledMesh,
+				const std::vector<ImageHandle>                      &renderTargets,
+				const vkcv::Buffer<vk::DrawIndexedIndirectCommand>  &indirectBuffer,
+				const uint32_t                                      drawCount,
+				const WindowHandle                                  &windowHandle);
+
 		void recordMeshShaderDrawcalls(
 			const CommandStreamHandle&              cmdStreamHandle,
 			const PassHandle&                       renderpassHandle,
diff --git a/include/vkcv/DescriptorConfig.hpp b/include/vkcv/DescriptorConfig.hpp
index 735f973777a6ef41951c7ef52350f9ddc5b1200c..becdb9f843aa254fed6f05dec0c18686fac497d6 100644
--- a/include/vkcv/DescriptorConfig.hpp
+++ b/include/vkcv/DescriptorConfig.hpp
@@ -61,17 +61,11 @@ namespace vkcv
     */
     struct DescriptorBinding
     {
-        DescriptorBinding(
-            uint32_t bindingID,
-            DescriptorType descriptorType,
-            uint32_t descriptorCount,
-            ShaderStages shaderStages
-        ) noexcept;
-        
         uint32_t        bindingID;
         DescriptorType  descriptorType;
         uint32_t        descriptorCount;
         ShaderStages    shaderStages;
+        bool            variableCount;
 
         bool operator ==(const DescriptorBinding &other) const;
     };
diff --git a/include/vkcv/DescriptorWrites.hpp b/include/vkcv/DescriptorWrites.hpp
index 457e8952d0fcb2dbe0ad281f6f8018575c4f4ede..f4f9729a193c9da4cfc4ccc869337314e8a71ccb 100644
--- a/include/vkcv/DescriptorWrites.hpp
+++ b/include/vkcv/DescriptorWrites.hpp
@@ -4,12 +4,13 @@
 
 namespace vkcv {
 	struct SampledImageDescriptorWrite {
-		inline SampledImageDescriptorWrite(uint32_t binding, ImageHandle image, uint32_t mipLevel = 0, bool useGeneralLayout = false)
-		    : binding(binding), image(image), mipLevel(mipLevel), useGeneralLayout(useGeneralLayout) {};
+		inline SampledImageDescriptorWrite(uint32_t binding, ImageHandle image, uint32_t mipLevel = 0, bool useGeneralLayout = false, uint32_t arrayIndex = 0)
+		: binding(binding), image(image), mipLevel(mipLevel), useGeneralLayout(useGeneralLayout), arrayIndex(arrayIndex) {};
 		uint32_t	binding;
 		ImageHandle	image;
 		uint32_t    mipLevel;
 		bool        useGeneralLayout;
+		uint32_t    arrayIndex;
 	};
 
 	struct StorageImageDescriptorWrite {
diff --git a/include/vkcv/DrawcallRecording.hpp b/include/vkcv/DrawcallRecording.hpp
index 37cf02d9102fcab5abd10ada711f67b721bcb52b..77e2a2e13bee3248e424d1d9c3d4cad7aaecd34b 100644
--- a/include/vkcv/DrawcallRecording.hpp
+++ b/include/vkcv/DrawcallRecording.hpp
@@ -3,6 +3,7 @@
 #include <vkcv/Handles.hpp>
 #include <vkcv/DescriptorConfig.hpp>
 #include <vkcv/PushConstants.hpp>
+#include "Buffer.hpp"
 
 namespace vkcv {
     struct VertexBufferBinding {
@@ -50,6 +51,8 @@ namespace vkcv {
 
     };
 
+    vk::IndexType getIndexType(IndexBitCount indexByteCount);
+
     struct DrawcallInfo {
         inline DrawcallInfo(const Mesh& mesh, const std::vector<DescriptorSetUsage>& descriptorSets, const uint32_t instanceCount = 1)
             : mesh(mesh), descriptorSets(descriptorSets), instanceCount(instanceCount){}
@@ -66,6 +69,15 @@ namespace vkcv {
         const PushConstants     &pushConstants,
         const size_t            drawcallIndex);
 
+    void recordIndirectDrawcall(
+            const DrawcallInfo                                  &drawcall,
+            vk::CommandBuffer                                   cmdBuffer,
+            const vkcv::Buffer<vk::DrawIndexedIndirectCommand>  &drawBuffer,
+            const uint32_t                                      drawCount,
+            vk::PipelineLayout                                  pipelineLayout,
+            const PushConstants                                 &pushConstants,
+            const size_t                                        drawcallIndex);
+
     void InitMeshShaderDrawFunctions(vk::Device device);
 
     struct MeshShaderDrawcall {
diff --git a/include/vkcv/FeatureManager.hpp b/include/vkcv/FeatureManager.hpp
index e9abd69009da1120284aefeb375dd380abd20201..2f932f6e25c12e80f42d762f82a73f9a5265b793 100644
--- a/include/vkcv/FeatureManager.hpp
+++ b/include/vkcv/FeatureManager.hpp
@@ -78,7 +78,7 @@ namespace vkcv {
 		
 		[[nodiscard]]
 		bool checkSupport(const vk::PhysicalDeviceMeshShaderFeaturesNV& features, bool required) const;
-
+		
 		/**
          * @brief Currently used for RTX. Checks support of the @p vk::PhysicalDeviceVulkan12Features.
          * @param features The features.
diff --git a/modules/camera/include/vkcv/camera/Camera.hpp b/modules/camera/include/vkcv/camera/Camera.hpp
index 8a8c5df5d74cf1402bd8810172657ba77ddb2d56..6ccafe4ebe65f4e50747f1341ed1306948fef242 100644
--- a/modules/camera/include/vkcv/camera/Camera.hpp
+++ b/modules/camera/include/vkcv/camera/Camera.hpp
@@ -4,6 +4,7 @@
 #include <glm/gtc/matrix_transform.hpp>
 #include <glm/gtc/matrix_access.hpp>
 #include <glm/vec3.hpp>
+#include <glm/gtc/type_ptr.hpp>
 #include <glm/mat4x4.hpp>
 
 namespace vkcv::camera {
diff --git a/modules/material/src/vkcv/material/Material.cpp b/modules/material/src/vkcv/material/Material.cpp
index 43a72e4cb6c936457f3723e32dc2715d9788ce08..bd26733b878634e3caf5b927e8c0307432279212 100644
--- a/modules/material/src/vkcv/material/Material.cpp
+++ b/modules/material/src/vkcv/material/Material.cpp
@@ -36,16 +36,36 @@ namespace vkcv::material {
 			case MaterialType::PBR_MATERIAL:
 				if (pbr_bindings.empty())
 				{
-					pbr_bindings.insert(std::make_pair(0, DescriptorBinding(0, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT)));
-					pbr_bindings.insert(std::make_pair(1, DescriptorBinding(1, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT)));
-					pbr_bindings.insert(std::make_pair(2, DescriptorBinding(2, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT)));
-					pbr_bindings.insert(std::make_pair(3, DescriptorBinding(3, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT)));
-					pbr_bindings.insert(std::make_pair(4, DescriptorBinding(4, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT)));
-					pbr_bindings.insert(std::make_pair(5, DescriptorBinding(5, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT)));
-					pbr_bindings.insert(std::make_pair(6, DescriptorBinding(6, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT)));
-					pbr_bindings.insert(std::make_pair(7, DescriptorBinding(7, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT)));
-					pbr_bindings.insert(std::make_pair(8, DescriptorBinding(8, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT)));
-					pbr_bindings.insert(std::make_pair(9, DescriptorBinding(9, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT)));
+					pbr_bindings.insert(std::make_pair(0, DescriptorBinding {
+						0, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT, false
+					}));
+					pbr_bindings.insert(std::make_pair(1, DescriptorBinding {
+						1, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT, false
+					}));
+					pbr_bindings.insert(std::make_pair(2, DescriptorBinding {
+						2, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT, false
+					}));
+					pbr_bindings.insert(std::make_pair(3, DescriptorBinding {
+						3, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT, false
+					}));
+					pbr_bindings.insert(std::make_pair(4, DescriptorBinding {
+						4, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT, false
+					}));
+					pbr_bindings.insert(std::make_pair(5, DescriptorBinding {
+						5, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT, false
+					}));
+					pbr_bindings.insert(std::make_pair(6, DescriptorBinding {
+						6, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT, false
+					}));
+					pbr_bindings.insert(std::make_pair(7, DescriptorBinding {
+						7, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT, false
+					}));
+					pbr_bindings.insert(std::make_pair(8, DescriptorBinding {
+						8, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT, false
+					}));
+					pbr_bindings.insert(std::make_pair(9, DescriptorBinding {
+						9, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT, false
+					}));
 				}
 				
 				return pbr_bindings;
diff --git a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
index e4aae60b595d50d7e859199079714e91f5e5a694..f7cf0b4a1e68e46a784723d13366deac899f9963 100644
--- a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
+++ b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
@@ -67,33 +67,36 @@ namespace vkcv::upscaling {
 	static DescriptorBindings getDescriptorBindings() {
 		DescriptorBindings descriptorBindings = {};
 
-	    auto binding_0 = DescriptorBinding(
+	    auto binding_0 = DescriptorBinding {
 	            0,
 	            DescriptorType::UNIFORM_BUFFER_DYNAMIC,
 	            1,
-	            ShaderStage::COMPUTE
-		);
+	            ShaderStage::COMPUTE,
+				false
+		};
 
-	    auto binding_1 = DescriptorBinding(
-	            1,
-	            DescriptorType::IMAGE_SAMPLED,
-	            1,
-	            ShaderStage::COMPUTE
-		);
+	    auto binding_1 = DescriptorBinding {
+				1,
+				DescriptorType::IMAGE_SAMPLED,
+				1,
+				ShaderStage::COMPUTE,
+				false
+		};
 
-	    auto binding_2 = DescriptorBinding(
-	            2,
-	            DescriptorType::IMAGE_STORAGE,
-	            1,
-	            ShaderStage::COMPUTE
-		);
+	    auto binding_2 = DescriptorBinding{
+				2,
+				DescriptorType::IMAGE_STORAGE,
+				1,
+				ShaderStage::COMPUTE,
+				false
+		};
 
-	    auto binding_3 = DescriptorBinding(
-	            3,
-	            DescriptorType::SAMPLER,
-	            1,
-	            ShaderStage::COMPUTE
-		);
+	    auto binding_3 = DescriptorBinding{
+				3,
+				DescriptorType::SAMPLER,
+				1,
+				ShaderStage::COMPUTE
+		};
 
 	    descriptorBindings.insert(std::make_pair(0, binding_0));
 	    descriptorBindings.insert(std::make_pair(1, binding_1));
diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt
index 52d4a4a21f001b9feaab917fcdac4d52c6bf7e63..9cf1ff1c9327114d28871f11694a2fb88bc00735 100644
--- a/projects/CMakeLists.txt
+++ b/projects/CMakeLists.txt
@@ -8,6 +8,8 @@ add_subdirectory(rtx_ambient_occlusion)
 add_subdirectory(sph)
 add_subdirectory(voxelization)
 add_subdirectory(mesh_shader)
+add_subdirectory(indirect_draw)
+add_subdirectory(bindless_textures)
 add_subdirectory(saf_r)
 add_subdirectory(indirect_dispatch)
 add_subdirectory(path_tracer)
\ No newline at end of file
diff --git a/projects/bindless_textures/.gitignore b/projects/bindless_textures/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..cd45650aee8ab49ad568556452dde2d9d51d5f13
--- /dev/null
+++ b/projects/bindless_textures/.gitignore
@@ -0,0 +1 @@
+bindless_textures
\ No newline at end of file
diff --git a/projects/bindless_textures/CMakeLists.txt b/projects/bindless_textures/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..08cc636af2495afa3bb69cc7b652cc20eb4fa117
--- /dev/null
+++ b/projects/bindless_textures/CMakeLists.txt
@@ -0,0 +1,28 @@
+cmake_minimum_required(VERSION 3.16)
+project(bindless_textures)
+
+# setting c++ standard for the project
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+# this should fix the execution path to load local files from the project
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+
+# adding source files to the project
+add_executable(bindless_textures src/main.cpp)
+
+# this should fix the execution path to load local files from the project (for MSVC)
+if(MSVC)
+	set_target_properties(bindless_textures PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
+	set_target_properties(bindless_textures PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
+    
+    # in addition to setting the output directory, the working directory has to be set
+	# by default visual studio sets the working directory to the build directory, when using the debugger
+	set_target_properties(bindless_textures PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
+endif()
+
+# including headers of dependencies and the VkCV framework
+target_include_directories(bindless_textures SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include})
+
+# linking with libraries from all dependencies and the VkCV framework
+target_link_libraries(bindless_textures vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler)
diff --git a/projects/bindless_textures/resources/Szene/Szene.bin b/projects/bindless_textures/resources/Szene/Szene.bin
new file mode 100644
index 0000000000000000000000000000000000000000..c87d27637516b0bbf864251dd162773f5cc53e06
--- /dev/null
+++ b/projects/bindless_textures/resources/Szene/Szene.bin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ee4742718f720d589a2a03f5d879f8c50ba9057718d191a43b046eaa9071080d
+size 70328
diff --git a/projects/bindless_textures/resources/Szene/Szene.gltf b/projects/bindless_textures/resources/Szene/Szene.gltf
new file mode 100644
index 0000000000000000000000000000000000000000..e5a32b29af5d0a2ac5f497e60b4b92c1873e1df9
--- /dev/null
+++ b/projects/bindless_textures/resources/Szene/Szene.gltf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:75ba834118792ebbacf528a1690c7d04df4b4c8122b9f99a9aa9a9d075d2c86a
+size 7421
diff --git a/projects/bindless_textures/resources/Szene/boards2_vcyc.jpg b/projects/bindless_textures/resources/Szene/boards2_vcyc.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..2636039e272289c0fba3fa2d88a060b857501248
--- /dev/null
+++ b/projects/bindless_textures/resources/Szene/boards2_vcyc.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564
+size 1192476
diff --git a/projects/bindless_textures/resources/Szene/boards2_vcyc_jpg.jpg b/projects/bindless_textures/resources/Szene/boards2_vcyc_jpg.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..2636039e272289c0fba3fa2d88a060b857501248
--- /dev/null
+++ b/projects/bindless_textures/resources/Szene/boards2_vcyc_jpg.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564
+size 1192476
diff --git a/projects/bindless_textures/resources/cube/Grass001_1K_AmbientOcclusion.jpg b/projects/bindless_textures/resources/cube/Grass001_1K_AmbientOcclusion.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..2217fb53744f9166232a40b78ee9518e04b0ded5
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/Grass001_1K_AmbientOcclusion.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f6c88f8f3facd9e91f8dd160bd8c4a602e433872ed18e08015a9fa9dfff889de
+size 901465
diff --git a/projects/bindless_textures/resources/cube/Grass001_1K_Color.jpg b/projects/bindless_textures/resources/cube/Grass001_1K_Color.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..b8aa1533ae5a023a5fc8457f30ed60efe3bda32d
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/Grass001_1K_Color.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:878b8fe4747d9ce693220edea1915de550e8f14d402d26a0915f162d40f84e91
+size 1763328
diff --git a/projects/bindless_textures/resources/cube/Grass001_1K_Displacement.jpg b/projects/bindless_textures/resources/cube/Grass001_1K_Displacement.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..89789cba150eea7c7abbdc1851090f6021af978a
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/Grass001_1K_Displacement.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1e8300e1107bee7e681059d9da0a7e3ca422977e8b6f496e16452a4c94b3d385
+size 912347
diff --git a/projects/bindless_textures/resources/cube/Grass001_1K_Normal.jpg b/projects/bindless_textures/resources/cube/Grass001_1K_Normal.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..3163d6391592ace10446cb71141a2192e63e9660
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/Grass001_1K_Normal.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:684426b49e841e267f12f06dc955b5b1d01b3ba3659bea0c5d73be889700929f
+size 2336471
diff --git a/projects/bindless_textures/resources/cube/Grass001_1K_Roughness.jpg b/projects/bindless_textures/resources/cube/Grass001_1K_Roughness.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..10e6ac33badf2a4795766a66546a62c67eb8b558
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/Grass001_1K_Roughness.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d952ffb098faf9ac5eb25134eabac08f0c65d927a448b5e7b4f9c72510cfcbe0
+size 822659
diff --git a/projects/bindless_textures/resources/cube/boards2_vcyc_jpg.jpg b/projects/bindless_textures/resources/cube/boards2_vcyc_jpg.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..2636039e272289c0fba3fa2d88a060b857501248
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/boards2_vcyc_jpg.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564
+size 1192476
diff --git a/projects/bindless_textures/resources/cube/cube.bin b/projects/bindless_textures/resources/cube/cube.bin
new file mode 100644
index 0000000000000000000000000000000000000000..3303cd8635848bee18e10ab8754d5e4e7218db92
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/cube.bin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9bb9b6b8bbe50a0aaa517057f245ee844f80afa7426dacb2aed4128f71629ce4
+size 840
diff --git a/projects/bindless_textures/resources/cube/cube.blend b/projects/bindless_textures/resources/cube/cube.blend
new file mode 100644
index 0000000000000000000000000000000000000000..62ccb2c742094bcfb5ed194ab905bffae86bfd65
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/cube.blend
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a6c1e245f259c610528c9485db6688928faac0ab2addee9e3c2dde7740e4dd09
+size 774920
diff --git a/projects/bindless_textures/resources/cube/cube.blend1 b/projects/bindless_textures/resources/cube/cube.blend1
new file mode 100644
index 0000000000000000000000000000000000000000..13f21dcca218d7bc7a07a8a9682b5e1d9e607736
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/cube.blend1
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f4496f423569b8ca81f3b3a55fad00f925557e0193fb9dbe6cdce7e71fb48f7b
+size 774920
diff --git a/projects/bindless_textures/resources/cube/cube.glb b/projects/bindless_textures/resources/cube/cube.glb
new file mode 100644
index 0000000000000000000000000000000000000000..66a42c65e71dcf375e04cc378256024dd3c7834d
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/cube.glb
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:198568b715f397d78f7c358c0f709a419e7fd677e54cdec7c19f71b5ed264897
+size 1194508
diff --git a/projects/bindless_textures/resources/cube/cube.gltf b/projects/bindless_textures/resources/cube/cube.gltf
new file mode 100644
index 0000000000000000000000000000000000000000..428176144843dd06c78fe1d11a6392a0ea02b22d
--- /dev/null
+++ b/projects/bindless_textures/resources/cube/cube.gltf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f82f455647a84ca6242882ae26a79a499d3ce594f8de317ab89488c5b79721ac
+size 2823
diff --git a/projects/bindless_textures/resources/shaders/shader.frag b/projects/bindless_textures/resources/shaders/shader.frag
new file mode 100644
index 0000000000000000000000000000000000000000..c855eb407944c415dc4055716aa64a531c830ef3
--- /dev/null
+++ b/projects/bindless_textures/resources/shaders/shader.frag
@@ -0,0 +1,16 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_EXT_nonuniform_qualifier : enable
+
+layout(location = 0) in vec3 passNormal;
+layout(location = 1) in vec2 passUV;
+layout(location = 2) in flat int passTextureIndex;
+
+layout(location = 0) out vec3 outColor;
+
+layout(set=0, binding=0) uniform sampler    textureSampler;
+layout(set=0, binding=1) uniform texture2D  materialTextures[];
+
+void main()	{
+	outColor =  texture(sampler2D(materialTextures[nonuniformEXT(passTextureIndex)], textureSampler), passUV).rgb;
+}
\ No newline at end of file
diff --git a/projects/bindless_textures/resources/shaders/shader.vert b/projects/bindless_textures/resources/shaders/shader.vert
new file mode 100644
index 0000000000000000000000000000000000000000..6bc918c6a186dcfb965719cd1e08cb448a49b44e
--- /dev/null
+++ b/projects/bindless_textures/resources/shaders/shader.vert
@@ -0,0 +1,43 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec3 inPosition;
+layout(location = 1) in vec3 inNormal;
+layout(location = 2) in vec2 inUV;
+
+layout(location = 0) out vec3 passNormal;
+layout(location = 1) out vec2 passUV;
+layout(location = 2) out flat int passTextureIndex;
+
+layout( push_constant ) uniform constants{
+    mat4 mvp;
+};
+
+void main()
+{
+	gl_Position = mvp * vec4(inPosition, 1.0);
+	passNormal  = inNormal;
+    passUV      = inUV;
+
+    passTextureIndex = (gl_VertexIndex / 4);
+
+    /*
+    if(inNormal.x > 0.9)
+        passTextureIndex = 0;
+
+    if(inNormal.x < -0.9)
+        passTextureIndex = 1;
+
+    if(inNormal.y > 0.9)
+        passTextureIndex = 2;
+
+    if(inNormal.y < -0.9)
+        passTextureIndex = 3;
+
+    if(inNormal.z > 0.9)
+        passTextureIndex = 4;
+
+    if(inNormal.z < -0.9)
+        passTextureIndex = 5;
+    */
+}
\ No newline at end of file
diff --git a/projects/bindless_textures/resources/triangle/Triangle.bin b/projects/bindless_textures/resources/triangle/Triangle.bin
new file mode 100644
index 0000000000000000000000000000000000000000..57f26ad96592b64377e6aa93823d96a94e6c5022
--- /dev/null
+++ b/projects/bindless_textures/resources/triangle/Triangle.bin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:412ebd5f7242c266b4957e7e26be13aa331dbcb7bbb854ab334a2437ae8ed959
+size 104
diff --git a/projects/bindless_textures/resources/triangle/Triangle.blend b/projects/bindless_textures/resources/triangle/Triangle.blend
new file mode 100644
index 0000000000000000000000000000000000000000..2421dc5e1bb029d73a9ec09cc4530c5196851fd7
--- /dev/null
+++ b/projects/bindless_textures/resources/triangle/Triangle.blend
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:387e544df173219fbf292a64a6656d1d782bbf71a5a9e9fdef0a308f47b05477
+size 758144
diff --git a/projects/bindless_textures/resources/triangle/Triangle.glb b/projects/bindless_textures/resources/triangle/Triangle.glb
new file mode 100644
index 0000000000000000000000000000000000000000..4148620cd6af0dadbc791aa1c52bb5431a40884b
--- /dev/null
+++ b/projects/bindless_textures/resources/triangle/Triangle.glb
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f4be087a605212d139416b5352a018283b26b99260cbcddb7013a1beeb331227
+size 980
diff --git a/projects/bindless_textures/resources/triangle/Triangle.gltf b/projects/bindless_textures/resources/triangle/Triangle.gltf
new file mode 100644
index 0000000000000000000000000000000000000000..a188e6ee16a5e8486cf307c7bda8cfd99bdbeea6
--- /dev/null
+++ b/projects/bindless_textures/resources/triangle/Triangle.gltf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d5fc354e040f79cff329e919677b194c75e3a522c6406f75c1108ad9575f12ec
+size 2202
diff --git a/projects/bindless_textures/src/main.cpp b/projects/bindless_textures/src/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a2e9ff5eed65d44f329c8986b64d6d8588a92d30
--- /dev/null
+++ b/projects/bindless_textures/src/main.cpp
@@ -0,0 +1,277 @@
+#include <iostream>
+#include <vkcv/Core.hpp>
+#include <GLFW/glfw3.h>
+#include <vkcv/camera/CameraManager.hpp>
+#include <chrono>
+#include <vkcv/asset/asset_loader.hpp>
+#include <vkcv/shader/GLSLCompiler.hpp>
+
+int main(int argc, const char** argv) {
+	const char* applicationName = "First Mesh";
+
+	uint32_t windowWidth = 800;
+	uint32_t windowHeight = 600;
+
+	vkcv::Features features;
+	features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+    features.requireExtensionFeature<vk::PhysicalDeviceDescriptorIndexingFeatures>(
+            VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, [](vk::PhysicalDeviceDescriptorIndexingFeatures &features) {
+                features.setShaderInputAttachmentArrayDynamicIndexing(true);
+                features.setShaderUniformTexelBufferArrayDynamicIndexing(true);
+                features.setShaderStorageTexelBufferArrayDynamicIndexing(true);
+                features.setShaderUniformBufferArrayNonUniformIndexing(true);
+                features.setShaderSampledImageArrayNonUniformIndexing(true);
+                features.setShaderStorageBufferArrayNonUniformIndexing(true);
+                features.setShaderStorageImageArrayNonUniformIndexing(true);
+                features.setShaderInputAttachmentArrayNonUniformIndexing(true);
+                features.setShaderUniformTexelBufferArrayNonUniformIndexing(true);
+                features.setShaderStorageTexelBufferArrayNonUniformIndexing(true);
+
+                features.setDescriptorBindingUniformBufferUpdateAfterBind(true);
+                features.setDescriptorBindingSampledImageUpdateAfterBind(true);
+                features.setDescriptorBindingStorageImageUpdateAfterBind(true);
+                features.setDescriptorBindingStorageBufferUpdateAfterBind(true);
+                features.setDescriptorBindingUniformTexelBufferUpdateAfterBind(true);
+                features.setDescriptorBindingStorageTexelBufferUpdateAfterBind(true);
+
+                features.setDescriptorBindingUpdateUnusedWhilePending(true);
+                features.setDescriptorBindingPartiallyBound(true);
+                features.setDescriptorBindingVariableDescriptorCount(true);
+                features.setRuntimeDescriptorArray(true);
+            }
+	);
+
+	vkcv::Core core = vkcv::Core::create(
+		applicationName,
+		VK_MAKE_VERSION(0, 0, 1),
+		{ vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer },
+		features
+	);
+
+	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth,windowHeight,false);
+
+	vkcv::asset::Scene mesh;
+
+	// TEST DATA
+	std::vector<vkcv::Image> texturesArray;
+    const std::string grassPaths[5] = { "resources/cube/Grass001_1K_AmbientOcclusion.jpg",
+                                        "resources/cube/Grass001_1K_Color.jpg",
+                                        "resources/cube/Grass001_1K_Displacement.jpg",
+                                        "resources/cube/Grass001_1K_Normal.jpg",
+                                        "resources/cube/Grass001_1K_Roughness.jpg" };
+    for(uint32_t i = 0; i < 5; i++)
+    {
+        std::filesystem::path grassPath(grassPaths[i]);
+        vkcv::asset::Texture grassTexture = vkcv::asset::loadTexture(grassPath);
+
+        vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, grassTexture.width, grassTexture.height);
+        texture.fill(grassTexture.data.data());
+        texture.generateMipChainImmediate();
+        texture.switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
+
+        texturesArray.push_back(texture);
+    }
+
+	const char* path = argc > 1 ? argv[1] : "resources/cube/cube.gltf";
+	int result = vkcv::asset::loadScene(path, mesh);
+
+	if (result == 1) {
+		std::cout << "Mesh loading successful!" << std::endl;
+	} else {
+		std::cerr << "Mesh loading failed: " << result << std::endl;
+		return 1;
+	}
+
+	assert(!mesh.vertexGroups.empty());
+	auto vertexBuffer = core.createBuffer<uint8_t>(
+			vkcv::BufferType::VERTEX,
+			mesh.vertexGroups[0].vertexBuffer.data.size(),
+			vkcv::BufferMemoryType::DEVICE_LOCAL
+	);
+	
+	vertexBuffer.fill(mesh.vertexGroups[0].vertexBuffer.data);
+
+	auto indexBuffer = core.createBuffer<uint8_t>(
+			vkcv::BufferType::INDEX,
+			mesh.vertexGroups[0].indexBuffer.data.size(),
+			vkcv::BufferMemoryType::DEVICE_LOCAL
+	);
+	
+	indexBuffer.fill(mesh.vertexGroups[0].indexBuffer.data);
+
+	// an example attachment for passes that output to the window
+	const vkcv::AttachmentDescription present_color_attachment(
+		vkcv::AttachmentOperation::STORE,
+		vkcv::AttachmentOperation::CLEAR,
+		core.getSwapchain(windowHandle).getFormat()
+	);
+	
+	const vkcv::AttachmentDescription depth_attachment(
+			vkcv::AttachmentOperation::STORE,
+			vkcv::AttachmentOperation::CLEAR,
+			vk::Format::eD32Sfloat
+	);
+
+	vkcv::PassConfig firstMeshPassDefinition({ present_color_attachment, depth_attachment });
+	vkcv::PassHandle firstMeshPass = core.createPass(firstMeshPassDefinition);
+
+	if (!firstMeshPass) {
+		std::cerr << "Error. Could not create renderpass. Exiting." << std::endl;
+		return EXIT_FAILURE;
+	}
+
+	vkcv::ShaderProgram firstMeshProgram;
+	vkcv::shader::GLSLCompiler compiler;
+	
+	compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/shader.vert"),
+					 [&firstMeshProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+		firstMeshProgram.addShader(shaderStage, path);
+	});
+	
+	compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/shader.frag"),
+					 [&firstMeshProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+		firstMeshProgram.addShader(shaderStage, path);
+	});
+ 
+	auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes;
+
+	
+	std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
+		return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
+	});
+
+    const std::vector<vkcv::VertexAttachment> vertexAttachments = firstMeshProgram.getVertexAttachments();
+	std::vector<vkcv::VertexBinding> bindings;
+	for (size_t i = 0; i < vertexAttachments.size(); i++) {
+		bindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] }));
+	}
+	
+	const vkcv::VertexLayout firstMeshLayout (bindings);
+
+	const std::unordered_map<uint32_t, vkcv::DescriptorBinding> &descriptorBindings = firstMeshProgram.getReflectedDescriptors().at(0);
+
+    std::unordered_map<uint32_t, vkcv::DescriptorBinding> adjustedBindings = descriptorBindings;
+    adjustedBindings[1].descriptorCount = 6;
+
+    vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout(adjustedBindings);
+	vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout);
+
+	const vkcv::GraphicsPipelineConfig firstMeshPipelineConfig {
+        firstMeshProgram,
+        UINT32_MAX,
+        UINT32_MAX,
+        firstMeshPass,
+        {firstMeshLayout},
+		{ core.getDescriptorSetLayout(descriptorSetLayout).vulkanHandle },
+		true
+	};
+	vkcv::GraphicsPipelineHandle firstMeshPipeline = core.createGraphicsPipeline(firstMeshPipelineConfig);
+	
+	if (!firstMeshPipeline) {
+		std::cerr << "Error. Could not create graphics pipeline. Exiting." << std::endl;
+		return EXIT_FAILURE;
+	}
+	
+	if (mesh.textures.empty()) {
+		std::cerr << "Error. No textures found. Exiting." << std::endl;
+		return EXIT_FAILURE;
+	}
+	
+	vkcv::asset::Texture &tex = mesh.textures[0];
+	vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, tex.w, tex.h);
+	texture.fill(tex.data.data());
+	texture.generateMipChainImmediate();
+	texture.switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
+	texturesArray.push_back(texture);
+
+	vkcv::SamplerHandle sampler = core.createSampler(
+		vkcv::SamplerFilterType::LINEAR,
+		vkcv::SamplerFilterType::LINEAR,
+		vkcv::SamplerMipmapMode::LINEAR,
+		vkcv::SamplerAddressMode::REPEAT
+	);
+
+	const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
+		vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[0].offset), vertexBuffer.getVulkanHandle()),
+		vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[1].offset), vertexBuffer.getVulkanHandle()),
+		vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[2].offset), vertexBuffer.getVulkanHandle()) };
+
+	vkcv::DescriptorWrites setWrites;
+	std::vector<vkcv::SampledImageDescriptorWrite> texturesArrayWrites;
+	for(uint32_t i = 0; i < 6; i++)
+	{
+	    texturesArrayWrites.push_back(vkcv::SampledImageDescriptorWrite(1,
+                                                                        texturesArray[i].getHandle(),
+                                                                        0,
+                                                                        false,
+                                                                        i));
+	}
+
+	setWrites.sampledImageWrites	= texturesArrayWrites;
+	setWrites.samplerWrites			= { vkcv::SamplerDescriptorWrite(0, sampler) };
+
+	core.writeDescriptorSet(descriptorSet, setWrites);
+
+	vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight, 1, false).getHandle();
+
+	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
+
+	const vkcv::Mesh renderMesh(vertexBufferBindings, indexBuffer.getVulkanHandle(), mesh.vertexGroups[0].numIndices);
+
+	vkcv::DescriptorSetUsage    descriptorUsage(0, core.getDescriptorSet(descriptorSet).vulkanHandle);
+	vkcv::DrawcallInfo          drawcall(renderMesh, { descriptorUsage },1);
+
+    vkcv::camera::CameraManager cameraManager(core.getWindow(windowHandle));
+    uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
+	uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL);
+	
+	cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -3));
+
+    auto start = std::chrono::system_clock::now();
+    
+	while (vkcv::Window::hasOpenWindow()) {
+        vkcv::Window::pollEvents();
+		
+		if(core.getWindow(windowHandle).getHeight() == 0 || core.getWindow(windowHandle).getWidth() == 0)
+			continue;
+		
+		uint32_t swapchainWidth, swapchainHeight;
+		if (!core.beginFrame(swapchainWidth, swapchainHeight,windowHandle)) {
+			continue;
+		}
+		
+		if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) {
+			depthBuffer = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight).getHandle();
+			
+			windowWidth = swapchainWidth;
+			windowHeight = swapchainHeight;
+		}
+  
+		auto end = std::chrono::system_clock::now();
+		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
+		
+		start = end;
+		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
+        glm::mat4 mvp = cameraManager.getActiveCamera().getMVP();
+
+		vkcv::PushConstants pushConstants (sizeof(glm::mat4));
+		pushConstants.appendDrawcall(mvp);
+
+		const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
+		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
+
+		core.recordDrawcallsToCmdStream(
+			cmdStream,
+			firstMeshPass,
+			firstMeshPipeline,
+			pushConstants,
+			{ drawcall },
+			renderTargets,
+			windowHandle);
+		core.prepareSwapchainImageForPresent(cmdStream);
+		core.submitCommandStream(cmdStream);
+		core.endFrame(windowHandle);
+	}
+	
+	return 0;
+}
diff --git a/projects/first_scene/CMakeLists.txt b/projects/first_scene/CMakeLists.txt
index ba2f7b1a7ae4845a12b9701269361a0a3f8affb7..21d6857e3b0086e31cc941659c7e8b3c57440922 100644
--- a/projects/first_scene/CMakeLists.txt
+++ b/projects/first_scene/CMakeLists.txt
@@ -22,7 +22,7 @@ if(MSVC)
 endif()
 
 # including headers of dependencies and the VkCV framework
-target_include_directories(first_scene SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_scene_include} ${vkcv_shader_compiler_include})
+target_include_directories(first_scene SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_scene_include} ${vkcv_shader_compiler_include} ${vkcv_gui_include})
 
 # linking with libraries from all dependencies and the VkCV framework
-target_link_libraries(first_scene vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_scene vkcv_shader_compiler)
+target_link_libraries(first_scene vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_scene vkcv_shader_compiler vkcv_gui)
diff --git a/projects/first_scene/src/main.cpp b/projects/first_scene/src/main.cpp
index 4891fecbf8e0fc5dee3f9930b477aa03115916d2..a078776eb4b7688f51d45cecb7e08d5ba9d0d973 100644
--- a/projects/first_scene/src/main.cpp
+++ b/projects/first_scene/src/main.cpp
@@ -2,6 +2,7 @@
 #include <vkcv/Core.hpp>
 #include <GLFW/glfw3.h>
 #include <vkcv/camera/CameraManager.hpp>
+#include <vkcv/gui/GUI.hpp>
 #include <chrono>
 #include <vkcv/asset/asset_loader.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
@@ -23,7 +24,9 @@ int main(int argc, const char** argv) {
 	vkcv::Window& window = core.getWindow(windowHandle);
 	vkcv::camera::CameraManager cameraManager(window);
 
-	uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
+    vkcv::gui::GUI gui (core, windowHandle);
+
+    uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
 	uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL);
 	
 	cameraManager.getCamera(camIndex0).setPosition(glm::vec3(-8, 1, -0.5));
@@ -148,6 +151,18 @@ int main(int argc, const char** argv) {
 		
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
+
+        auto stop = std::chrono::system_clock::now();
+        auto kektime = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
+
+        gui.beginGUI();
+
+        ImGui::Begin("Settings");
+        ImGui::Text("Deltatime %fms, %f", 0.001 * static_cast<double>(kektime.count()), 1/(0.000001 * static_cast<double>(kektime.count())));
+        ImGui::End();
+
+        gui.endGUI();
+
 		core.endFrame(windowHandle);
 	}
 	
diff --git a/projects/indirect_draw/.gitignore b/projects/indirect_draw/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..92714f6a3381225d29daff0d99efe51e12b40970
--- /dev/null
+++ b/projects/indirect_draw/.gitignore
@@ -0,0 +1 @@
+indirect_draw
\ No newline at end of file
diff --git a/projects/indirect_draw/CMakeLists.txt b/projects/indirect_draw/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a1bc8073a4105b291f0b3177cb6d252f8ce9e9a4
--- /dev/null
+++ b/projects/indirect_draw/CMakeLists.txt
@@ -0,0 +1,28 @@
+cmake_minimum_required(VERSION 3.16)
+project(indirect_draw)
+
+# setting c++ standard for the project
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+# this should fix the execution path to load local files from the project
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+
+# adding source files to the project
+add_executable(indirect_draw src/main.cpp)
+
+# this should fix the execution path to load local files from the project (for MSVC)
+if(MSVC)
+	set_target_properties(indirect_draw PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
+	set_target_properties(indirect_draw PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
+    
+    # in addition to setting the output directory, the working directory has to be set
+	# by default visual studio sets the working directory to the build directory, when using the debugger
+	set_target_properties(indirect_draw PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
+endif()
+
+# including headers of dependencies and the VkCV framework
+target_include_directories(indirect_draw SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include} ${vkcv_gui_include})
+
+# linking with libraries from all dependencies and the VkCV framework
+target_link_libraries(indirect_draw vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler vkcv_gui)
diff --git a/projects/indirect_draw/resources/Sponza/Sponza.bin b/projects/indirect_draw/resources/Sponza/Sponza.bin
new file mode 100644
index 0000000000000000000000000000000000000000..cfedd26ca5a67b6d0a47d44d13a75e14a141717a
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/Sponza.bin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4b809f7a17687dc99e6f41ca1ea32c06eded8779bf34d16f1f565d750b0ffd68
+size 6347696
diff --git a/projects/indirect_draw/resources/Sponza/Sponza.gltf b/projects/indirect_draw/resources/Sponza/Sponza.gltf
new file mode 100644
index 0000000000000000000000000000000000000000..172ea07e21c94465211c860cd805355704cef230
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/Sponza.gltf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5cc0ecad5c4694088ff820e663619c370421afc1323ac487406e8e9b4735d787
+size 713962
diff --git a/projects/indirect_draw/resources/Sponza/SponzaFloor.bin b/projects/indirect_draw/resources/Sponza/SponzaFloor.bin
new file mode 100644
index 0000000000000000000000000000000000000000..684251288f35070d2e7d244877fd844cc00ca632
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/SponzaFloor.bin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:678455aca641cb1f449aa1a5054a7cae132be81c2b333aac283053967da66df0
+size 512
diff --git a/projects/indirect_draw/resources/Sponza/SponzaFloor.gltf b/projects/indirect_draw/resources/Sponza/SponzaFloor.gltf
new file mode 100644
index 0000000000000000000000000000000000000000..b45f1c55ef85f2aa1d4bff01df3d9625aa38c809
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/SponzaFloor.gltf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a6deb75441b1138b50a6b0eec05e60df276fe8fb6d58118fdfce2090b6fbe734
+size 3139
diff --git a/projects/indirect_draw/resources/Sponza/background.png b/projects/indirect_draw/resources/Sponza/background.png
new file mode 100644
index 0000000000000000000000000000000000000000..b64def129da38f4e23d89e21b4af1039008a4327
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/background.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f5b5f900ff8ed83a31750ec8e428b5b91273794ddcbfc4e4b8a6a7e781f8c686
+size 1417666
diff --git a/projects/indirect_draw/resources/Sponza/chain_texture.png b/projects/indirect_draw/resources/Sponza/chain_texture.png
new file mode 100644
index 0000000000000000000000000000000000000000..c1e1768cff78e0614ad707eca8602a4c4edab5e5
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/chain_texture.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d8362cfd472880daeaea37439326a4651d1338680ae69bb2513fc6b17c8de7d4
+size 490895
diff --git a/projects/indirect_draw/resources/Sponza/lion.png b/projects/indirect_draw/resources/Sponza/lion.png
new file mode 100644
index 0000000000000000000000000000000000000000..c49c7f0ed31e762e19284d0d3624fbc47664e56b
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/lion.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9f882f746c3a9cd51a9c6eedc1189b97668721d91a3fe49232036e789912c652
+size 2088728
diff --git a/projects/indirect_draw/resources/Sponza/spnza_bricks_a_diff.png b/projects/indirect_draw/resources/Sponza/spnza_bricks_a_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..cde4c7a6511e9a5f03c63ad996437fcdba3ce2df
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/spnza_bricks_a_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b94219c2f5f943f3f4715c74e7d1038bf0ab3b3b3216a758eaee67f875df0851
+size 1928829
diff --git a/projects/indirect_draw/resources/Sponza/sponza_arch_diff.png b/projects/indirect_draw/resources/Sponza/sponza_arch_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..bcd9bda2918d226039f9e2d03902d377b706fab6
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_arch_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c0df2c8a01b2843b1c792b494f7173cdbc4f834840fc2177af3e5d690fceda57
+size 1596151
diff --git a/projects/indirect_draw/resources/Sponza/sponza_ceiling_a_diff.png b/projects/indirect_draw/resources/Sponza/sponza_ceiling_a_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..59de631ffac4414cabf69b2dc794c46fc187d6cb
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_ceiling_a_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ab6c187a81aa68f4eba30119e17fce2e4882a9ec320f70c90482dbe9da82b1c6
+size 1872074
diff --git a/projects/indirect_draw/resources/Sponza/sponza_column_a_diff.png b/projects/indirect_draw/resources/Sponza/sponza_column_a_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..01a82432d3f9939bbefe850bdb900f1ff9a3f6db
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_column_a_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2c291507e2808bb83e160ab4b020689817df273baad3713a9ad19ac15fac6826
+size 1840992
diff --git a/projects/indirect_draw/resources/Sponza/sponza_column_b_diff.png b/projects/indirect_draw/resources/Sponza/sponza_column_b_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..10a660cce2a5a9b8997772c746058ce23e7d45d7
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_column_b_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2820b0267c4289c6cedbb42721792a57ef244ec2d0935941011c2a7d3fe88a9b
+size 2170433
diff --git a/projects/indirect_draw/resources/Sponza/sponza_column_c_diff.png b/projects/indirect_draw/resources/Sponza/sponza_column_c_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc46fd979044a938d3adca7601689e71504e48bf
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_column_c_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a0bc993ff59865468ef4530798930c7dfefb07482d71db45bc2a520986b27735
+size 2066950
diff --git a/projects/indirect_draw/resources/Sponza/sponza_curtain_blue_diff.png b/projects/indirect_draw/resources/Sponza/sponza_curtain_blue_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..384c8c2c051160d530eb3ac8b05c9c60752a2d2b
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_curtain_blue_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b85c6bb3cd5105f48d3812ec8e7a1068521ce69e917300d79e136e19d45422fb
+size 9510905
diff --git a/projects/indirect_draw/resources/Sponza/sponza_curtain_diff.png b/projects/indirect_draw/resources/Sponza/sponza_curtain_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..af842e9f5fe18c1f609875e00899a6770fa4488b
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_curtain_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:563c56bdbbee395a6ef7f0c51c8ac9223c162e517b4cdba0d4654e8de27c98d8
+size 9189263
diff --git a/projects/indirect_draw/resources/Sponza/sponza_curtain_green_diff.png b/projects/indirect_draw/resources/Sponza/sponza_curtain_green_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c9b6391a199407637fa71033d79fb58b8b4f0d7
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_curtain_green_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:238fe1c7f481388d1c1d578c2da8d411b99e8f0030ab62060a306db333124476
+size 8785458
diff --git a/projects/indirect_draw/resources/Sponza/sponza_details_diff.png b/projects/indirect_draw/resources/Sponza/sponza_details_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..12656686362c3e0a297e060491f33bd7351551f9
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_details_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cb1223b3bb82f8757e7df25a6891f1239cdd7ec59990340e952fb2d6b7ea570c
+size 1522643
diff --git a/projects/indirect_draw/resources/Sponza/sponza_fabric_blue_diff.png b/projects/indirect_draw/resources/Sponza/sponza_fabric_blue_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..879d16ef84722a4fc13e83a771778de326e4bc54
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_fabric_blue_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:467d290bf5d4b2a017da140ba9e244ed8a8a9be5418a9ac9bcb4ad572ae2d7ab
+size 2229440
diff --git a/projects/indirect_draw/resources/Sponza/sponza_fabric_diff.png b/projects/indirect_draw/resources/Sponza/sponza_fabric_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..3311287a219d2148620b87fe428fea071688d051
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_fabric_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1594f59cc2848db26add47361f4e665e3d8afa147760ed915d839fea42b20287
+size 2267382
diff --git a/projects/indirect_draw/resources/Sponza/sponza_fabric_green_diff.png b/projects/indirect_draw/resources/Sponza/sponza_fabric_green_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..de110f369004388dae4cd5067c63428db3a07834
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_fabric_green_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:902b87faab221173bf370cea7c74cb9060b4d870ac6316b190dafded1cb12993
+size 2258220
diff --git a/projects/indirect_draw/resources/Sponza/sponza_flagpole_diff.png b/projects/indirect_draw/resources/Sponza/sponza_flagpole_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..5f6e0812a0df80346318baa3cb50a6888afc58f8
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_flagpole_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bfffb62e770959c725d0f3db6dc7dbdd46a380ec55ef884dab94d44ca017b438
+size 1425673
diff --git a/projects/indirect_draw/resources/Sponza/sponza_floor_a_diff.png b/projects/indirect_draw/resources/Sponza/sponza_floor_a_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..788ed764f79ba724f04a2d603076a5b85013e188
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_floor_a_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a16f9230fa91f9f31dfca6216ce205f1ef132d44f3b012fbf6efc0fba69770ab
+size 1996838
diff --git a/projects/indirect_draw/resources/Sponza/sponza_roof_diff.png b/projects/indirect_draw/resources/Sponza/sponza_roof_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..c5b84261fdd1cc776a94b3ce398c7806b895f9a3
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_roof_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7fc412138c20da19f8173e53545e771f4652558dff624d4dc67143e40efe562b
+size 2320533
diff --git a/projects/indirect_draw/resources/Sponza/sponza_thorn_diff.png b/projects/indirect_draw/resources/Sponza/sponza_thorn_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..7a9142674a7d4a6f94a48c5152cf0300743b597a
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/sponza_thorn_diff.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a73a17c883cd0d0d67cfda2dc4118400a916366c05b9a5ac465f0c8b30fd9c8e
+size 635001
diff --git a/projects/indirect_draw/resources/Sponza/vase_dif.png b/projects/indirect_draw/resources/Sponza/vase_dif.png
new file mode 100644
index 0000000000000000000000000000000000000000..61236a81cb324af8797b05099cd264cefe189e56
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/vase_dif.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:53d06f52bf9e59df4cf00237707cca76c4f692bda61a62b06a30d321311d6dd9
+size 1842101
diff --git a/projects/indirect_draw/resources/Sponza/vase_hanging.png b/projects/indirect_draw/resources/Sponza/vase_hanging.png
new file mode 100644
index 0000000000000000000000000000000000000000..36a3cee71d8213225090c74f8c0dce33b9d44378
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/vase_hanging.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a9d10b4f27a3c9a78d5bac882fdd4b6a6987c262f48fa490670fe5e235951e31
+size 1432804
diff --git a/projects/indirect_draw/resources/Sponza/vase_plant.png b/projects/indirect_draw/resources/Sponza/vase_plant.png
new file mode 100644
index 0000000000000000000000000000000000000000..7ad95e702e229f1ebd803e5203a266d15f2c07b9
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/vase_plant.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d2087371ff02212fb7014b6daefa191cf5676d2227193fff261a5d02f554cb8e
+size 998089
diff --git a/projects/indirect_draw/resources/Sponza/vase_round.png b/projects/indirect_draw/resources/Sponza/vase_round.png
new file mode 100644
index 0000000000000000000000000000000000000000..c17953abc000c44b8991e23c136c2b67348f3d1b
--- /dev/null
+++ b/projects/indirect_draw/resources/Sponza/vase_round.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aa23d48d492d5d4ada2ddb27d1ef22952b214e6eb3b301c65f9d88442723d20a
+size 1871399
diff --git a/projects/indirect_draw/resources/Szene/Szene.bin b/projects/indirect_draw/resources/Szene/Szene.bin
new file mode 100644
index 0000000000000000000000000000000000000000..c87d27637516b0bbf864251dd162773f5cc53e06
--- /dev/null
+++ b/projects/indirect_draw/resources/Szene/Szene.bin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ee4742718f720d589a2a03f5d879f8c50ba9057718d191a43b046eaa9071080d
+size 70328
diff --git a/projects/indirect_draw/resources/Szene/Szene.gltf b/projects/indirect_draw/resources/Szene/Szene.gltf
new file mode 100644
index 0000000000000000000000000000000000000000..e5a32b29af5d0a2ac5f497e60b4b92c1873e1df9
--- /dev/null
+++ b/projects/indirect_draw/resources/Szene/Szene.gltf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:75ba834118792ebbacf528a1690c7d04df4b4c8122b9f99a9aa9a9d075d2c86a
+size 7421
diff --git a/projects/indirect_draw/resources/Szene/boards2_vcyc.jpg b/projects/indirect_draw/resources/Szene/boards2_vcyc.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..2636039e272289c0fba3fa2d88a060b857501248
--- /dev/null
+++ b/projects/indirect_draw/resources/Szene/boards2_vcyc.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564
+size 1192476
diff --git a/projects/indirect_draw/resources/Szene/boards2_vcyc_jpg.jpg b/projects/indirect_draw/resources/Szene/boards2_vcyc_jpg.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..2636039e272289c0fba3fa2d88a060b857501248
--- /dev/null
+++ b/projects/indirect_draw/resources/Szene/boards2_vcyc_jpg.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564
+size 1192476
diff --git a/projects/indirect_draw/resources/cube/Grass001_1K_AmbientOcclusion.jpg b/projects/indirect_draw/resources/cube/Grass001_1K_AmbientOcclusion.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..2217fb53744f9166232a40b78ee9518e04b0ded5
--- /dev/null
+++ b/projects/indirect_draw/resources/cube/Grass001_1K_AmbientOcclusion.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f6c88f8f3facd9e91f8dd160bd8c4a602e433872ed18e08015a9fa9dfff889de
+size 901465
diff --git a/projects/indirect_draw/resources/cube/Grass001_1K_Color.jpg b/projects/indirect_draw/resources/cube/Grass001_1K_Color.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..b8aa1533ae5a023a5fc8457f30ed60efe3bda32d
--- /dev/null
+++ b/projects/indirect_draw/resources/cube/Grass001_1K_Color.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:878b8fe4747d9ce693220edea1915de550e8f14d402d26a0915f162d40f84e91
+size 1763328
diff --git a/projects/indirect_draw/resources/cube/Grass001_1K_Displacement.jpg b/projects/indirect_draw/resources/cube/Grass001_1K_Displacement.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..89789cba150eea7c7abbdc1851090f6021af978a
--- /dev/null
+++ b/projects/indirect_draw/resources/cube/Grass001_1K_Displacement.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1e8300e1107bee7e681059d9da0a7e3ca422977e8b6f496e16452a4c94b3d385
+size 912347
diff --git a/projects/indirect_draw/resources/cube/Grass001_1K_Normal.jpg b/projects/indirect_draw/resources/cube/Grass001_1K_Normal.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..3163d6391592ace10446cb71141a2192e63e9660
--- /dev/null
+++ b/projects/indirect_draw/resources/cube/Grass001_1K_Normal.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:684426b49e841e267f12f06dc955b5b1d01b3ba3659bea0c5d73be889700929f
+size 2336471
diff --git a/projects/indirect_draw/resources/cube/Grass001_1K_Roughness.jpg b/projects/indirect_draw/resources/cube/Grass001_1K_Roughness.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..10e6ac33badf2a4795766a66546a62c67eb8b558
--- /dev/null
+++ b/projects/indirect_draw/resources/cube/Grass001_1K_Roughness.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d952ffb098faf9ac5eb25134eabac08f0c65d927a448b5e7b4f9c72510cfcbe0
+size 822659
diff --git a/projects/indirect_draw/resources/cube/boards2_vcyc_jpg.jpg b/projects/indirect_draw/resources/cube/boards2_vcyc_jpg.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..2636039e272289c0fba3fa2d88a060b857501248
--- /dev/null
+++ b/projects/indirect_draw/resources/cube/boards2_vcyc_jpg.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564
+size 1192476
diff --git a/projects/indirect_draw/resources/cube/cube.bin b/projects/indirect_draw/resources/cube/cube.bin
new file mode 100644
index 0000000000000000000000000000000000000000..3303cd8635848bee18e10ab8754d5e4e7218db92
--- /dev/null
+++ b/projects/indirect_draw/resources/cube/cube.bin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9bb9b6b8bbe50a0aaa517057f245ee844f80afa7426dacb2aed4128f71629ce4
+size 840
diff --git a/projects/indirect_draw/resources/cube/cube.blend b/projects/indirect_draw/resources/cube/cube.blend
new file mode 100644
index 0000000000000000000000000000000000000000..62ccb2c742094bcfb5ed194ab905bffae86bfd65
--- /dev/null
+++ b/projects/indirect_draw/resources/cube/cube.blend
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a6c1e245f259c610528c9485db6688928faac0ab2addee9e3c2dde7740e4dd09
+size 774920
diff --git a/projects/indirect_draw/resources/cube/cube.blend1 b/projects/indirect_draw/resources/cube/cube.blend1
new file mode 100644
index 0000000000000000000000000000000000000000..13f21dcca218d7bc7a07a8a9682b5e1d9e607736
--- /dev/null
+++ b/projects/indirect_draw/resources/cube/cube.blend1
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f4496f423569b8ca81f3b3a55fad00f925557e0193fb9dbe6cdce7e71fb48f7b
+size 774920
diff --git a/projects/indirect_draw/resources/cube/cube.glb b/projects/indirect_draw/resources/cube/cube.glb
new file mode 100644
index 0000000000000000000000000000000000000000..66a42c65e71dcf375e04cc378256024dd3c7834d
--- /dev/null
+++ b/projects/indirect_draw/resources/cube/cube.glb
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:198568b715f397d78f7c358c0f709a419e7fd677e54cdec7c19f71b5ed264897
+size 1194508
diff --git a/projects/indirect_draw/resources/cube/cube.gltf b/projects/indirect_draw/resources/cube/cube.gltf
new file mode 100644
index 0000000000000000000000000000000000000000..428176144843dd06c78fe1d11a6392a0ea02b22d
--- /dev/null
+++ b/projects/indirect_draw/resources/cube/cube.gltf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f82f455647a84ca6242882ae26a79a499d3ce594f8de317ab89488c5b79721ac
+size 2823
diff --git a/projects/indirect_draw/resources/shaders/culling.comp b/projects/indirect_draw/resources/shaders/culling.comp
new file mode 100644
index 0000000000000000000000000000000000000000..5d5884b9e494baf56ab32576f094f34584b163b3
--- /dev/null
+++ b/projects/indirect_draw/resources/shaders/culling.comp
@@ -0,0 +1,58 @@
+#version 460
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_EXT_nonuniform_qualifier : enable
+
+layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
+
+struct Plane{
+    vec3    pointOnPlane;
+    float   padding0;
+    vec3    normal;
+    float   padding1;
+};
+
+struct DrawCommand{
+    uint indexCount;
+    uint instanceCount;
+    uint firstIndex;
+    int vertexOffset;
+    uint firstInstance;
+};
+
+layout(set=0, binding=0, std140) readonly uniform cameraPlaneBuffer{
+    Plane cameraPlanes[6];
+};
+
+layout(set=0, binding=1, std430) buffer DrawCommandsBuffer{
+    DrawCommand commands[];
+};
+
+layout(set=0, binding=2, std430) readonly buffer boundingBoxBuffer{
+    // x,y,z for meanPosition;
+    // w for boundingSphereRadius
+    vec4 boundingBoxes[];
+};
+
+bool isSphereInsideFrustum(vec3 spherePos, float sphereRadius){
+    bool isInside = true;
+    for(int i = 0; i < 6; i++){
+        Plane p     = cameraPlanes[i];
+        isInside    = isInside && dot(p.normal, spherePos - p.pointOnPlane) - sphereRadius < 0;
+    }
+    return isInside;
+}
+
+void main(){
+    if(gl_GlobalInvocationID.x > commands.length()){
+        return;
+    }
+
+    vec4 bbox = boundingBoxes[gl_GlobalInvocationID.x];
+    if (isSphereInsideFrustum(bbox.xyz, bbox.w)){
+        commands[gl_GlobalInvocationID.x].instanceCount = 1;
+    }
+    else{
+        commands[gl_GlobalInvocationID.x].instanceCount = 0;
+    }
+}
+
diff --git a/projects/indirect_draw/resources/shaders/shader.frag b/projects/indirect_draw/resources/shaders/shader.frag
new file mode 100644
index 0000000000000000000000000000000000000000..f0a0b49314926ef9195d9a145b805b55d17b8807
--- /dev/null
+++ b/projects/indirect_draw/resources/shaders/shader.frag
@@ -0,0 +1,18 @@
+#version 460
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_EXT_nonuniform_qualifier : enable
+
+layout(location = 0) in vec3 passNormal;
+layout(location = 1) in vec2 passUV;
+layout(location = 2) in flat uint passDrawIndex;
+
+layout(location = 0) out vec3 outColor;
+
+layout(set=0, binding=0) uniform sampler standardSampler;
+layout(set=0, binding=2) uniform texture2D baseColorTex[];
+
+
+void main()
+{
+    outColor = texture(sampler2D(baseColorTex[nonuniformEXT(passDrawIndex)], standardSampler), passUV).rgb;
+}
\ No newline at end of file
diff --git a/projects/indirect_draw/resources/shaders/shader.vert b/projects/indirect_draw/resources/shaders/shader.vert
new file mode 100644
index 0000000000000000000000000000000000000000..10b250761437dce6eea01dd195e1248fc9c0bd90
--- /dev/null
+++ b/projects/indirect_draw/resources/shaders/shader.vert
@@ -0,0 +1,27 @@
+#version 460
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shader_draw_parameters : enable
+
+layout(location = 0) in vec3 inPosition;
+layout(location = 1) in vec3 inNormal;
+layout(location = 2) in vec2 inUV;
+
+layout(std430, binding=1) readonly buffer uModel {
+    mat4 modelMatrix[];
+};
+
+layout(location = 0) out vec3 passNormal;
+layout(location = 1) out vec2 passUV;
+layout(location = 2) out flat uint passDrawIndex;
+
+layout( push_constant ) uniform constants{
+    mat4 vp;
+};
+
+void main()
+{
+	gl_Position = vp * modelMatrix[gl_DrawID] * vec4(inPosition, 1.0);
+	passNormal  = inNormal;
+    passUV      = inUV;
+    passDrawIndex = gl_DrawID;
+}
\ No newline at end of file
diff --git a/projects/indirect_draw/resources/triangle/Triangle.bin b/projects/indirect_draw/resources/triangle/Triangle.bin
new file mode 100644
index 0000000000000000000000000000000000000000..57f26ad96592b64377e6aa93823d96a94e6c5022
--- /dev/null
+++ b/projects/indirect_draw/resources/triangle/Triangle.bin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:412ebd5f7242c266b4957e7e26be13aa331dbcb7bbb854ab334a2437ae8ed959
+size 104
diff --git a/projects/indirect_draw/resources/triangle/Triangle.blend b/projects/indirect_draw/resources/triangle/Triangle.blend
new file mode 100644
index 0000000000000000000000000000000000000000..2421dc5e1bb029d73a9ec09cc4530c5196851fd7
--- /dev/null
+++ b/projects/indirect_draw/resources/triangle/Triangle.blend
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:387e544df173219fbf292a64a6656d1d782bbf71a5a9e9fdef0a308f47b05477
+size 758144
diff --git a/projects/indirect_draw/resources/triangle/Triangle.glb b/projects/indirect_draw/resources/triangle/Triangle.glb
new file mode 100644
index 0000000000000000000000000000000000000000..4148620cd6af0dadbc791aa1c52bb5431a40884b
--- /dev/null
+++ b/projects/indirect_draw/resources/triangle/Triangle.glb
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f4be087a605212d139416b5352a018283b26b99260cbcddb7013a1beeb331227
+size 980
diff --git a/projects/indirect_draw/resources/triangle/Triangle.gltf b/projects/indirect_draw/resources/triangle/Triangle.gltf
new file mode 100644
index 0000000000000000000000000000000000000000..a188e6ee16a5e8486cf307c7bda8cfd99bdbeea6
--- /dev/null
+++ b/projects/indirect_draw/resources/triangle/Triangle.gltf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d5fc354e040f79cff329e919677b194c75e3a522c6406f75c1108ad9575f12ec
+size 2202
diff --git a/projects/indirect_draw/src/main.cpp b/projects/indirect_draw/src/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..88e7005f2912bb448980bd4c1219245481f1559a
--- /dev/null
+++ b/projects/indirect_draw/src/main.cpp
@@ -0,0 +1,619 @@
+#include <iostream>
+#include <vkcv/Core.hpp>
+#include <vkcv/camera/CameraManager.hpp>
+#include <chrono>
+#include <vkcv/gui/GUI.hpp>
+#include <vkcv/asset/asset_loader.hpp>
+#include <vkcv/shader/GLSLCompiler.hpp>
+
+struct Plane {
+    glm::vec3   pointOnPlane;
+    float       padding0;
+    glm::vec3   normal;
+    float       padding1;
+};
+
+struct CameraPlanes {
+    Plane planes[6];
+};
+
+CameraPlanes computeCameraPlanes(const vkcv::camera::Camera& camera) {
+    const float     fov     = camera.getFov();
+    const glm::vec3 pos     = camera.getPosition();
+    const float     ratio   = camera.getRatio();
+    const glm::vec3 forward = glm::normalize(camera.getFront());
+    float near;
+    float far;
+    camera.getNearFar(near, far);
+
+    glm::vec3 up    = glm::vec3(0, -1, 0);
+    glm::vec3 right = glm::normalize(glm::cross(forward, up));
+    up              = glm::cross(forward, right);
+
+    const glm::vec3 nearCenter      = pos + forward * near;
+    const glm::vec3 farCenter       = pos + forward * far;
+
+    const float tanFovHalf          = glm::tan(fov / 2);
+
+    const glm::vec3 nearUpCenter    = nearCenter + up    * tanFovHalf * near;
+    const glm::vec3 nearDownCenter  = nearCenter - up    * tanFovHalf * near;
+    const glm::vec3 nearRightCenter = nearCenter + right * tanFovHalf * near * ratio;
+    const glm::vec3 nearLeftCenter  = nearCenter - right * tanFovHalf * near * ratio;
+
+    const glm::vec3 farUpCenter     = farCenter + up    * tanFovHalf * far;
+    const glm::vec3 farDownCenter   = farCenter - up    * tanFovHalf * far;
+    const glm::vec3 farRightCenter  = farCenter + right * tanFovHalf * far * ratio;
+    const glm::vec3 farLeftCenter   = farCenter - right * tanFovHalf * far * ratio;
+
+    CameraPlanes cameraPlanes;
+    // near
+    cameraPlanes.planes[0].pointOnPlane = nearCenter;
+    cameraPlanes.planes[0].normal       = -forward;
+    // far
+    cameraPlanes.planes[1].pointOnPlane = farCenter;
+    cameraPlanes.planes[1].normal       = forward;
+
+    // top
+    cameraPlanes.planes[2].pointOnPlane = nearUpCenter;
+    cameraPlanes.planes[2].normal       = glm::normalize(glm::cross(farUpCenter - nearUpCenter, right));
+    // bot
+    cameraPlanes.planes[3].pointOnPlane = nearDownCenter;
+    cameraPlanes.planes[3].normal       = glm::normalize(glm::cross(right, farDownCenter - nearDownCenter));
+
+    // right
+    cameraPlanes.planes[4].pointOnPlane = nearRightCenter;
+    cameraPlanes.planes[4].normal       = glm::normalize(glm::cross(up, farRightCenter - nearRightCenter));
+    // left
+    cameraPlanes.planes[5].pointOnPlane = nearLeftCenter;
+    cameraPlanes.planes[5].normal       = glm::normalize(glm::cross(farLeftCenter - nearLeftCenter, up));
+
+    return cameraPlanes;
+}
+
+struct Vertex
+{
+	glm::vec3 position;
+	glm::vec3 normal;
+	glm::vec2 uv;
+};
+
+struct CompiledMaterial
+{
+    std::vector<vkcv::Image> baseColor;
+    std::vector<vkcv::Image> metalRough;
+    std::vector<vkcv::Image> normal;
+    std::vector<vkcv::Image> occlusion;
+};
+
+void interleaveScene(vkcv::asset::Scene scene,
+                     std::vector<std::vector<Vertex>> &interleavedVertexBuffers,
+                     std::vector<glm::vec4> &boundingBoxBuffers)
+{
+    for(const auto &mesh : scene.meshes)
+    {
+        for(auto vertexGroupIndex : mesh.vertexGroups)
+        {
+			// Sort attributes to fix it!
+			auto& attributes = scene.vertexGroups[vertexGroupIndex].vertexBuffer.attributes;
+	
+			std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
+				return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
+			});
+			
+            const auto &vertexGroup = scene.vertexGroups[vertexGroupIndex];
+
+            const vkcv::asset::VertexAttribute positionAttribute = vertexGroup.vertexBuffer.attributes[0];
+            const vkcv::asset::VertexAttribute normalAttribute   = vertexGroup.vertexBuffer.attributes[1];
+            const vkcv::asset::VertexAttribute uvAttribute       = vertexGroup.vertexBuffer.attributes[2];
+
+            assert(positionAttribute.type   == vkcv::asset::PrimitiveType::POSITION);
+            assert(normalAttribute.type     == vkcv::asset::PrimitiveType::NORMAL);
+            assert(uvAttribute.type         == vkcv::asset::PrimitiveType::TEXCOORD_0);
+
+            const uint64_t &verticesCount          = vertexGroup.numVertices;
+            const std::vector<uint8_t> &vertexData = vertexGroup.vertexBuffer.data;
+
+            std::vector<Vertex> vertices;
+            vertices.reserve(verticesCount);
+
+            const size_t positionStride = positionAttribute.stride == 0 ? sizeof(glm::vec3) : positionAttribute.stride;
+            const size_t normalStride   = normalAttribute.stride   == 0 ? sizeof(glm::vec3) : normalAttribute.stride;
+            const size_t uvStride       = uvAttribute.stride       == 0 ? sizeof(glm::vec2) : uvAttribute.stride;
+
+            glm::vec3 max_pos(-std::numeric_limits<float>::max());
+            glm::vec3 min_pos(std::numeric_limits<float>::max());
+
+            for(size_t i = 0; i < verticesCount; i++)
+            {
+                const size_t positionOffset = positionAttribute.offset + positionStride * i;
+                const size_t normalOffset   = normalAttribute.offset   + normalStride * i;
+                const size_t uvOffset       = uvAttribute.offset       + uvStride * i;
+
+                Vertex v;
+
+                v.position = *reinterpret_cast<const glm::vec3*>(&(vertexData[positionOffset]));
+                v.normal   = *reinterpret_cast<const glm::vec3*>(&(vertexData[normalOffset]));
+                v.uv       = *reinterpret_cast<const glm::vec3*>(&(vertexData[uvOffset]));
+
+                glm::vec3 posWorld = glm::make_mat4(mesh.modelMatrix.data()) * glm::vec4(v.position, 1);
+
+                max_pos.x = glm::max(max_pos.x, posWorld.x);
+                max_pos.y = glm::max(max_pos.y, posWorld.y);
+                max_pos.z = glm::max(max_pos.z, posWorld.z);
+
+                min_pos.x = glm::min(min_pos.x, posWorld.x);
+                min_pos.y = glm::min(min_pos.y, posWorld.y);
+                min_pos.z = glm::min(min_pos.z, posWorld.z);
+
+                vertices.push_back(v);
+            }
+
+            const glm::vec3 boundingPosition = (max_pos + min_pos) / 2.0f;
+            const float radius = glm::distance(max_pos, min_pos) / 2.0f;
+
+            boundingBoxBuffers.emplace_back(boundingPosition.x,
+                                            boundingPosition.y,
+                                            boundingPosition.z,
+                                            radius);
+
+            interleavedVertexBuffers.push_back(vertices);
+        }
+    }
+}
+
+// Assumes the meshes use index buffers
+void compileMeshForIndirectDraw(vkcv::Core &core,
+                                const vkcv::asset::Scene &scene,
+                                std::vector<uint8_t> &compiledVertexBuffer,
+                                std::vector<uint8_t> &compiledIndexBuffer,
+                                CompiledMaterial &compiledMat,
+                                std::vector<vk::DrawIndexedIndirectCommand> &indexedIndirectCommands)
+{
+    vkcv::Image pseudoImg = core.createImage(vk::Format::eR8G8B8A8Srgb, 2, 2);
+    std::vector<uint8_t> pseudoData = {0, 0, 0, 0};
+    pseudoImg.fill(pseudoData.data());
+    pseudoImg.switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
+
+	uint32_t vertexOffset = 0;
+    for (const auto &mesh : scene.meshes)
+    {
+        for(auto &vertexGroupIndex : mesh.vertexGroups)
+        {
+            auto &vertexGroup = scene.vertexGroups[vertexGroupIndex];
+
+            auto &material      = scene.materials[vertexGroup.materialIndex];
+
+            if(material.baseColor == -1)
+            {
+                std::cout << "baseColor is -1! Pushing pseudo-texture!" << std::endl;
+                compiledMat.baseColor.push_back(pseudoImg);
+            }
+            else
+            {
+                auto &baseColor     = scene.textures[material.baseColor];
+
+                vkcv::Image baseColorImg = core.createImage(vk::Format::eR8G8B8A8Srgb, baseColor.w, baseColor.h);
+                baseColorImg.fill(baseColor.data.data());
+                baseColorImg.generateMipChainImmediate();
+                baseColorImg.switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
+
+                compiledMat.baseColor.push_back(baseColorImg);
+            }
+
+            indexedIndirectCommands.emplace_back(static_cast<uint32_t>(vertexGroup.numIndices),
+                                                 1,
+                                                 static_cast<uint32_t>(compiledIndexBuffer.size() / 4),
+                                                 vertexOffset,
+                                                 static_cast<uint32_t>(indexedIndirectCommands.size()));
+
+            vertexOffset += vertexGroup.numVertices;
+
+            compiledVertexBuffer.insert(compiledVertexBuffer.end(),
+                                        vertexGroup.vertexBuffer.data.begin(),
+                                        vertexGroup.vertexBuffer.data.end());
+
+			if(vertexGroup.indexBuffer.type == vkcv::asset::IndexType::UINT8)
+            {
+				for(auto index : vertexGroup.indexBuffer.data)
+				{
+                    uint32_t index32 = static_cast<uint32_t>(index);
+                    uint8_t firstByte = index32;
+                    uint8_t secondByte = index32 >> 8;
+                    uint8_t thirdByte = index32 >> 16;
+                    uint8_t fourthByte = index32 >> 24;
+
+                    compiledIndexBuffer.push_back(firstByte);
+                    compiledIndexBuffer.push_back(secondByte);
+                    compiledIndexBuffer.push_back(thirdByte);
+                    compiledIndexBuffer.push_back(fourthByte);
+                }
+			}
+            else if(vertexGroup.indexBuffer.type == vkcv::asset::IndexType::UINT16)
+            {
+                for (size_t i = 0; i < vertexGroup.indexBuffer.data.size(); i += 2)
+                {
+                    uint16_t index16 = *reinterpret_cast<const uint16_t*>(&vertexGroup.indexBuffer.data[i]);
+                    uint32_t index32 = static_cast<uint32_t>(index16);
+
+                    uint8_t firstByte = index32;
+                    uint8_t secondByte = index32 >> 8;
+                    uint8_t thirdByte = index32 >> 16;
+                    uint8_t fourthByte = index32 >> 24;
+
+                    compiledIndexBuffer.push_back(firstByte);
+                    compiledIndexBuffer.push_back(secondByte);
+                    compiledIndexBuffer.push_back(thirdByte);
+                    compiledIndexBuffer.push_back(fourthByte);
+                }
+			}
+			else
+			{
+				compiledIndexBuffer.insert(compiledIndexBuffer.end(),
+										   vertexGroup.indexBuffer.data.begin(),
+										   vertexGroup.indexBuffer.data.end());
+			}
+        }
+    }
+}
+
+int main(int argc, const char** argv) {
+	const char* applicationName = "Indirect draw";
+
+	uint32_t windowWidth = 800;
+	uint32_t windowHeight = 600;
+
+	vkcv::Features features;
+	features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+    features.requireFeature([](vk::PhysicalDeviceFeatures &features){
+        features.setMultiDrawIndirect(true);
+    });
+
+	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);
+                features.setShaderUniformTexelBufferArrayDynamicIndexing(true);
+                features.setShaderStorageTexelBufferArrayDynamicIndexing(true);
+                features.setShaderUniformBufferArrayNonUniformIndexing(true);
+                features.setShaderSampledImageArrayNonUniformIndexing(true);
+                features.setShaderStorageBufferArrayNonUniformIndexing(true);
+                features.setShaderStorageImageArrayNonUniformIndexing(true);
+                // features.setShaderInputAttachmentArrayNonUniformIndexing(true);
+                features.setShaderUniformTexelBufferArrayNonUniformIndexing(true);
+                features.setShaderStorageTexelBufferArrayNonUniformIndexing(true);
+
+                features.setDescriptorBindingUniformBufferUpdateAfterBind(true);
+                features.setDescriptorBindingSampledImageUpdateAfterBind(true);
+                features.setDescriptorBindingStorageImageUpdateAfterBind(true);
+                features.setDescriptorBindingStorageBufferUpdateAfterBind(true);
+                features.setDescriptorBindingUniformTexelBufferUpdateAfterBind(true);
+                features.setDescriptorBindingStorageTexelBufferUpdateAfterBind(true);
+
+                features.setDescriptorBindingUpdateUnusedWhilePending(true);
+                features.setDescriptorBindingPartiallyBound(true);
+                features.setDescriptorBindingVariableDescriptorCount(true);
+                features.setRuntimeDescriptorArray(true);
+            }
+    );
+
+
+	vkcv::Core core = vkcv::Core::create(
+		applicationName,
+		VK_MAKE_VERSION(0, 0, 1),
+		{ vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer },
+		features
+	);
+
+	vkcv::WindowHandle windowHandle = core.createWindow(applicationName,windowWidth,windowHeight,false);
+
+    vkcv::gui::GUI gui (core, windowHandle);
+
+    vkcv::asset::Scene asset_scene;
+	const char* path = argc > 1 ? argv[1] : "resources/Sponza/Sponza.gltf";
+	int result = vkcv::asset::loadScene(path, asset_scene);
+
+	if (result == 1) {
+		std::cout << "Loading Sponza successful!" << std::endl;
+	} else {
+		std::cerr << "Loading Sponza failed: " << result << std::endl;
+		return 1;
+	}
+	assert(!asset_scene.vertexGroups.empty());
+    if (asset_scene.textures.empty())
+    {
+        std::cerr << "Error. No textures found. Exiting." << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    const vkcv::AttachmentDescription present_color_attachment(
+		vkcv::AttachmentOperation::STORE,
+		vkcv::AttachmentOperation::CLEAR,
+		core.getSwapchain(windowHandle).getFormat()
+	);
+	const vkcv::AttachmentDescription depth_attachment(
+			vkcv::AttachmentOperation::STORE,
+			vkcv::AttachmentOperation::CLEAR,
+			vk::Format::eD32Sfloat
+	);
+
+	vkcv::PassConfig passDefinition({ present_color_attachment, depth_attachment });
+	vkcv::PassHandle passHandle = core.createPass(passDefinition);
+	if (!passHandle) {
+		std::cerr << "Error. Could not create renderpass. Exiting." << std::endl;
+		return EXIT_FAILURE;
+	}
+
+
+	vkcv::ShaderProgram sponzaProgram;
+	vkcv::shader::GLSLCompiler compiler;
+	compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/shader.vert"),
+					 [&sponzaProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+        sponzaProgram.addShader(shaderStage, path);
+	});
+	compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/shader.frag"),
+					 [&sponzaProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+        sponzaProgram.addShader(shaderStage, path);
+	});
+
+    vkcv::ShaderProgram cullingProgram;
+    compiler.compile(vkcv::ShaderStage::COMPUTE, std::filesystem::path("resources/shaders/culling.comp"),
+                     [&cullingProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+        cullingProgram.addShader(shaderStage, path);
+    });
+
+    // vertex layout for the pipeline. (assumed to be) used by all sponza meshes.
+    const std::vector<vkcv::VertexAttachment> vertexAttachments = sponzaProgram.getVertexAttachments();
+	const vkcv::VertexLayout sponzaVertexLayout({ vkcv::VertexBinding(0, { vertexAttachments }) });
+
+    std::vector<uint8_t> compiledVertexBuffer; // IGNORED, since the vertex buffer is not interleaved!
+
+    std::vector<uint8_t> compiledIndexBuffer;
+    CompiledMaterial compiledMaterial;
+    std::vector<vk::DrawIndexedIndirectCommand> indexedIndirectCommands;
+
+    compileMeshForIndirectDraw(core,
+                               asset_scene,
+                               compiledVertexBuffer,
+                               compiledIndexBuffer,
+                               compiledMaterial,
+                               indexedIndirectCommands);
+
+	std::vector<std::vector<Vertex>> interleavedVertices;
+    std::vector<glm::vec4> compiledBoundingBoxBuffer;
+    interleaveScene(asset_scene,
+                    interleavedVertices,
+                    compiledBoundingBoxBuffer);
+
+	std::vector<Vertex> compiledInterleavedVertexBuffer;
+	for(auto& vertexGroup : interleavedVertices )
+	{
+        compiledInterleavedVertexBuffer.insert(compiledInterleavedVertexBuffer.end(),vertexGroup.begin(),vertexGroup.end());
+	}
+
+	auto vkCompiledVertexBuffer = core.createBuffer<Vertex>(
+			vkcv::BufferType::VERTEX,
+            compiledInterleavedVertexBuffer.size(),
+			vkcv::BufferMemoryType::DEVICE_LOCAL
+			);
+    vkCompiledVertexBuffer.fill(compiledInterleavedVertexBuffer.data());
+
+    auto vkCompiledIndexBuffer = core.createBuffer<uint8_t>(
+            vkcv::BufferType::INDEX,
+            compiledIndexBuffer.size(),
+            vkcv::BufferMemoryType::DEVICE_LOCAL);
+    vkCompiledIndexBuffer.fill(compiledIndexBuffer.data());
+
+    vkcv::Buffer<vk::DrawIndexedIndirectCommand> indirectBuffer = core.createBuffer<vk::DrawIndexedIndirectCommand>(
+            vkcv::BufferType::INDIRECT,
+            indexedIndirectCommands.size(),
+            vkcv::BufferMemoryType::DEVICE_LOCAL);
+    indirectBuffer.fill(indexedIndirectCommands);
+
+    auto boundingBoxBuffer = core.createBuffer<glm::vec4>(
+            vkcv::BufferType::STORAGE,
+            compiledBoundingBoxBuffer.size());
+    boundingBoxBuffer.fill(compiledBoundingBoxBuffer);
+
+    std::vector<glm::mat4> modelMatrix;
+	for( auto& mesh : asset_scene.meshes)
+	{
+		modelMatrix.push_back(glm::make_mat4(mesh.modelMatrix.data()));
+	}
+	vkcv::Buffer<glm::mat4> modelBuffer = core.createBuffer<glm::mat4>(
+			vkcv::BufferType::STORAGE,
+			modelMatrix.size(),
+			vkcv::BufferMemoryType::DEVICE_LOCAL
+			);
+	modelBuffer.fill(modelMatrix);
+
+	const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
+			vkcv::VertexBufferBinding(static_cast<vk::DeviceSize> (0), vkCompiledVertexBuffer.getVulkanHandle() )
+	};
+
+	const vkcv::Mesh compiledMesh(vertexBufferBindings, vkCompiledIndexBuffer.getVulkanHandle(), 0, vkcv::IndexBitCount::Bit32);
+
+    //assert(compiledMaterial.baseColor.size() == compiledMaterial.metalRough.size());
+
+	vkcv::DescriptorBindings descriptorBindings = sponzaProgram.getReflectedDescriptors().at(0);
+    descriptorBindings[2].descriptorCount = compiledMaterial.baseColor.size();
+
+	vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout(descriptorBindings);
+	vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout);
+
+    vkcv::SamplerHandle standardSampler = core.createSampler(
+            vkcv::SamplerFilterType::LINEAR,
+            vkcv::SamplerFilterType::LINEAR,
+            vkcv::SamplerMipmapMode::LINEAR,
+            vkcv::SamplerAddressMode::REPEAT
+    );
+
+    std::vector<vkcv::SampledImageDescriptorWrite> textureArrayWrites;
+    for(uint32_t i = 0; i < compiledMaterial.baseColor.size(); i++)
+    {
+        vkcv::SampledImageDescriptorWrite baseColorWrite(2, compiledMaterial.baseColor[i].getHandle(), 0, false, i);
+        textureArrayWrites.push_back(baseColorWrite);
+    }
+
+    vkcv::DescriptorWrites setWrites;
+    setWrites.sampledImageWrites	= textureArrayWrites;
+    setWrites.samplerWrites			= { vkcv::SamplerDescriptorWrite(0, standardSampler) };
+	setWrites.storageBufferWrites   = { vkcv::BufferDescriptorWrite(1, modelBuffer.getHandle())};
+    core.writeDescriptorSet(descriptorSet, setWrites);
+
+	const vkcv::GraphicsPipelineConfig sponzaPipelineConfig {
+        sponzaProgram,
+        UINT32_MAX,
+        UINT32_MAX,
+        passHandle,
+        {sponzaVertexLayout},
+		{ core.getDescriptorSetLayout(descriptorSetLayout).vulkanHandle },
+		true
+	};
+	vkcv::GraphicsPipelineHandle sponzaPipelineHandle = core.createGraphicsPipeline(sponzaPipelineConfig);
+	if (!sponzaPipelineHandle) {
+		std::cerr << "Error. Could not create graphics pipeline. Exiting." << std::endl;
+		return EXIT_FAILURE;
+	}
+
+    vkcv::DescriptorBindings cullingBindings = cullingProgram.getReflectedDescriptors().at(0);
+    vkcv::DescriptorSetLayoutHandle cullingSetLayout = core.createDescriptorSetLayout(cullingBindings);
+    vkcv::DescriptorSetHandle cullingDescSet = core.createDescriptorSet(cullingSetLayout);
+
+    vkcv::Buffer<CameraPlanes> cameraPlaneBuffer = core.createBuffer<CameraPlanes>(
+            vkcv::BufferType::UNIFORM,
+            1);
+
+    //Plane dummyPlane{};
+    //dummyPlane.pointOnPlane = glm::vec3(0.0f);
+    //dummyPlane.padding0 = 0.0f;
+    //dummyPlane.normal = glm::vec3(0.0f);
+    //dummyPlane.padding1 = 0.0f;
+
+    //CameraPlanes dummyCameraPlane{};
+    //dummyCameraPlane.planes[0] = dummyPlane;
+    //dummyCameraPlane.planes[1] = dummyPlane;
+    //dummyCameraPlane.planes[2] = dummyPlane;
+    //dummyCameraPlane.planes[3] = dummyPlane;
+    //dummyCameraPlane.planes[4] = dummyPlane;
+    //dummyCameraPlane.planes[5] = dummyPlane;
+
+    //cameraPlaneBuffer.fill(&dummyCameraPlane);
+
+    vkcv::BufferDescriptorWrite cameraPlaneWrite(0, cameraPlaneBuffer.getHandle());
+    vkcv::BufferDescriptorWrite drawCommandsWrite(1, indirectBuffer.getHandle());
+    vkcv::BufferDescriptorWrite boundingBoxWrite(2, boundingBoxBuffer.getHandle());
+
+    vkcv::DescriptorWrites cullingWrites;
+    cullingWrites.storageBufferWrites = {drawCommandsWrite, boundingBoxWrite};
+    cullingWrites.uniformBufferWrites = {cameraPlaneWrite};
+    core.writeDescriptorSet(cullingDescSet, cullingWrites);
+
+
+    const vkcv::ComputePipelineConfig computeCullingConfig {
+        cullingProgram,
+        {core.getDescriptorSetLayout(cullingSetLayout).vulkanHandle}
+    };
+    vkcv::ComputePipelineHandle cullingPipelineHandle = core.createComputePipeline(computeCullingConfig);
+    if (!cullingPipelineHandle) {
+        std::cerr << "Error. Could not create culling pipeline. Exiting." << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    vkcv::camera::CameraManager cameraManager(core.getWindow(windowHandle));
+    uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
+	
+	cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -3));
+	cameraManager.getCamera(camIndex0).setNearFar(0.1f, 20.f);
+
+    vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight, 1, false).getHandle();
+    const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
+
+    auto start = std::chrono::system_clock::now();
+
+    float ceiledDispatchCount = static_cast<float>(indexedIndirectCommands.size()) / 64.0f;
+    ceiledDispatchCount = std::ceil(ceiledDispatchCount);
+    const uint32_t dispatchCount[3] = {static_cast<uint32_t>(ceiledDispatchCount), 1, 1};
+
+
+    vkcv::DescriptorSetUsage cullingUsage(0, core.getDescriptorSet(cullingDescSet).vulkanHandle, {});
+    vkcv::PushConstants emptyPushConstant(0);
+
+    bool updateFrustumPlanes    = true;
+
+    while (vkcv::Window::hasOpenWindow()) {
+        vkcv::Window::pollEvents();
+		
+		if(core.getWindow(windowHandle).getHeight() == 0 || core.getWindow(windowHandle).getWidth() == 0)
+			continue;
+		
+		uint32_t swapchainWidth, swapchainHeight;
+		if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) {
+			continue;
+		}
+		
+		if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) {
+			depthBuffer = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight).getHandle();
+			
+			windowWidth = swapchainWidth;
+			windowHeight = swapchainHeight;
+		}
+  
+		auto end = std::chrono::system_clock::now();
+		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
+		
+		start = end;
+		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
+        vkcv::camera::Camera cam = cameraManager.getActiveCamera();
+		vkcv::PushConstants pushConstants(sizeof(glm::mat4));
+		pushConstants.appendDrawcall(cam.getProjection() * cam.getView());
+
+        if(updateFrustumPlanes)
+        {
+            const CameraPlanes cameraPlanes = computeCameraPlanes(cam);
+            cameraPlaneBuffer.fill({ cameraPlanes });
+        }
+
+		const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
+		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
+
+        core.recordComputeDispatchToCmdStream(cmdStream,
+                                              cullingPipelineHandle,
+                                              dispatchCount,
+                                              {cullingUsage},
+                                              emptyPushConstant);
+
+        core.recordBufferMemoryBarrier(cmdStream, indirectBuffer.getHandle());
+
+		core.recordIndexedIndirectDrawcallsToCmdStream(
+			cmdStream,
+			passHandle,
+            sponzaPipelineHandle,
+            pushConstants,
+            descriptorSet,
+            compiledMesh,
+			renderTargets,
+			indirectBuffer,
+            indexedIndirectCommands.size(),
+			windowHandle);
+
+		core.prepareSwapchainImageForPresent(cmdStream);
+		core.submitCommandStream(cmdStream);
+
+		auto stop = std::chrono::system_clock::now();
+		auto kektime = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
+        gui.beginGUI();
+
+        ImGui::Begin("Settings");
+        ImGui::Checkbox("Update frustum culling", &updateFrustumPlanes);
+		ImGui::Text("Deltatime %fms, %f", 0.001 * static_cast<double>(kektime.count()), 1/(0.000001 * static_cast<double>(kektime.count())));
+
+        ImGui::End();
+
+        gui.endGUI();
+
+		core.endFrame(windowHandle);
+	}
+	
+	return 0;
+}
diff --git a/projects/path_tracer/.gitignore b/projects/path_tracer/.gitignore
index ff3dff30031efafa24269a9ac0ef93f64f63ded1..24a57cda822232aa24d513fab3901ff7db36adb1 100644
--- a/projects/path_tracer/.gitignore
+++ b/projects/path_tracer/.gitignore
@@ -1 +1 @@
-saf_r
\ No newline at end of file
+path_tracer
\ No newline at end of file
diff --git a/projects/sph/.gitignore b/projects/sph/.gitignore
index 4964f89e973f38358aa57f564f56d3d4b0c328a9..3968dc30067c92efde65779bc5eebecf167dfc75 100644
--- a/projects/sph/.gitignore
+++ b/projects/sph/.gitignore
@@ -1 +1 @@
-particle_simulation
\ No newline at end of file
+sph
\ No newline at end of file
diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp
index 9bb9e67166013a8d1c2f7eb22a2ea867ddc9da32..52915ff04800ef3b6cb1e6caefdb3f25f6568aac 100644
--- a/src/vkcv/BufferManager.cpp
+++ b/src/vkcv/BufferManager.cpp
@@ -48,7 +48,10 @@ namespace vkcv {
 			case BufferType::INDEX:
 				usageFlags = vk::BufferUsageFlagBits::eIndexBuffer;
 				break;
-            default:
+            case BufferType::INDIRECT:
+                usageFlags = vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndirectBuffer ;
+                break;
+			default:
 				vkcv_log(LogLevel::WARNING, "Unknown buffer type");
 				break;
 		}
diff --git a/src/vkcv/Context.cpp b/src/vkcv/Context.cpp
index 60ee35629dd54c16f40762861667de5054defc12..2f3453f3ff97bd778df8fcfd36d79cc901c867ba 100644
--- a/src/vkcv/Context.cpp
+++ b/src/vkcv/Context.cpp
@@ -154,11 +154,10 @@ namespace vkcv
 	}
 	
 	/**
-	 * @brief With the help of the reference "supported" all elements in "check" checked,
-	 * if they are supported by the physical device.
-	 * @param supported The reference that can be used to check "check"
-	 * @param check The elements to be checked
-	 * @return True, if all elements in "check" are supported
+	 * @brief Check whether all string occurrences in "check" are contained in "supported"
+	 * @param supported The const vector const char* reference used to compare entries in "check"
+	 * @param check The const vector const char* reference elements to be checked by "supported"
+	 * @return True, if all elements in "check" are supported (contained in supported)
 	*/
 	bool checkSupport(const std::vector<const char*>& supported, const std::vector<const char*>& check)
 	{
@@ -185,7 +184,7 @@ namespace vkcv
 		
 		return extensions;
 	}
-	
+
 	Context Context::create(const char *applicationName,
 							uint32_t applicationVersion,
 							const std::vector<vk::QueueFlagBits>& queueFlags,
@@ -213,7 +212,7 @@ namespace vkcv
 		}
 #endif
 		
-		// check for extension support
+		// check for instance extension support
 		std::vector<vk::ExtensionProperties> instanceExtensionProperties = vk::enumerateInstanceExtensionProperties();
 		
 		std::vector<const char*> supportedExtensions;
@@ -260,7 +259,7 @@ namespace vkcv
 #endif
 		
 		vk::Instance instance = vk::createInstance(instanceCreateInfo);
-		
+
 		std::vector<vk::PhysicalDevice> physicalDevices = instance.enumeratePhysicalDevices();
 		vk::PhysicalDevice physicalDevice;
 		
@@ -273,6 +272,21 @@ namespace vkcv
 #ifdef __APPLE__
 		featureManager.useExtension("VK_KHR_portability_subset", true);
 #endif
+
+		if (featureManager.useExtension(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false)) {
+			featureManager.useFeatures<vk::PhysicalDeviceShaderFloat16Int8Features>(
+					[](vk::PhysicalDeviceShaderFloat16Int8Features& features) {
+				features.setShaderFloat16(true);
+			}, false);
+		}
+		
+		if (featureManager.useExtension(VK_KHR_16BIT_STORAGE_EXTENSION_NAME, false)) {
+			featureManager.useFeatures<vk::PhysicalDevice16BitStorageFeatures>(
+					[](vk::PhysicalDevice16BitStorageFeatures& features) {
+				features.setStorageBuffer16BitAccess(true);
+			}, false);
+		}
+
 		featureManager.useFeatures([](vk::PhysicalDeviceFeatures& features) {
 			features.setFragmentStoresAndAtomics(true);
 			features.setGeometryShader(true);
@@ -285,10 +299,8 @@ namespace vkcv
 		}
 		
 		const auto& extensions = featureManager.getActiveExtensions();
-		
+
 		std::vector<vk::DeviceQueueCreateInfo> qCreateInfos;
-		
-		// create required queues
 		std::vector<float> qPriorities;
 		qPriorities.resize(queueFlags.size(), 1.f);
 		std::vector<std::pair<int, int>> queuePairsGraphics, queuePairsCompute, queuePairsTransfer;
@@ -310,7 +322,7 @@ namespace vkcv
 		deviceCreateInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
 		deviceCreateInfo.ppEnabledLayerNames = validationLayers.data();
 #endif
-		
+
 		deviceCreateInfo.setPNext(&(featureManager.getFeatures()));
 		
 		vk::Device device = physicalDevice.createDevice(deviceCreateInfo);
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index efba11ec2a95d8b08c9c9ddc646ec471f4f7adce..99d27496ba134c75dc555dca18fc26410005ed23 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -330,6 +330,103 @@ namespace vkcv
 		recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction);
 	}
 
+    void Core::recordIndexedIndirectDrawcallsToCmdStream(
+            const CommandStreamHandle                           cmdStreamHandle,
+            const PassHandle                                    renderpassHandle,
+            const GraphicsPipelineHandle                        &pipelineHandle,
+            const PushConstants                                 &pushConstantData,
+            const vkcv::DescriptorSetHandle                     &compiledDescriptorSet,
+            const vkcv::Mesh                                    &compiledMesh,
+            const std::vector<ImageHandle>                      &renderTargets,
+            const vkcv::Buffer<vk::DrawIndexedIndirectCommand>  &indirectBuffer,
+            const uint32_t                                      drawCount,
+			const WindowHandle                                  &windowHandle) {
+
+        if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
+            return;
+        }
+		SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
+        const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, m_SwapchainManager->getSwapchain(swapchainHandle),
+                                                                                    *m_ImageManager);
+        const auto width = widthHeight[0];
+        const auto height = widthHeight[1];
+
+        const vk::RenderPass        renderpass      = m_PassManager->getVkPass(renderpassHandle);
+        const PassConfig            passConfig      = m_PassManager->getPassConfig(renderpassHandle);
+
+        const vk::Pipeline          pipeline        = m_PipelineManager->getVkPipeline(pipelineHandle);
+        const vk::PipelineLayout    pipelineLayout  = m_PipelineManager->getVkPipelineLayout(pipelineHandle);
+        const vk::Rect2D            renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height));
+
+        vk::CommandBuffer cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle);
+        transitionRendertargetsToAttachmentLayout(renderTargets, *m_ImageManager, cmdBuffer);
+
+        const vk::Framebuffer framebuffer = createFramebuffer(renderTargets, *m_ImageManager, m_SwapchainManager->getSwapchain(swapchainHandle), renderpass,
+                                                              m_Context.m_Device);
+
+        if (!framebuffer) {
+            vkcv_log(LogLevel::ERROR, "Failed to create temporary framebuffer");
+            return;
+        }
+
+        SubmitInfo submitInfo;
+        submitInfo.queueType = QueueType::Graphics;
+        submitInfo.signalSemaphores = {m_SyncResources.renderFinished};
+
+        auto submitFunction = [&](const vk::CommandBuffer &cmdBuffer) {
+
+            const std::vector<vk::ClearValue> clearValues = createAttachmentClearValues(passConfig.attachments);
+
+            const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(),
+                                                    clearValues.data());
+            cmdBuffer.beginRenderPass(beginInfo, {}, {});
+
+            cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {});
+
+            const GraphicsPipelineConfig &pipeConfig = m_PipelineManager->getPipelineConfig(pipelineHandle);
+            if (pipeConfig.m_UseDynamicViewport) {
+                recordDynamicViewport(cmdBuffer, width, height);
+            }
+
+			if (pushConstantData.getSizePerDrawcall() > 0)
+			{
+				cmdBuffer.pushConstants(
+					pipelineLayout,
+					vk::ShaderStageFlagBits::eAll,
+					0,
+					pushConstantData.getSizePerDrawcall(),
+					pushConstantData.getDrawcallData(0));
+			}
+
+            vkcv::DescriptorSet descSet = m_DescriptorManager->getDescriptorSet(compiledDescriptorSet);
+
+            cmdBuffer.bindDescriptorSets(
+                    vk::PipelineBindPoint::eGraphics,
+                    pipelineLayout,
+                    0,
+                    descSet.vulkanHandle,
+                    nullptr);
+
+			vk::DeviceSize deviceSize = 0;
+			cmdBuffer.bindVertexBuffers(0, 1, &compiledMesh.vertexBufferBindings[0].buffer,&deviceSize);
+            cmdBuffer.bindIndexBuffer(compiledMesh.indexBuffer, 0, getIndexType(compiledMesh.indexBitCount));
+
+            cmdBuffer.drawIndexedIndirect(
+                    indirectBuffer.getVulkanHandle(),
+                    0,
+                    drawCount,
+                    sizeof(vk::DrawIndexedIndirectCommand));
+
+            cmdBuffer.endRenderPass();
+        };
+
+        auto finishFunction = [framebuffer, this]() {
+            m_Context.m_Device.destroy(framebuffer);
+        };
+
+        recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction);
+    }
+
 	void Core::recordMeshShaderDrawcalls(
 		const CommandStreamHandle&                          cmdStreamHandle,
 		const PassHandle&                                   renderpassHandle,
diff --git a/src/vkcv/DescriptorConfig.cpp b/src/vkcv/DescriptorConfig.cpp
index 91a9c24dc1819b69223b6befa9b6d64409698c35..15bb05fd9cb3e5430b2a9909a682c468dfcde342 100644
--- a/src/vkcv/DescriptorConfig.cpp
+++ b/src/vkcv/DescriptorConfig.cpp
@@ -2,22 +2,12 @@
 
 namespace vkcv
 {
-	DescriptorBinding::DescriptorBinding(
-		uint32_t bindingID,
-		DescriptorType descriptorType,
-		uint32_t descriptorCount,
-		ShaderStages shaderStages) noexcept:
-		bindingID(bindingID),
-		descriptorType(descriptorType),
-		descriptorCount(descriptorCount),
-		shaderStages(shaderStages)
-		{}
-
     bool DescriptorBinding::operator==(const DescriptorBinding &other) const
     {
 	    return (this->bindingID == other.bindingID) &&
 	           (this->descriptorType == other.descriptorType) &&
 	           (this->descriptorCount == other.descriptorCount) &&
-	           (this->shaderStages == other.shaderStages);
+	           (this->shaderStages == other.shaderStages) &&
+	           (this->variableCount == other.variableCount);
     }
 }
diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp
index 7480289b1f2a0b1db194db3a06c52f7050888afe..f273e958b12892d6f2c9cc92a4ca07de3f6b29c7 100644
--- a/src/vkcv/DescriptorManager.cpp
+++ b/src/vkcv/DescriptorManager.cpp
@@ -61,27 +61,42 @@ namespace vkcv
             }
         }
         
-        //create the descriptor set's layout by iterating over its bindings
+        //create the descriptor set's layout and binding flags by iterating over its bindings
         std::vector<vk::DescriptorSetLayoutBinding> bindingsVector = {};
+		std::vector<vk::DescriptorBindingFlags> bindingsFlags = {};
+		
         for (auto bindingElem : setBindingsMap)
         {
             DescriptorBinding binding = bindingElem.second;
             uint32_t bindingID = bindingElem.first;
-
-            vk::DescriptorSetLayoutBinding descriptorSetLayoutBinding(
-                    bindingID,
-                    getVkDescriptorType(binding.descriptorType),
-                    binding.descriptorCount,
-                    getShaderStageFlags(binding.shaderStages),
-                    nullptr
-                    );
-
-            bindingsVector.push_back(descriptorSetLayoutBinding);
+	
+			bindingsVector.emplace_back(
+					bindingID,
+					getVkDescriptorType(binding.descriptorType),
+					binding.descriptorCount,
+					getShaderStageFlags(binding.shaderStages),
+					nullptr
+			);
+			
+			if (binding.variableCount) {
+				bindingsFlags.push_back(
+						vk::DescriptorBindingFlagBits::eVariableDescriptorCount |
+						vk::DescriptorBindingFlagBits::ePartiallyBound
+				);
+			} else {
+				bindingsFlags.emplace_back();
+			}
         }
+		
+        vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsInfo (
+				bindingsFlags.size(), bindingsFlags.data()
+		);
 
         //create the descriptor set's layout from the binding data gathered above
         vk::DescriptorSetLayout vulkanHandle;
-        vk::DescriptorSetLayoutCreateInfo layoutInfo({}, bindingsVector);
+        vk::DescriptorSetLayoutCreateInfo layoutInfo(vk::DescriptorSetLayoutCreateFlags(), bindingsVector);
+		layoutInfo.setPNext(&bindingFlagsInfo);
+		
         auto result = m_Device.createDescriptorSetLayout(&layoutInfo, nullptr, &vulkanHandle);
         if (result != vk::Result::eSuccess) {
             vkcv_log(LogLevel::ERROR, "Failed to create descriptor set layout");
@@ -99,6 +114,23 @@ namespace vkcv
         DescriptorSetLayout setLayout = m_DescriptorSetLayouts[setLayoutHandle.getId()];
         vk::DescriptorSet vulkanHandle;
         vk::DescriptorSetAllocateInfo allocInfo(m_Pools.back(), 1, &setLayout.vulkanHandle);
+
+        uint32_t sumVariableDescriptorCounts = 0;
+        for (auto bindingElem : setLayout.descriptorBindings)
+        {
+            DescriptorBinding binding = bindingElem.second;
+            uint32_t bindingID = bindingElem.first;
+
+            if(binding.variableCount)
+                sumVariableDescriptorCounts += binding.descriptorCount;
+        }
+
+        vk::DescriptorSetVariableDescriptorCountAllocateInfo variableAllocInfo(1, &sumVariableDescriptorCounts);
+
+        if (sumVariableDescriptorCounts > 0) {
+            allocInfo.setPNext(&variableAllocInfo);
+        }
+
         auto result = m_Device.allocateDescriptorSets(&allocInfo, &vulkanHandle);
         if(result != vk::Result::eSuccess)
         {
@@ -126,6 +158,7 @@ namespace vkcv
 		size_t imageInfoIndex;
 		size_t bufferInfoIndex;
 		uint32_t binding;
+		uint32_t arrayElementIndex;
 		vk::DescriptorType type;
     };
 
@@ -142,7 +175,8 @@ namespace vkcv
 		
 		std::vector<WriteDescriptorSetInfo> writeInfos;
 
-		for (const auto& write : writes.sampledImageWrites) {
+		for (const auto& write : writes.sampledImageWrites)
+		{
 		    vk::ImageLayout layout = write.useGeneralLayout ? vk::ImageLayout::eGeneral : vk::ImageLayout::eShaderReadOnlyOptimal;
 			const vk::DescriptorImageInfo imageInfo(
 				nullptr,
@@ -156,6 +190,7 @@ namespace vkcv
 					imageInfos.size(),
 					0,
 					write.binding,
+					write.arrayIndex,
 					vk::DescriptorType::eSampledImage,
 			};
 			
@@ -175,6 +210,7 @@ namespace vkcv
 					imageInfos.size(),
 					0,
 					write.binding,
+					0,
 					vk::DescriptorType::eStorageImage
 			};
 			
@@ -199,6 +235,7 @@ namespace vkcv
 					0,
 					bufferInfos.size(),
 					write.binding,
+					0,
 					write.dynamic?
 					vk::DescriptorType::eUniformBufferDynamic :
 					vk::DescriptorType::eUniformBuffer
@@ -225,6 +262,7 @@ namespace vkcv
 					0,
 					bufferInfos.size(),
 					write.binding,
+					0,
 					write.dynamic?
 					vk::DescriptorType::eStorageBufferDynamic :
 					vk::DescriptorType::eStorageBuffer
@@ -248,6 +286,7 @@ namespace vkcv
 					imageInfos.size(),
 					0,
 					write.binding,
+					0,
 					vk::DescriptorType::eSampler
 			};
 			
@@ -260,7 +299,7 @@ namespace vkcv
 			vk::WriteDescriptorSet vulkanWrite(
 					set,
 					write.binding,
-					static_cast<uint32_t>(0),
+					write.arrayElementIndex,
 					1,
 					write.type,
 					(write.imageInfoIndex > 0? &(imageInfos[write.imageInfoIndex - 1]) : nullptr),
@@ -289,6 +328,7 @@ namespace vkcv
 		}
 		
 		auto& set = m_DescriptorSets[id];
+		
 		if (set.vulkanHandle) {
 			m_Device.freeDescriptorSets(m_Pools[set.poolIndex], 1, &(set.vulkanHandle));
 			set.setLayoutHandle = DescriptorSetLayoutHandle();
@@ -303,6 +343,7 @@ namespace vkcv
 	    }
 
 	    auto& layout = m_DescriptorSetLayouts[id];
+		
 	    if (layout.vulkanHandle){
 	        m_Device.destroy(layout.vulkanHandle);
 	        layout.vulkanHandle = nullptr;
@@ -321,4 +362,4 @@ namespace vkcv
 		return pool;
 	}
 
-}
\ No newline at end of file
+}
diff --git a/src/vkcv/DrawcallRecording.cpp b/src/vkcv/DrawcallRecording.cpp
index ca8b248a06d06c7aed6f8d0e9760645b727a5993..638df388178f79f680c3b9f797d9d435e752fe10 100644
--- a/src/vkcv/DrawcallRecording.cpp
+++ b/src/vkcv/DrawcallRecording.cpp
@@ -52,6 +52,17 @@ namespace vkcv {
         }
     }
 
+    void recordIndirectDrawcall(
+            const DrawcallInfo                              &drawcall,
+            vk::CommandBuffer                               cmdBuffer,
+            const Buffer <vk::DrawIndexedIndirectCommand>   &drawBuffer,
+            const uint32_t                                  drawCount,
+            vk::PipelineLayout                              pipelineLayout,
+            const PushConstants                             &pushConstants,
+            const size_t                                    drawcallIndex) {
+        return;
+    }
+
     struct MeshShaderFunctions
     {
         PFN_vkCmdDrawMeshTasksNV cmdDrawMeshTasks                           = nullptr;
diff --git a/src/vkcv/FeatureManager.cpp b/src/vkcv/FeatureManager.cpp
index 2dd5585d0f3b01c9741e8e52da6a7b45b24d37e5..c25745fcdd007df86316e0f72bc3d654377baa70 100644
--- a/src/vkcv/FeatureManager.cpp
+++ b/src/vkcv/FeatureManager.cpp
@@ -87,6 +87,7 @@ m_physicalDevice.getFeatures2(&query)
 		vkcv_check_feature(variableMultisampleRate);
 		vkcv_check_feature(vertexPipelineStoresAndAtomics);
 		vkcv_check_feature(wideLines);
+        vkcv_check_feature(multiDrawIndirect);
 		
 		return true;
 	}
@@ -291,7 +292,7 @@ m_physicalDevice.getFeatures2(&query)
 		
 		return true;
 	}
-
+	
 	bool FeatureManager::checkSupport(const vk::PhysicalDeviceVulkan12Features &features, bool required) const {
 	    vkcv_check_init_features2(vk::PhysicalDeviceVulkan12Features);
 
@@ -534,5 +535,4 @@ m_physicalDevice.getFeatures2(&query)
 	const vk::PhysicalDeviceFeatures2& FeatureManager::getFeatures() const {
 		return m_featuresBase;
 	}
-	
 }
diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp
index 4d2165aa22eb5f53dcc3067273721543edd309e4..504d8fcdbd84935db339370b15c31ccefe26e02b 100644
--- a/src/vkcv/ShaderProgram.cpp
+++ b/src/vkcv/ShaderProgram.cpp
@@ -146,14 +146,26 @@ namespace vkcv {
         {
             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);
-            auto binding = DescriptorBinding(
-                    bindingID,
-                    DescriptorType::UNIFORM_BUFFER,
-                    base_type.vecsize,
-                    shaderStage);
+
+            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};
 
             auto insertionResult = m_DescriptorSets[setID].insert(std::make_pair(bindingID, binding));
             if(!insertionResult.second)
@@ -169,14 +181,27 @@ namespace vkcv {
         {
             auto& u = resources.storage_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);
-            auto binding = DescriptorBinding(
-                    bindingID,
-                    DescriptorType::STORAGE_BUFFER,
-                    base_type.vecsize,
-                    shaderStage);
+
+            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};
+
 
             auto insertionResult = m_DescriptorSets[setID].insert(std::make_pair(bindingID, binding));
             if(!insertionResult.second)
@@ -191,14 +216,26 @@ namespace vkcv {
         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);
-            auto binding = DescriptorBinding(
-                    bindingID,
-                    DescriptorType::SAMPLER,
-                    base_type.vecsize,
-                    shaderStage);
+
+            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::SAMPLER,
+                                       descriptorCount,
+                                       shaderStage,
+                                       variableCount};
 
             auto insertionResult = m_DescriptorSets[setID].insert(std::make_pair(bindingID, binding));
             if(!insertionResult.second)
@@ -212,15 +249,27 @@ namespace vkcv {
 
         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 &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);
-            auto binding = DescriptorBinding(
-                    bindingID,
-                    DescriptorType::IMAGE_SAMPLED,
-                    base_type.vecsize,
-                    shaderStage);
+
+            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,
+                                       variableCount};
 
             auto insertionResult = m_DescriptorSets[setID].insert(std::make_pair(bindingID, binding));
             if(!insertionResult.second)
@@ -235,14 +284,26 @@ namespace vkcv {
         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);
 
             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::IMAGE_STORAGE,
-                    base_type.vecsize,
-                    shaderStage);
+
+            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_STORAGE,
+                                       descriptorCount,
+                                       shaderStage,
+                                       variableCount};
 
             auto insertionResult = m_DescriptorSets[setID].insert(std::make_pair(bindingID, binding));
             if(!insertionResult.second)
@@ -261,11 +322,13 @@ namespace vkcv {
 
             uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet);
             uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding);
-            auto binding = DescriptorBinding(
+            auto binding = DescriptorBinding {
                 bindingID,
                 DescriptorType::ACCELERATION_STRUCTURE_KHR,
                 base_type.vecsize,
-                shaderStage);
+                shaderStage,
+				false
+			};
 
             auto insertionResult = m_DescriptorSets[setID].insert(std::make_pair(bindingID, binding));
             if (!insertionResult.second)
diff --git a/src/vkcv/VertexLayout.cpp b/src/vkcv/VertexLayout.cpp
index fa079a3264ae47b32461bda26485adb97b0be280..0edced571502bbaf0e89ff6d45d338ca38b229ca 100644
--- a/src/vkcv/VertexLayout.cpp
+++ b/src/vkcv/VertexLayout.cpp
@@ -46,8 +46,8 @@ namespace vkcv {
         uint32_t offset = 0;
         for (auto &attachment : vertexAttachments)
         {
-            offset += getFormatSize(attachment.format);
             attachment.offset = offset;
+            offset += getFormatSize(attachment.format);
         }
         stride = offset;
     }