From b627796accedd0ac11fa97e2865c4e6a5a68bb8c Mon Sep 17 00:00:00 2001
From: Tobias Frisch <tfrisch@uni-koblenz.de>
Date: Sat, 11 Jun 2022 01:01:26 +0200
Subject: [PATCH] Got SPD somewhat working... still getting validation layer
 output

Signed-off-by: Tobias Frisch <tfrisch@uni-koblenz.de>
---
 include/vkcv/BlitDownsampler.hpp              |   2 +-
 include/vkcv/Core.hpp                         |   2 +-
 include/vkcv/DescriptorWrites.hpp             |   8 +-
 include/vkcv/Downsampler.hpp                  |   2 +-
 include/vkcv/Image.hpp                        |   4 +-
 .../vkcv/algorithm/SinglePassDownsampler.hpp  |   6 +-
 .../vkcv/algorithm/SinglePassDownsampler.cpp  |  53 +++++---
 .../include/vkcv/material/Material.hpp        |   4 +-
 .../material/src/vkcv/material/Material.cpp   |   2 +-
 modules/scene/src/vkcv/scene/Scene.cpp        |   8 +-
 projects/voxelization/src/main.cpp            |  11 +-
 src/vkcv/BlitDownsampler.cpp                  |   2 +-
 src/vkcv/Core.cpp                             |   7 +-
 src/vkcv/DescriptorManager.cpp                |  12 +-
 src/vkcv/DescriptorWrites.cpp                 |  26 +++-
 src/vkcv/Image.cpp                            |   2 +-
 src/vkcv/ImageManager.cpp                     | 119 +++++++++++++-----
 src/vkcv/ImageManager.hpp                     |   8 +-
 18 files changed, 196 insertions(+), 82 deletions(-)

diff --git a/include/vkcv/BlitDownsampler.hpp b/include/vkcv/BlitDownsampler.hpp
index a7e7e477..ed14d37c 100644
--- a/include/vkcv/BlitDownsampler.hpp
+++ b/include/vkcv/BlitDownsampler.hpp
@@ -16,7 +16,7 @@ namespace vkcv {
 	
 	public:
 		void recordDownsampling(const CommandStreamHandle& cmdStream,
-								const ImageHandle& image) const override;
+								const ImageHandle& image) override;
 	};
 	
 }
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index dacf9697..d5cc376c 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -251,7 +251,7 @@ namespace vkcv
 		 * @return Blit-downsampler
 		 */
 		[[nodiscard]]
-		const Downsampler& getDownsampler() const;
+		Downsampler& getDownsampler();
 
         /**
          * Creates a new window and returns it's handle
diff --git a/include/vkcv/DescriptorWrites.hpp b/include/vkcv/DescriptorWrites.hpp
index 9d3269fe..eb532119 100644
--- a/include/vkcv/DescriptorWrites.hpp
+++ b/include/vkcv/DescriptorWrites.hpp
@@ -21,6 +21,7 @@ namespace vkcv {
 		bool useGeneralLayout;
 		uint32_t arrayIndex;
 		uint32_t mipCount;
+		bool arrayView;
 	};
 	
 	/**
@@ -31,6 +32,7 @@ namespace vkcv {
 		ImageHandle image;
 		uint32_t mipLevel;
 		uint32_t mipCount;
+		bool arrayView;
 	};
 	
 	/**
@@ -91,7 +93,8 @@ namespace vkcv {
 											uint32_t mipLevel = 0,
 											bool useGeneralLayout = false,
 											uint32_t arrayIndex = 0,
-											uint32_t mipCount = 1);
+											uint32_t mipCount = 1,
+											bool arrayView = false);
 		
 		/**
 		 * @brief Adds an entry to write an image to a given binding
@@ -106,7 +109,8 @@ namespace vkcv {
 		DescriptorWrites& writeStorageImage(uint32_t binding,
 											ImageHandle image,
 											uint32_t mipLevel = 0,
-											uint32_t mipCount = 1);
+											uint32_t mipCount = 1,
+											bool arrayView = false);
 		
 		/**
 		 * @brief Adds an entry to write a buffer to a given binding
diff --git a/include/vkcv/Downsampler.hpp b/include/vkcv/Downsampler.hpp
index ae491ea3..d833f58e 100644
--- a/include/vkcv/Downsampler.hpp
+++ b/include/vkcv/Downsampler.hpp
@@ -33,7 +33,7 @@ namespace vkcv {
 		 * @param[in] image
 		 */
 		virtual void recordDownsampling(const CommandStreamHandle& cmdStream,
-										const ImageHandle& image) const = 0;
+										const ImageHandle& image) = 0;
 		
 	};
 	
diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp
index 3cb115fd..6e7b6e57 100644
--- a/include/vkcv/Image.hpp
+++ b/include/vkcv/Image.hpp
@@ -101,10 +101,10 @@ namespace vkcv {
 		 * mip level zero is used as source
 		 * 
 		 * @param[out] cmdStream Command stream that the commands are recorded into
-		 * @param[in] downsampler Downsampler to generate mip levels with
+		 * @param[in,out] downsampler Downsampler to generate mip levels with
 		 */
 		void recordMipChainGeneration(const vkcv::CommandStreamHandle& cmdStream,
-									  const Downsampler &downsampler);
+									  Downsampler &downsampler);
 		
 	private:
 	    // TODO: const qualifier removed, very hacky!!!
diff --git a/modules/algorithm/include/vkcv/algorithm/SinglePassDownsampler.hpp b/modules/algorithm/include/vkcv/algorithm/SinglePassDownsampler.hpp
index c2869aff..588fe925 100644
--- a/modules/algorithm/include/vkcv/algorithm/SinglePassDownsampler.hpp
+++ b/modules/algorithm/include/vkcv/algorithm/SinglePassDownsampler.hpp
@@ -1,5 +1,7 @@
 #pragma once
 
+#include <vector>
+
 #include <vkcv/Core.hpp>
 #include <vkcv/Downsampler.hpp>
 #include <vkcv/ShaderProgram.hpp>
@@ -27,7 +29,7 @@ namespace vkcv::algorithm {
 		ComputePipelineHandle m_pipeline;
 		
 		DescriptorSetLayoutHandle m_descriptorSetLayout;
-		DescriptorSetHandle m_descriptorSet;
+		std::vector<DescriptorSetHandle> m_descriptorSets;
 		
 		Buffer<uint32_t> m_globalCounter;
 		
@@ -38,7 +40,7 @@ namespace vkcv::algorithm {
 									   const SamplerHandle &sampler = SamplerHandle());
 		
 		void recordDownsampling(const CommandStreamHandle& cmdStream,
-								const ImageHandle& image) const override;
+								const ImageHandle& image) override;
 	
 	};
 
diff --git a/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp b/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp
index d715fbda..65ecfc85 100644
--- a/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp
+++ b/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp
@@ -125,7 +125,7 @@ namespace vkcv::algorithm {
 		 m_pipeline(),
 		
 		 m_descriptorSetLayout(m_core.createDescriptorSetLayout(getDescriptorBindings(sampler))),
-		 m_descriptorSet(m_core.createDescriptorSet(m_descriptorSetLayout)),
+		 m_descriptorSets(),
 		
 		 m_globalCounter(m_core.createBuffer<uint32_t>(
 				 vkcv::BufferType::STORAGE,
@@ -202,30 +202,38 @@ namespace vkcv::algorithm {
 		uint32_t zeroes [m_globalCounter.getCount()];
 		memset(zeroes, 0, m_globalCounter.getSize());
 		m_globalCounter.fill(zeroes);
-		
-		vkcv::DescriptorWrites writes;
-		writes.writeStorageBuffer(2, m_globalCounter.getHandle());
-		
-		if (m_sampler) {
-			writes.writeSampler(4, m_sampler);
-		}
-		
-		m_core.writeDescriptorSet(m_descriptorSet, writes);
 	}
 	
 	void SinglePassDownsampler::recordDownsampling(const CommandStreamHandle &cmdStream,
-												   const ImageHandle &image) const {
+												   const ImageHandle &image) {
+		const uint32_t mipLevels = m_core.getImageMipLevels(image);
+		
+		if (mipLevels < 7) {
+			m_core.getDownsampler().recordDownsampling(cmdStream, image);
+			return;
+		}
+		
+		auto descriptorSet = m_core.createDescriptorSet(m_descriptorSetLayout);
+		
 		vkcv::DescriptorWrites writes;
-		writes.writeStorageImage(1, image, 6);
+		writes.writeStorageImage(1, image, 6, 1, true);
+		writes.writeStorageBuffer(2, m_globalCounter.getHandle());
 		
 		if (m_sampler) {
-			writes.writeStorageImage(0, image, 0, m_core.getImageMipLevels(image) - 1);
-			writes.writeSampledImage(3, image);
+			writes.writeStorageImage(0, image, 1, mipLevels - 1, true);
+			
+			writes.writeSampledImage(3, image, 0, false, 0, 1, true);
+			writes.writeSampler(4, m_sampler);
 		} else {
-			writes.writeStorageImage(0, image, 0, m_core.getImageMipLevels(image));
+			writes.writeStorageImage(0, image, 0, mipLevels, true);
 		}
 		
-		m_core.writeDescriptorSet(m_descriptorSet, writes);
+		m_core.writeDescriptorSet(descriptorSet, writes);
+		m_descriptorSets.push_back(descriptorSet);
+		
+		m_core.recordCommandsToStream(cmdStream, nullptr, [this]() {
+			m_descriptorSets.erase(m_descriptorSets.begin());
+		});
 		
 		varAU2(dispatchThreadGroupCountXY);
 		varAU2(workGroupOffset);
@@ -237,11 +245,16 @@ namespace vkcv::algorithm {
 				m_core.getImageHeight(image)
 		);
 		
-		SpdSetup(dispatchThreadGroupCountXY, workGroupOffset, numWorkGroupsAndMips, rectInfo);
+		SpdSetup(
+				dispatchThreadGroupCountXY,
+				workGroupOffset,
+				numWorkGroupsAndMips,
+				rectInfo
+		);
 		
 		if (m_sampler) {
-			m_core.prepareImageForStorage(cmdStream, image, m_core.getImageMipLevels(image) - 1, 1);
 			m_core.prepareImageForSampling(cmdStream, image, 1);
+			m_core.prepareImageForStorage(cmdStream, image, m_core.getImageMipLevels(image) - 1, 1);
 		} else {
 			m_core.prepareImageForStorage(cmdStream, image);
 		}
@@ -274,11 +287,11 @@ namespace vkcv::algorithm {
 		}
 		
 		m_core.recordComputeDispatchToCmdStream(cmdStream, m_pipeline, dispatch, {
-			vkcv::DescriptorSetUsage(0, m_descriptorSet)
+			vkcv::DescriptorSetUsage(0, descriptorSet)
 		}, pushConstants);
 		
 		if (m_sampler) {
-			m_core.prepareImageForSampling(cmdStream, image, m_core.getImageMipLevels(image) - 1, 1);
+			m_core.prepareImageForSampling(cmdStream, image, mipLevels - 1, 1);
 		} else {
 			m_core.prepareImageForSampling(cmdStream, image);
 		}
diff --git a/modules/material/include/vkcv/material/Material.hpp b/modules/material/include/vkcv/material/Material.hpp
index c47d0c5b..345d2291 100644
--- a/modules/material/include/vkcv/material/Material.hpp
+++ b/modules/material/include/vkcv/material/Material.hpp
@@ -152,10 +152,10 @@ namespace vkcv::material {
 		 * @brief Records mip chain generation to command stream of the whole material.
 		 *
 		 * @param[out] cmdStream Command stream that the commands are recorded into
-		 * @param[in] downsampler Downsampler to generate mip levels with
+		 * @param[in,out] downsampler Downsampler to generate mip levels with
 		 */
 		void recordMipChainGeneration(const vkcv::CommandStreamHandle& cmdStream,
-									  const Downsampler &downsampler);
+									  Downsampler &downsampler);
 
         /**
          * Returns the descriptor bindings required by a given material
diff --git a/modules/material/src/vkcv/material/Material.cpp b/modules/material/src/vkcv/material/Material.cpp
index a1054ed0..a6a16f80 100644
--- a/modules/material/src/vkcv/material/Material.cpp
+++ b/modules/material/src/vkcv/material/Material.cpp
@@ -28,7 +28,7 @@ namespace vkcv::material {
 	}
 	
 	void Material::recordMipChainGeneration(const vkcv::CommandStreamHandle& cmdStream,
-											const Downsampler &downsampler) {
+											Downsampler &downsampler) {
 		for (auto& texture : m_Textures) {
 			downsampler.recordDownsampling(cmdStream, texture.m_Image);
 		}
diff --git a/modules/scene/src/vkcv/scene/Scene.cpp b/modules/scene/src/vkcv/scene/Scene.cpp
index 695e5684..bb21807d 100644
--- a/modules/scene/src/vkcv/scene/Scene.cpp
+++ b/modules/scene/src/vkcv/scene/Scene.cpp
@@ -161,7 +161,7 @@ namespace vkcv::scene {
 			//asset_sampler = &(asset_scene.samplers[asset_texture.sampler]); // TODO
 		}
 		
-		Image img = core.createImage(format, asset_texture.w, asset_texture.h);
+		Image img = core.createImage(format, asset_texture.w, asset_texture.h, 1, true);
 		img.fill(asset_texture.data.data());
 		image = img.getHandle();
 		
@@ -195,7 +195,7 @@ namespace vkcv::scene {
 		SamplerHandle normalSmp;
 		
 		if ((material.baseColor >= 0) && (material.baseColor < scene.textures.size())) {
-			loadImage(*m_core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Srgb,
+			loadImage(*m_core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Unorm,
 					  diffuseImg,diffuseSmp);
 		}
 		
@@ -203,7 +203,7 @@ namespace vkcv::scene {
 		SamplerHandle metalRoughSmp;
 		
 		if ((material.baseColor >= 0) && (material.baseColor < scene.textures.size())) {
-			loadImage(*m_core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Srgb,
+			loadImage(*m_core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Unorm,
 					  diffuseImg,diffuseSmp);
 		}
 		
@@ -211,7 +211,7 @@ namespace vkcv::scene {
 		SamplerHandle occlusionSmp;
 		
 		if ((material.baseColor >= 0) && (material.baseColor < scene.textures.size())) {
-			loadImage(*m_core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Srgb,
+			loadImage(*m_core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Unorm,
 					  diffuseImg,diffuseSmp);
 		}
 		
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index 02dc237e..4380a23b 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -251,7 +251,8 @@ int main(int argc, const char** argv) {
 	std::vector<vkcv::DescriptorSetHandle> materialDescriptorSets;
 	std::vector<vkcv::Image> sceneImages;
 	
-	const vkcv::Downsampler &downsampler = core.getDownsampler();
+	vkcv::algorithm::SinglePassDownsampler spdDownsampler (core);
+	vkcv::Downsampler &downsampler = core.getDownsampler();
 	
 	auto mipStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
@@ -287,15 +288,15 @@ int main(int argc, const char** argv) {
 		const vkcv::ImageHandle albedoHandle = sceneImages.back().getHandle();
 
 		// normal texture
-		sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Unorm, normalTexture.w, normalTexture.h, 1, true));
+		sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Unorm, normalTexture.w, normalTexture.h, 1, true, true));
 		sceneImages.back().fill(normalTexture.data.data());
-		sceneImages.back().recordMipChainGeneration(mipStream, downsampler);
+		sceneImages.back().recordMipChainGeneration(mipStream, spdDownsampler);
 		const vkcv::ImageHandle normalHandle = sceneImages.back().getHandle();
 
 		// specular texture
-		sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Unorm, specularTexture.w, specularTexture.h, 1, true));
+		sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Unorm, specularTexture.w, specularTexture.h, 1, true, true));
 		sceneImages.back().fill(specularTexture.data.data());
-		sceneImages.back().recordMipChainGeneration(mipStream, downsampler);
+		sceneImages.back().recordMipChainGeneration(mipStream, spdDownsampler);
 		const vkcv::ImageHandle specularHandle = sceneImages.back().getHandle();
 
 		vkcv::DescriptorWrites setWrites;
diff --git a/src/vkcv/BlitDownsampler.cpp b/src/vkcv/BlitDownsampler.cpp
index ce031549..38e48129 100644
--- a/src/vkcv/BlitDownsampler.cpp
+++ b/src/vkcv/BlitDownsampler.cpp
@@ -12,7 +12,7 @@ namespace vkcv {
 		m_imageManager(imageManager) {}
 	
 	void BlitDownsampler::recordDownsampling(const CommandStreamHandle &cmdStream,
-											 const ImageHandle &image) const {
+											 const ImageHandle &image) {
 		m_imageManager.recordImageMipChainGenerationToCmdStream(cmdStream, image);
 		m_core.prepareImageForSampling(cmdStream, image);
 	}
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 383dcbb9..df7050cc 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -805,7 +805,10 @@ namespace vkcv
 		const RecordCommandFunction &record, 
 		const FinishCommandFunction &finish) {
 
-		m_CommandStreamManager->recordCommandsToStream(cmdStreamHandle, record);
+		if (record) {
+			m_CommandStreamManager->recordCommandsToStream(cmdStreamHandle, record);
+		}
+		
 		if (finish) {
 			m_CommandStreamManager->addFinishCallbackToStream(cmdStreamHandle, finish);
 		}
@@ -858,7 +861,7 @@ namespace vkcv
 			multisampling);
 	}
 	
-	const Downsampler &Core::getDownsampler() const {
+	Downsampler &Core::getDownsampler() {
 		return *m_downsampler;
 	}
 
diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp
index a7e2f06c..127eb805 100644
--- a/src/vkcv/DescriptorManager.cpp
+++ b/src/vkcv/DescriptorManager.cpp
@@ -190,7 +190,11 @@ namespace vkcv
 			for (uint32_t i = 0; i < write.mipCount; i++) {
 				const vk::DescriptorImageInfo imageInfo(
 						nullptr,
-						imageManager.getVulkanImageView(write.image, write.mipLevel + i),
+						imageManager.getVulkanImageView(
+								write.image,
+								write.mipLevel + i,
+								write.arrayView
+						),
 						layout
 				);
 				
@@ -213,7 +217,11 @@ namespace vkcv
 			for (uint32_t i = 0; i < write.mipCount; i++) {
 				const vk::DescriptorImageInfo imageInfo(
 						nullptr,
-						imageManager.getVulkanImageView(write.image, write.mipLevel + i),
+						imageManager.getVulkanImageView(
+								write.image,
+								write.mipLevel + i,
+								write.arrayView
+						),
 						vk::ImageLayout::eGeneral
 				);
 				
diff --git a/src/vkcv/DescriptorWrites.cpp b/src/vkcv/DescriptorWrites.cpp
index 68333b45..c64eeb5c 100644
--- a/src/vkcv/DescriptorWrites.cpp
+++ b/src/vkcv/DescriptorWrites.cpp
@@ -8,16 +8,34 @@ namespace vkcv {
 														  uint32_t mipLevel,
 														  bool useGeneralLayout,
 														  uint32_t arrayIndex,
-														  uint32_t mipCount) {
-		m_sampledImageWrites.emplace_back(binding, image, mipLevel, useGeneralLayout, arrayIndex, mipCount);
+														  uint32_t mipCount,
+														  bool arrayView) {
+		m_sampledImageWrites.emplace_back(
+				binding,
+				image,
+				mipLevel,
+				useGeneralLayout,
+				arrayIndex,
+				mipCount,
+				arrayView
+		);
+		
 		return *this;
 	}
 	
 	DescriptorWrites &DescriptorWrites::writeStorageImage(uint32_t binding,
 														  ImageHandle image,
 														  uint32_t mipLevel,
-														  uint32_t mipCount) {
-		m_storageImageWrites.emplace_back(binding, image, mipLevel, mipCount);
+														  uint32_t mipCount,
+														  bool arrayView) {
+		m_storageImageWrites.emplace_back(
+				binding,
+				image,
+				mipLevel,
+				mipCount,
+				arrayView
+		);
+		
 		return *this;
 	}
 	
diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp
index 32cbb056..84a706f8 100644
--- a/src/vkcv/Image.cpp
+++ b/src/vkcv/Image.cpp
@@ -71,7 +71,7 @@ namespace vkcv{
 	}
 
 	void Image::recordMipChainGeneration(const vkcv::CommandStreamHandle& cmdStream,
-										 const Downsampler &downsampler) {
+										 Downsampler &downsampler) {
 		downsampler.recordDownsampling(cmdStream, m_handle);
 	}
 	
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index e7112579..1e4e3a5b 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -80,7 +80,9 @@ namespace vkcv {
 		
 		vk::ImageCreateFlags createFlags;
 		vk::ImageUsageFlags imageUsageFlags = (
-				vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eTransferSrc
+				vk::ImageUsageFlagBits::eSampled |
+				vk::ImageUsageFlagBits::eTransferDst |
+				vk::ImageUsageFlagBits::eTransferSrc
 		);
 		
 		vk::ImageTiling imageTiling = vk::ImageTiling::eOptimal;
@@ -130,8 +132,12 @@ namespace vkcv {
 			imageTiling = vk::ImageTiling::eLinear;
 		}
 		
-		const vk::ImageFormatProperties imageFormatProperties = 
-			physicalDevice.getImageFormatProperties(format, imageType, imageTiling, imageUsageFlags);
+		const vk::ImageFormatProperties imageFormatProperties = physicalDevice.getImageFormatProperties(
+				format,
+				imageType,
+				imageTiling,
+				imageUsageFlags
+		);
 		
 		const uint32_t arrayLayers = std::min<uint32_t>(1, imageFormatProperties.maxArrayLayers);
 
@@ -179,32 +185,74 @@ namespace vkcv {
 		const vk::Device& device = m_core->getContext().getDevice();
 		
 		std::vector<vk::ImageView> views;
+		std::vector<vk::ImageView> arrayViews;
+		
 		for (uint32_t mip = 0; mip < mipCount; mip++) {
-			const vk::ImageViewCreateInfo imageViewCreateInfo(
-				{},
-				image,
-				imageViewType,
-				format,
-				vk::ComponentMapping(
-					vk::ComponentSwizzle::eIdentity,
-					vk::ComponentSwizzle::eIdentity,
-					vk::ComponentSwizzle::eIdentity,
-					vk::ComponentSwizzle::eIdentity
-				),
-				vk::ImageSubresourceRange(
-					aspectFlags,
-					mip,
-					mipCount - mip,
-					0,
-					arrayLayers
-				)
-			);
-
-			views.push_back(device.createImageView(imageViewCreateInfo));
+			{
+				const vk::ImageViewCreateInfo imageViewCreateInfo(
+						{},
+						image,
+						imageViewType,
+						format,
+						vk::ComponentMapping(
+								vk::ComponentSwizzle::eIdentity,
+								vk::ComponentSwizzle::eIdentity,
+								vk::ComponentSwizzle::eIdentity,
+								vk::ComponentSwizzle::eIdentity
+						),
+						vk::ImageSubresourceRange(
+								aspectFlags,
+								mip,
+								mipCount - mip,
+								0,
+								arrayLayers
+						)
+				);
+				
+				views.push_back(device.createImageView(imageViewCreateInfo));
+			}
+			
+			{
+				const vk::ImageViewCreateInfo imageViewCreateInfo(
+						{},
+						image,
+						vk::ImageViewType::e2DArray,
+						format,
+						vk::ComponentMapping(
+								vk::ComponentSwizzle::eIdentity,
+								vk::ComponentSwizzle::eIdentity,
+								vk::ComponentSwizzle::eIdentity,
+								vk::ComponentSwizzle::eIdentity
+						),
+						vk::ImageSubresourceRange(
+								aspectFlags,
+								mip,
+								1,
+								0,
+								arrayLayers
+						)
+				);
+				
+				arrayViews.push_back(device.createImageView(imageViewCreateInfo));
+			}
 		}
 		
 		const uint64_t id = m_images.size();
-		m_images.push_back({ image, allocation, views, width, height, depth, format, arrayLayers, vk::ImageLayout::eUndefined });
+		m_images.push_back({
+			image,
+			allocation,
+			
+			views,
+			arrayViews,
+			
+			width,
+			height,
+			depth,
+			
+			format,
+			arrayLayers,
+			vk::ImageLayout::eUndefined
+		});
 		return ImageHandle(id, [&](uint64_t id) { destroyImageById(id); });
 	}
 	
@@ -253,7 +301,9 @@ namespace vkcv {
 		return info.deviceMemory;
 	}
 	
-	vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle, size_t mipLevel) const {
+	vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle,
+												   size_t mipLevel,
+												   bool arrayView) const {
 		
 		if (handle.isSwapchainImage()) {
 			return m_swapchainImages[m_currentSwapchainInputImage].m_viewPerMip[0];
@@ -266,13 +316,14 @@ namespace vkcv {
 		}
 		
 		const auto& image = m_images[id];
-
-		if (mipLevel >= image.m_viewPerMip.size()) {
+		const auto& views = arrayView? image.m_arrayViewPerMip : image.m_viewPerMip;
+		
+		if (mipLevel >= views.size()) {
 			vkcv_log(LogLevel::ERROR, "Image does not have requested mipLevel");
 			return nullptr;
 		}
-
-		return image.m_viewPerMip[mipLevel];
+		
+		return views[mipLevel];
 	}
 	
 	static vk::ImageMemoryBarrier createImageLayoutTransitionBarrier(const ImageManager::Image &image,
@@ -665,6 +716,13 @@ namespace vkcv {
 			}
 		}
 		
+		for (auto& view : image.m_arrayViewPerMip) {
+			if (view) {
+				device.destroyImageView(view);
+				view = nullptr;
+			}
+		}
+		
 		const vma::Allocator& allocator = m_core->getContext().getAllocator();
 
 		if (image.m_handle) {
@@ -724,6 +782,7 @@ namespace vkcv {
 				images[i],
 				nullptr,
 				{ views[i] },
+				{},
 				width,
 				height,
 				1,
diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp
index 5ac21b55..dec46c6f 100644
--- a/src/vkcv/ImageManager.hpp
+++ b/src/vkcv/ImageManager.hpp
@@ -35,10 +35,14 @@ namespace vkcv {
 		{
 			vk::Image                   m_handle;
 			vma::Allocation             m_allocation;
+			
 			std::vector<vk::ImageView>  m_viewPerMip;
+			std::vector<vk::ImageView>  m_arrayViewPerMip;
+			
 			uint32_t                    m_width;
 			uint32_t                    m_height;
 			uint32_t                    m_depth;
+			
 			vk::Format                  m_format;
 			uint32_t                    m_layers;
 			vk::ImageLayout             m_layout;
@@ -94,7 +98,9 @@ namespace vkcv {
 		vk::DeviceMemory getVulkanDeviceMemory(const ImageHandle& handle) const;
 		
 		[[nodiscard]]
-		vk::ImageView getVulkanImageView(const ImageHandle& handle, size_t mipLevel = 0) const;
+		vk::ImageView getVulkanImageView(const ImageHandle& handle,
+										 size_t mipLevel = 0,
+										 bool arrayView = false) const;
 
 		void switchImageLayoutImmediate(const ImageHandle& handle, vk::ImageLayout newLayout);
 		
-- 
GitLab