diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index e8ce984d7daa945c5436c51e896e797a0da22b84..a11db52fdc2c6a8bdc1668110539dffb9c23e6de 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -300,41 +300,54 @@ namespace vkcv
 		Swapchain& getSwapchain(const WindowHandle &handle);
 
 		/**
-		 * Returns the image width
-		 * @param image handle
+		 * @brief Returns the image width.
+		 *
+		 * @param image Image handle
 		 * @return imageWidth
 		 */
         [[nodiscard]]
         uint32_t getImageWidth(const ImageHandle &image);
 
         /**
-         * Returns the image height
-         * @param image handle
+         * @brief Returns the image height.
+         *
+         * @param[in] image Image handle
          * @return imageHeight
          */
         [[nodiscard]]
         uint32_t getImageHeight(const ImageHandle &image);
 	
 		/**
-         * Returns the image depth
-         * @param image handle
+         * @brief Returns the image depth.
+         *
+         * @param[in] image Image handle
          * @return imageDepth
          */
 		[[nodiscard]]
 		uint32_t getImageDepth(const ImageHandle &image);
 
         /**
-         * Returns the image format of the image
-         * @param image handle
+         * @brief Returns the image format of the image.
+         *
+         * @param[in] image Image handle
          * @return imageFormat
          */
 		[[nodiscard]]
 		vk::Format getImageFormat(const ImageHandle &image);
+	
+		/**
+		 * @brief Returns whether the image supports storage or not.
+		 *
+		 * @param[in] image Image handle
+		 * @return True, if the image supports storage, otherwise false.
+		 */
+		[[nodiscard]]
+		bool isImageSupportingStorage(const ImageHandle& image);
 		
 		/**
 		 * @brief Returns the images amount of mip levels.
 		 *
-		 * @param image Image handle
+		 * @param[in] image Image handle
 		 * @return Amount of mip levels
 		 */
 		[[nodiscard]]
@@ -343,7 +356,7 @@ namespace vkcv
 		/**
 		 * @brief Returns the images amount of array layers.
 		 *
-		 * @param image Image handle
+		 * @param[in] image Image handle
 		 * @return Amount of array layers
 		 */
 		[[nodiscard]]
@@ -352,7 +365,7 @@ namespace vkcv
 		/**
 		 * @brief Creates a descriptor set layout handle by a set of descriptor bindings.
 		 *
-		 * @param bindings Descriptor bindings
+		 * @param[in] bindings Descriptor bindings
 		 * @return Descriptor set layout handle
 		 */
 		[[nodiscard]]
@@ -361,7 +374,7 @@ namespace vkcv
 		/**
 		 * @brief Returns the descriptor set layout of a descriptor set layout handle.
 		 *
-		 * @param handle Descriptor set layout handle
+		 * @param[in] handle Descriptor set layout handle
 		 * @return Descriptor set layout
 		 */
 		DescriptorSetLayout getDescriptorSetLayout(const DescriptorSetLayoutHandle handle) const;
@@ -369,11 +382,11 @@ namespace vkcv
 		/**
 		 * @brief Creates a new descriptor set
 		 * 
-		 * @param layoutHandle Handle to the layout that the descriptor set will use
+		 * @param[in] layout Handle to the layout that the descriptor set will use
 		 * @return Handle that represents the descriptor set
 		 */
         [[nodiscard]]
-        DescriptorSetHandle createDescriptorSet(const DescriptorSetLayoutHandle &layoutHandle);
+        DescriptorSetHandle createDescriptorSet(const DescriptorSetLayoutHandle &layout);
 
 		/**
 		 * @brief Writes resources bindings to a descriptor set
diff --git a/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp b/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp
index fb078cc9cca080d77932422de9a86a4876de249b..ea6331b022437dcb7eb623bd268467e7deca1e8f 100644
--- a/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp
+++ b/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp
@@ -234,7 +234,7 @@ namespace vkcv::algorithm {
 		
 		m_core.prepareImageForSampling(cmdStream, image);
 		
-		if ((mipLevels < 4) || (depth > 1)) {
+		if ((mipLevels < 4) || (depth > 1) || (!m_core.isImageSupportingStorage(image))) {
 			m_core.getDownsampler().recordDownsampling(cmdStream, image);
 			return;
 		}
diff --git a/modules/material/src/vkcv/material/Material.cpp b/modules/material/src/vkcv/material/Material.cpp
index a6a16f80aab9f6baf610cd550f1cbd205795cf4e..8c3f3099cee731d24854d314fd7e3df4506a9aec 100644
--- a/modules/material/src/vkcv/material/Material.cpp
+++ b/modules/material/src/vkcv/material/Material.cpp
@@ -119,21 +119,21 @@ namespace vkcv::material {
 		}
 		
 		if (!normalImg) {
-			vkcv::Image defaultNormal = core.createImage(vk::Format::eR8G8B8A8Srgb, 2, 2);
+			vkcv::Image defaultNormal = core.createImage(vk::Format::eR8G8B8A8Unorm, 2, 2);
 			float normalData [4] = { 0, 0, 1, 0 };
 			fillImage(defaultNormal, normalData);
 			images[1] = defaultNormal.getHandle();
 		}
 		
 		if (!metRoughImg) {
-			vkcv::Image defaultRough = core.createImage(vk::Format::eR8G8B8A8Srgb, 2, 2);
+			vkcv::Image defaultRough = core.createImage(vk::Format::eR8G8B8A8Unorm, 2, 2);
 			float roughData [4] = { 228, 51, 255, 1 };
 			fillImage(defaultRough, roughData);
 			images[2] = defaultRough.getHandle();
 		}
 		
 		if (!occlusionImg) {
-			vkcv::Image defaultOcclusion = core.createImage(vk::Format::eR8G8B8A8Srgb, 2, 2);
+			vkcv::Image defaultOcclusion = core.createImage(vk::Format::eR8G8B8A8Unorm, 2, 2);
 			float occlusionData [4] = { 228, 51, 255, 1 };
 			fillImage(defaultOcclusion, occlusionData);
 			images[3] = defaultOcclusion.getHandle();
diff --git a/modules/scene/CMakeLists.txt b/modules/scene/CMakeLists.txt
index 08a7e59074728a6429949da2c39f2d9e169b6928..39effd0f802bb9d8ef4b318d5f1d233fa93db492 100644
--- a/modules/scene/CMakeLists.txt
+++ b/modules/scene/CMakeLists.txt
@@ -36,13 +36,24 @@ add_library(vkcv_scene ${vkcv_build_attribute} ${vkcv_scene_sources})
 target_link_libraries(vkcv_scene vkcv)
 
 # including headers of dependencies and the VkCV framework
-target_include_directories(vkcv_scene SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_material_include} ${vkcv_camera_include})
+target_include_directories(vkcv_scene SYSTEM BEFORE PRIVATE
+		${vkcv_include}
+		${vkcv_includes}
+		${vkcv_asset_loader_include}
+		${vkcv_material_include}
+		${vkcv_camera_include}
+		${vkcv_algorithm_include})
 
 # add the own include directory for public headers
 target_include_directories(vkcv_scene BEFORE PUBLIC ${vkcv_scene_include})
 
 # linking with libraries from all dependencies and the VkCV framework
-target_link_libraries(vkcv_scene vkcv vkcv_asset_loader vkcv_material vkcv_camera)
+target_link_libraries(vkcv_scene
+		vkcv
+		vkcv_asset_loader
+		vkcv_material
+		vkcv_camera
+		vkcv_algorithm)
 
 if (vkcv_parent_scope)
 	list(APPEND vkcv_modules_includes ${vkcv_scene_include})
diff --git a/modules/scene/src/vkcv/scene/Scene.cpp b/modules/scene/src/vkcv/scene/Scene.cpp
index bb21807d72d63ded8b2aa538414a4694babbfe05..cb8193708efcac7eb5e614353aa0b30101078833 100644
--- a/modules/scene/src/vkcv/scene/Scene.cpp
+++ b/modules/scene/src/vkcv/scene/Scene.cpp
@@ -4,6 +4,8 @@
 #include <vkcv/Logger.hpp>
 #include <vkcv/asset/asset_loader.hpp>
 
+#include <vkcv/algorithm/SinglePassDownsampler.hpp>
+
 namespace vkcv::scene {
 	
 	Scene::Scene(Core* core) :
@@ -274,10 +276,34 @@ namespace vkcv::scene {
 			scene.getNode(root).loadMesh(asset_scene, mesh);
 		}
 		
+		vkcv::SamplerHandle sampler = core.createSampler(
+				vkcv::SamplerFilterType::LINEAR,
+				vkcv::SamplerFilterType::LINEAR,
+				vkcv::SamplerMipmapMode::LINEAR,
+				vkcv::SamplerAddressMode::REPEAT
+		);
+		
+		const vkcv::FeatureManager& featureManager = core.getContext().getFeatureManager();
+		const bool partialBound = featureManager.checkFeatures<vk::PhysicalDeviceDescriptorIndexingFeatures>(
+				vk::StructureType::ePhysicalDeviceDescriptorIndexingFeatures,
+				[](const vk::PhysicalDeviceDescriptorIndexingFeatures& features) {
+					return features.descriptorBindingPartiallyBound;
+				}
+		);
+		
+		vkcv::Downsampler& downsampler = core.getDownsampler();
+		vkcv::algorithm::SinglePassDownsampler spdDownsampler (core, sampler);
+		
 		auto mipStream = core.createCommandStream(vkcv::QueueType::Graphics);
 		
-		for (auto& material : scene.m_materials) {
-			material.m_data.recordMipChainGeneration(mipStream, core.getDownsampler());
+		if (partialBound) {
+			for (auto& material : scene.m_materials) {
+				material.m_data.recordMipChainGeneration(mipStream, spdDownsampler);
+			}
+		} else {
+			for (auto& material : scene.m_materials) {
+				material.m_data.recordMipChainGeneration(mipStream, downsampler);
+			}
 		}
 		
 		core.submitCommandStream(mipStream, false);
diff --git a/projects/first_scene/src/main.cpp b/projects/first_scene/src/main.cpp
index 3c4f51bb119f585e449aa48de2aef086d0789dd7..b09943e4b9432ac09c6eaaa5bbe06b0b28b59ab3 100644
--- a/projects/first_scene/src/main.cpp
+++ b/projects/first_scene/src/main.cpp
@@ -7,18 +7,28 @@
 #include <vkcv/asset/asset_loader.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
 #include <vkcv/scene/Scene.hpp>
+#include <vkcv/Features.hpp>
 
 int main(int argc, const char** argv) {
 	const char* applicationName = "First Scene";
 
 	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.setDescriptorBindingPartiallyBound(true);
+			}
+	);
 
 	vkcv::Core core = vkcv::Core::create(
 			applicationName,
 			VK_MAKE_VERSION(0, 0, 1),
 			{vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute},
-			{ VK_KHR_SWAPCHAIN_EXTENSION_NAME }
+			features
 	);
 	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true);
 	vkcv::Window& window = core.getWindow(windowHandle);
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index 4aa285deb81ead2e078fb59f3c59e852fb6c4627..fe096114ead0b66a6d919a4d49baa791debc4897 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -291,7 +291,7 @@ int main(int argc, const char** argv) {
 		// albedo texture
 		sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Srgb, albedoTexture.w, albedoTexture.h, 1, true));
 		sceneImages.back().fill(albedoTexture.data.data());
-		sceneImages.back().recordMipChainGeneration(mipStream, downsampler);
+		sceneImages.back().recordMipChainGeneration(mipStream, spdDownsampler);
 		const vkcv::ImageHandle albedoHandle = sceneImages.back().getHandle();
 
 		// normal texture
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 808e0c68407fdd6a2f5dd1df962af01f82c760a1..06942b87c072566120d4397f3d3be728770ae057 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -900,6 +900,10 @@ namespace vkcv
 		return m_ImageManager->getImageFormat(image);
 	}
 	
+	bool Core::isImageSupportingStorage(const ImageHandle &image) {
+		return m_ImageManager->isImageSupportingStorage(image);
+	}
+	
 	uint32_t Core::getImageMipLevels(const ImageHandle &image) {
 		return m_ImageManager->getImageMipCount(image);
 	}
@@ -931,9 +935,9 @@ namespace vkcv
 	    return m_DescriptorManager->getDescriptorSetLayout(handle);
 	}
 
-	DescriptorSetHandle Core::createDescriptorSet(const DescriptorSetLayoutHandle &layoutHandle)
+	DescriptorSetHandle Core::createDescriptorSet(const DescriptorSetLayoutHandle &layout)
     {
-        return m_DescriptorManager->createDescriptorSet(layoutHandle);
+        return m_DescriptorManager->createDescriptorSet(layout);
     }
 
 	void Core::writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites &writes) {
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index b58fe3c56a60d3ba619b1d11859271dd198bc6b7..d37e293b48c2c6fc39cab6afd9cfc5054aebcb73 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -92,6 +92,9 @@ namespace vkcv {
 			
 			if (!(formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eStorageImage)) {
 				imageTiling = vk::ImageTiling::eLinear;
+				
+				if (!(formatProperties.linearTilingFeatures & vk::FormatFeatureFlagBits::eStorageImage))
+					return ImageHandle();
 			}
 		}
 		
@@ -253,7 +256,8 @@ namespace vkcv {
 			
 			format,
 			arrayLayers,
-			vk::ImageLayout::eUndefined
+			vk::ImageLayout::eUndefined,
+			supportStorage
 		});
 		
 		return ImageHandle(id, [&](uint64_t id) { destroyImageById(id); });
@@ -750,6 +754,22 @@ namespace vkcv {
 
 		return isSwapchainFormat ? m_swapchainImages[m_currentSwapchainInputImage].m_format : m_images[id].m_format;
 	}
+	
+	bool ImageManager::isImageSupportingStorage(const ImageHandle& handle) const {
+		const uint64_t id = handle.getId();
+		const bool isSwapchainFormat = handle.isSwapchainImage();
+		
+		if (isSwapchainFormat) {
+			return false;
+		}
+		
+		if (id >= m_images.size()) {
+			vkcv_log(LogLevel::ERROR, "Invalid handle");
+			return false;
+		}
+		
+		return m_images[id].m_storage;
+	}
 
 	uint32_t ImageManager::getImageMipCount(const ImageHandle& handle) const {
 		const uint64_t id = handle.getId();
@@ -808,7 +828,8 @@ namespace vkcv {
 				1,
 				format,
 				1,
-				vk::ImageLayout::eUndefined
+				vk::ImageLayout::eUndefined,
+				false
 			});
 		}
 	}
diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp
index 84d41d3457d1d5a2869f08ba1401ddc3decbbc26..3e577fd9a61fea6334faaf136386bf845ca387e8 100644
--- a/src/vkcv/ImageManager.hpp
+++ b/src/vkcv/ImageManager.hpp
@@ -46,6 +46,7 @@ namespace vkcv {
 			vk::Format                  m_format;
 			uint32_t                    m_layers;
 			vk::ImageLayout             m_layout;
+			bool 						m_storage;
 		private:
 			friend ImageManager;
 		};
@@ -130,6 +131,9 @@ namespace vkcv {
 		
 		[[nodiscard]]
 		vk::Format getImageFormat(const ImageHandle& handle) const;
+		
+		[[nodiscard]]
+		bool isImageSupportingStorage(const ImageHandle& handle) const;
 
 		[[nodiscard]]
 		uint32_t getImageMipCount(const ImageHandle& handle) const;