From 40a58f3516fa1b5f7af46ccbfa49e5f9db5fe558 Mon Sep 17 00:00:00 2001
From: Alexander Gauggel <agauggel@uni-koblenz.de>
Date: Tue, 8 Jun 2021 17:02:29 +0200
Subject: [PATCH] [#70] Implement initial voxelization

---
 include/vkcv/Core.hpp                         |   5 +-
 include/vkcv/Image.hpp                        |   2 +-
 .../resources/shaders/compile.bat             |   2 +
 .../resources/shaders/voxelization.frag       |  27 ++++
 .../resources/shaders/voxelization.vert       |  18 +++
 .../resources/shaders/voxelization_frag.spv   | Bin 0 -> 2284 bytes
 .../resources/shaders/voxelization_vert.spv   | Bin 0 -> 1692 bytes
 projects/cmd_sync_test/src/main.cpp           | 137 +++++++++++++-----
 projects/first_mesh/src/main.cpp              |   2 +-
 src/vkcv/Context.cpp                          |   7 +-
 src/vkcv/Core.cpp                             |  17 ++-
 src/vkcv/DescriptorManager.cpp                |   3 +-
 src/vkcv/DescriptorManager.hpp                |   3 +-
 src/vkcv/Image.cpp                            |   4 +-
 src/vkcv/ImageManager.cpp                     |   8 +-
 src/vkcv/ImageManager.hpp                     |   2 +-
 16 files changed, 184 insertions(+), 53 deletions(-)
 create mode 100644 projects/cmd_sync_test/resources/shaders/voxelization.frag
 create mode 100644 projects/cmd_sync_test/resources/shaders/voxelization.vert
 create mode 100644 projects/cmd_sync_test/resources/shaders/voxelization_frag.spv
 create mode 100644 projects/cmd_sync_test/resources/shaders/voxelization_vert.spv

diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index 8a165adf..c21ee3d6 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -203,7 +203,7 @@ namespace vkcv
          * @return Image-Object
          */
         [[nodiscard]]
-        Image createImage(vk::Format format, uint32_t width, uint32_t height, uint32_t depth = 1);
+        Image createImage(vk::Format format, uint32_t width, uint32_t height, uint32_t depth = 1, bool supportStorage = false, bool supportColorAttachment = false);
 
         /** TODO:
          *   @param setDescriptions
@@ -211,7 +211,7 @@ namespace vkcv
          */
         [[nodiscard]]
         DescriptorSetHandle createDescriptorSet(const std::vector<DescriptorBinding> &bindings);
-		void writeResourceDescription(DescriptorSetHandle handle, size_t setIndex, const DescriptorWrites& writes);
+		void writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites& writes);
 
 		DescriptorSet getDescriptorSet(const DescriptorSetHandle handle) const;
 
@@ -259,5 +259,6 @@ namespace vkcv
 		void submitCommandStream(const CommandStreamHandle handle);
 		void prepareSwapchainImageForPresent(const CommandStreamHandle handle);
 		void prepareImageForSampling(const CommandStreamHandle cmdStream, const ImageHandle image);
+		void prepareImageForStorage(const CommandStreamHandle cmdStream, const ImageHandle image);
     };
 }
diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp
index a1219ce4..e1eb78ee 100644
--- a/include/vkcv/Image.hpp
+++ b/include/vkcv/Image.hpp
@@ -45,7 +45,7 @@ namespace vkcv {
 
 		Image(ImageManager* manager, const ImageHandle& handle);
 		
-		static Image create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth);
+		static Image create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth, bool supportStorage, bool supportColorAttachment);
 		
 	};
 	
diff --git a/projects/cmd_sync_test/resources/shaders/compile.bat b/projects/cmd_sync_test/resources/shaders/compile.bat
index 516c2f2f..9ffc664a 100644
--- a/projects/cmd_sync_test/resources/shaders/compile.bat
+++ b/projects/cmd_sync_test/resources/shaders/compile.bat
@@ -2,4 +2,6 @@
 %VULKAN_SDK%\Bin32\glslc.exe shader.frag -o frag.spv
 %VULKAN_SDK%\Bin32\glslc.exe shadow.vert -o shadow_vert.spv
 %VULKAN_SDK%\Bin32\glslc.exe shadow.frag -o shadow_frag.spv
+%VULKAN_SDK%\Bin32\glslc.exe voxelization.vert -o voxelization_vert.spv
+%VULKAN_SDK%\Bin32\glslc.exe voxelization.frag -o voxelization_frag.spv
 pause
\ No newline at end of file
diff --git a/projects/cmd_sync_test/resources/shaders/voxelization.frag b/projects/cmd_sync_test/resources/shaders/voxelization.frag
new file mode 100644
index 00000000..91575b03
--- /dev/null
+++ b/projects/cmd_sync_test/resources/shaders/voxelization.frag
@@ -0,0 +1,27 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec3 passPos;
+
+layout(set=0, binding=0, r8) uniform image3D  voxelImage;
+layout(set=0, binding=1) uniform voxelizationInfo{
+    float extent;
+} voxelInfo;
+
+vec3 worldToVoxelCoordinates(vec3 world, float voxelExtent){
+    return world / voxelExtent + 0.5f;
+}
+
+ivec3 voxelCoordinatesToUV(vec3 voxelCoordinates, ivec3 voxelImageResolution){
+    return ivec3(voxelCoordinates * voxelImageResolution);
+}
+
+void main()	{
+    vec3 voxelCoordinates = worldToVoxelCoordinates(passPos, voxelInfo.extent);
+    ivec3 voxeImageSize = imageSize(voxelImage);
+    ivec3 UV = voxelCoordinatesToUV(voxelCoordinates, voxeImageSize);
+    if(any(lessThan(UV, ivec3(0))) || any(greaterThanEqual(UV, voxeImageSize))){
+        //return;
+    }
+    imageStore(voxelImage, UV, vec4(1));
+}
\ No newline at end of file
diff --git a/projects/cmd_sync_test/resources/shaders/voxelization.vert b/projects/cmd_sync_test/resources/shaders/voxelization.vert
new file mode 100644
index 00000000..6914e951
--- /dev/null
+++ b/projects/cmd_sync_test/resources/shaders/voxelization.vert
@@ -0,0 +1,18 @@
+#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 passPos;
+
+layout( push_constant ) uniform constants{
+    mat4 mvp;
+    mat4 model;
+};
+
+void main()	{
+	gl_Position = mvp * vec4(inPosition, 1.0);
+    passPos     = (model * vec4(inPosition, 1)).xyz;
+}
\ No newline at end of file
diff --git a/projects/cmd_sync_test/resources/shaders/voxelization_frag.spv b/projects/cmd_sync_test/resources/shaders/voxelization_frag.spv
new file mode 100644
index 0000000000000000000000000000000000000000..2093865dba17592a0d8ba5d4372e6fdf6f578b85
GIT binary patch
literal 2284
zcmZQ(Qf6mhU}WH8;AIG7fB-=TCI&_Z1_lsq2&Ih}7#O%2*udiMKEXbE#U&|vCZ+};
z9efPTAPOwU&%nmO!oUE=xrv#1APG$d1_l8JX0RAL0|Nsy12Y2?0|Ub$MvyvA26vx$
z#~`Qp;?#n~qQsKa_~MMjl+>d5{G_bZ<dR~L8ZM9;cYl9(AJ_Qgf`a(slFFRa_?*nV
z)cBOlqSWM)%(7IFIu0atnR&@Mr76e?SQ%KrZh*Oq73^yUhVuNPoRpCKu>6YD9OwM}
zqLj?M#FErvjj}Xj>oh}a29Q2(2B<m)1_o9JRt6ph28J?_2G@#`)VvY~kXfQIF(gAm
z@<YSG29;$RTZ8qoGYDf*2MQ-v1`Y-hn4D*BVtQ&&YH@x}X-Q^2I2>6S*csGe@<?_-
z^s+F3!nq)^xHurcm;t0#3#K-+DiLIcXI@%9NG%%!D+5Rl#7>1cAM7U`n0lx}kiYd9
z7#Kj&k_&b_3j-*^5qvfVQ>Y%WbAvOhQo-t287!dUIbaD;m@zYe{2v+yVzV&VpqXoj
z#CK<a_(cxnKn4Z|4<z?0L&e+~n8AJkr6Z7fklU5uax4rWz9IuCWiv2<<U#I1mIv`c
z@*pk33{2qks0dD(3}Chx12Y2~0|SEs0|SFN0}BHv-Gca_l&-+Q!ob1602Tv@gZP|K
zK1husI5j}zLFzzqq6{ny{7~~ic?)Ew00RS93}&Vv)C`a~$P6KHzF`2{1(MTdU;(>H
z8sumO278db8c;ui++oeY!T{of(gDa0IcOMx+yqjq%>eNmDDFXOL3~g+fx-}EHpqRt
z5VILT>OpxQBnQ$j$-v2A#=ro!A0!4<2BtuKkY11;bFc^lSS`p75FZrQAYl;(CI&|a
z1_m7lHU^L%of#MyK=#2fC`?=!7{F>kVFKa@L+u2WDIhn3+!6vZjG@6^hk=~|Bp=Ga
zzyK0M#%2tx;CKY-11SfY_k)3nft7)Q0pu=Fynyl&EZjhRP+EuaL2(93>o7hj{6T3Q
znGZ_OFg{2>C{4roAoZX$4dUBE(~mv_12~V`F)%ZL>;mx}8CV%W;S4HYL2&^J3y^w{
zKaCj}7(n76^&q!^_?8T8;B;rgz`(%EzyLM}BnMIh@()NHq#qPlmJAHwe3-$&0uFC0
z1_lO@7)UM1FCachEyRrs;IaiI2l9t40|Ns{4ir!J3=H6W4w3`;3B(7v10?6bz`y`1
z_dt3;av(LJZ~=+K^gDsWoPl8@11ke4eq9+D7(im6_@BkV0*-Gt1_lO@_zEcBoq>S?
zB=((wnZXkjfeZ``zZh5;K;j+@3}7)(e1h08d%U3b!1&%!cdv)q>%+jn08#@IhuQ1P
zz`y_!|H;4vHxp#vR|a;lyZoT;0Hqs{T9ADpHw7TM38coKfq?-e267XK4KfeL2hkum
zf#M_(8Ydt=DExw;Zcb)kVF1a2_@MOsmw}PNf`O3%l#W611Bz>qm0S!A4CV~X;4%SJ
zCxO_Yv<j+^Kz8#%;|-)o99(BZ;t`adBp4XL^;Q5h4M{REFo48hxC2~kFfd4g%RB}K
zkUUJS3<CoLsN8|!MyOg@28eo){67XJ1`r?SZjk>#X#?bLP+10YD=43U>OT-0lt)1I
z9mw5cNbUxexga&LFanj+Aa(iBd;}`TL1L{?H-PE{kRFhD9yG11FfcHH#6fNXu|aME
E0Jogg#{d8T

literal 0
HcmV?d00001

diff --git a/projects/cmd_sync_test/resources/shaders/voxelization_vert.spv b/projects/cmd_sync_test/resources/shaders/voxelization_vert.spv
new file mode 100644
index 0000000000000000000000000000000000000000..bbccee7abd853e0f78d16fecf37dccefc2a13c00
GIT binary patch
literal 1692
zcmZQ(Qf6mhU}WH8;AJpofB-=TCI&_Z1_o{hHZbk(6YQf`T#}+^Vrl?V!N<T1qQG+e
z3|wF~3j+f~ZenI0h{Makz#z%Mz@Wmwz@X2-z+l9{z`)GF%)rFJz;K9>fq{jAlfm65
z-Z982zBsiYu_&=5HNH3_F(tJqK0hfdHMyi1q=pNm#@*lF-N!XPxu77vxTG>CH9jXZ
zFEu_TvnVyWB(p3Pq>ckgU1nZ#PH76V0#*hV24uIfGO#glGcYiu=fnr37KNo2m84dH
z+{^|Q2eCnN`Nf$fnfZAPYz*vR^`P(oi)ZGQ1ZP&Ig4Ds}K>kY4iFeM)EO5yzE=kNw
zPKE0Og%n7xG$#ix$I8IW016|J8LSMf41x>{49WR<Ae|tuGq5qRfW<)axn%_mY)~~I
zH{|B0q~<U%Fo4Vu1}OluS;66*nHK<aHUr4tpztq9EG`bnFJ=JQ1B#2xJiq*++(eKO
zU^Sq)$;=B40~zJPzzX&&hz$xO5DgOp*$ol{(J(QPzd>RkKf=U7Y>*fz%-tE7!RCR~
zc_8_Tm4Sf)q#opNkR8e(0!@safq_8)ECvY+kiE$IKzxus2e2AQ7=pxL`apJr%m>-+
z&cFf=A33N!AU-H;kolmn0r5d*faF2p0^)=83o|f*)2<>o6*GX@Vhqd-Yzzzx3JeSk
z;tVVdAbUZ4kiSJ4SQt2<av&?Up?ppTP)=iDkYQi}=NgcDkeD1a?ZM0d<tPwVU|?b3
z0p}f1USwdfhVnsa2NcdAd-$OCfZPqT2gK(GS<1k`Ai=-_4nvT+Aa}_@;{X({APh2J
zh=G9t<OWb!fy@W-LFo!429g(n%7fH{{3*`B0QNV?evrHb)K4IBkbV#!l=oro0r^oH
z?4AaDka|U^e?V>m@j-D2G7F|3#0SL<NDSs5Rj3^>^FeV1G9Tm~P=N%p0~Ajn^9-SC
zLE<3wAU-HALF&yISix}z3JZ{vKz96KU}6BJD@O)a29RGx85kHq{s)DjEj0YZ7#J8p
z;vhcAol?;9Esz0Jnt<YyfdM22G7}~y%K#Ar*$Wc`r6-U&kXr*7*uiN=o&lmCq#h&>
z3ag0>tYA4%+5*`PQUh`yDD0FN7{Fx*$bEJU%nTqtNF7KX<bE}%`$1s_axW;})EO8U
zK;j@i$o(2<?$=~sU;v3ByI%_`hU|WAs2H;Qbr={JK>9)ILGsA%*JWT}0ND*v<Iljs
j0J2+;fq?-e4#FV&K<0zY1Nj9M?;!cV42%pG42%o_!H|6w

literal 0
HcmV?d00001

diff --git a/projects/cmd_sync_test/src/main.cpp b/projects/cmd_sync_test/src/main.cpp
index a0fb29fa..c6cde4f4 100644
--- a/projects/cmd_sync_test/src/main.cpp
+++ b/projects/cmd_sync_test/src/main.cpp
@@ -63,16 +63,16 @@ int main(int argc, const char** argv) {
 	
 	indexBuffer.fill(mesh.vertexGroups[0].indexBuffer.data);
 	
-	auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes;
+	auto& vertexAttributes = mesh.vertexGroups[0].vertexBuffer.attributes;
 	
-	std::sort(attributes.begin(), attributes.end(), [](const vkcv::VertexAttribute& x, const vkcv::VertexAttribute& y) {
+	std::sort(vertexAttributes.begin(), vertexAttributes.end(), [](const vkcv::VertexAttribute& x, const vkcv::VertexAttribute& y) {
 		return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
 	});
 
 	const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
-		vkcv::VertexBufferBinding(attributes[0].offset, vertexBuffer.getVulkanHandle()),
-		vkcv::VertexBufferBinding(attributes[1].offset, vertexBuffer.getVulkanHandle()),
-		vkcv::VertexBufferBinding(attributes[2].offset, vertexBuffer.getVulkanHandle()) };
+		vkcv::VertexBufferBinding(vertexAttributes[0].offset, vertexBuffer.getVulkanHandle()),
+		vkcv::VertexBufferBinding(vertexAttributes[1].offset, vertexBuffer.getVulkanHandle()),
+		vkcv::VertexBufferBinding(vertexAttributes[2].offset, vertexBuffer.getVulkanHandle()) };
 
 	const vkcv::Mesh loadedMesh(vertexBufferBindings, indexBuffer.getVulkanHandle(), mesh.vertexGroups[0].numIndices);
 
@@ -116,7 +116,7 @@ int main(int argc, const char** argv) {
 		windowWidth,
 		windowHeight,
 		trianglePass,
-		attributes,
+		vertexAttributes,
 		{ core.getDescriptorSet(descriptorSet).layout },
 		true);
 	vkcv::PipelineHandle trianglePipeline = core.createGraphicsPipeline(trianglePipelineDefinition);
@@ -147,30 +147,6 @@ int main(int argc, const char** argv) {
 
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
 
-	const vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(descriptorSet).vulkanHandle);
-
-	const std::vector<glm::vec3> instancePositions = {
-		glm::vec3( 0.f, -2.f, 0.f),
-		glm::vec3( 3.f,  0.f, 0.f),
-		glm::vec3(-3.f,  0.f, 0.f),
-		glm::vec3( 0.f,  2.f, 0.f),
-		glm::vec3( 0.f, -5.f, 0.f)
-	};
-
-	std::vector<glm::mat4> modelMatrices;
-	std::vector<vkcv::DrawcallInfo> drawcalls;
-	std::vector<vkcv::DrawcallInfo> shadowDrawcalls;
-	for (const auto& position : instancePositions) {
-		modelMatrices.push_back(glm::translate(glm::mat4(1.f), position));
-		drawcalls.push_back(vkcv::DrawcallInfo(loadedMesh, { descriptorUsage }));
-		shadowDrawcalls.push_back(vkcv::DrawcallInfo(loadedMesh, {}));
-	}
-
-	modelMatrices.back() *= glm::scale(glm::mat4(1.f), glm::vec3(10.f, 1.f, 10.f));
-
-	std::vector<std::array<glm::mat4, 2>> mainPassMatrices;
-	std::vector<glm::mat4> mvpLight;
-
 	vkcv::ShaderProgram shadowShader;
 	shadowShader.addShader(vkcv::ShaderStage::VERTEX, "resources/shaders/shadow_vert.spv");
 	shadowShader.addShader(vkcv::ShaderStage::FRAGMENT, "resources/shaders/shadow_frag.spv");
@@ -191,7 +167,7 @@ int main(int argc, const char** argv) {
 		shadowMapResolution, 
 		shadowMapResolution, 
 		shadowPass, 
-		attributes,
+		vertexAttributes,
 		{}, 
 		false);
 	const vkcv::PipelineHandle shadowPipe = core.createGraphicsPipeline(shadowPipeConfig);
@@ -212,7 +188,79 @@ int main(int argc, const char** argv) {
         vkcv::SamplerDescriptorWrite(1, sampler), 
         vkcv::SamplerDescriptorWrite(4, shadowSampler) };
     setWrites.uniformBufferWrites   = { vkcv::UniformBufferDescriptorWrite(2, lightBuffer.getHandle()) };
-	core.writeResourceDescription(descriptorSet, 0, setWrites);
+	core.writeDescriptorSet(descriptorSet, setWrites);
+
+	const uint32_t voxelResolution = 32;
+	vkcv::Image voxelImage = core.createImage(vk::Format::eR8Unorm, voxelResolution, voxelResolution, voxelResolution, true);
+
+	const vk::Format voxelizationDummyFormat = vk::Format::eR8Unorm;
+	vkcv::Image voxelizationDummyRenderTarget = core.createImage(voxelizationDummyFormat, voxelResolution, voxelResolution, 1, false, true);
+
+	vkcv::ShaderProgram voxelizationShader;
+	voxelizationShader.addShader(vkcv::ShaderStage::VERTEX, "resources/shaders/voxelization_vert.spv");
+	voxelizationShader.addShader(vkcv::ShaderStage::FRAGMENT, "resources/shaders/voxelization_frag.spv");
+	voxelizationShader.reflectShader(vkcv::ShaderStage::VERTEX);
+	voxelizationShader.reflectShader(vkcv::ShaderStage::FRAGMENT);
+
+	vkcv::PassConfig voxelizationPassConfig({
+		vkcv::AttachmentDescription(vkcv::AttachmentOperation::DONT_CARE, vkcv::AttachmentOperation::DONT_CARE, voxelizationDummyFormat)});
+	vkcv::PassHandle voxelizationPass = core.createPass(voxelizationPassConfig);
+
+	std::vector<vkcv::DescriptorBinding> voxelizationDescriptorBindings = {
+		vkcv::DescriptorBinding(vkcv::DescriptorType::IMAGE_STORAGE,  1, vkcv::ShaderStage::FRAGMENT),
+		vkcv::DescriptorBinding(vkcv::DescriptorType::UNIFORM_BUFFER, 1, vkcv::ShaderStage::FRAGMENT)};
+	vkcv::DescriptorSetHandle voxelizationDescriptorSet = core.createDescriptorSet(voxelizationDescriptorBindings);
+
+	const vkcv::PipelineConfig voxelizationPipeConfig(
+		voxelizationShader,
+		voxelResolution,
+		voxelResolution,
+		voxelizationPass,
+		vertexAttributes,
+		{ core.getDescriptorSet(voxelizationDescriptorSet).layout },
+		false);
+	const vkcv::PipelineHandle voxelizationPipe = core.createGraphicsPipeline(voxelizationPipeConfig);
+
+	struct VoxelizationInfo {
+		float extent;
+	};
+	vkcv::Buffer voxelizationInfoBuffer = core.createBuffer<VoxelizationInfo>(vkcv::BufferType::UNIFORM, sizeof(VoxelizationInfo));
+	const float voxelizationExtent = 10.f;
+	VoxelizationInfo voxelizationInfo;
+	voxelizationInfo.extent = voxelizationExtent;
+	voxelizationInfoBuffer.fill({ voxelizationInfo });
+
+	vkcv::DescriptorWrites voxelizationDescriptorWrites;
+	voxelizationDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(0, voxelImage.getHandle()) };
+	voxelizationDescriptorWrites.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(1, voxelizationInfoBuffer.getHandle()) };
+	core.writeDescriptorSet(voxelizationDescriptorSet, voxelizationDescriptorWrites);
+
+	const std::vector<glm::vec3> instancePositions = {
+		glm::vec3(0.f, -2.f, 0.f),
+		glm::vec3(3.f,  0.f, 0.f),
+		glm::vec3(-3.f,  0.f, 0.f),
+		glm::vec3(0.f,  2.f, 0.f),
+		glm::vec3(0.f, -5.f, 0.f)
+	};
+
+	const vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(descriptorSet).vulkanHandle);
+	const vkcv::DescriptorSetUsage voxelizationDescriptorUsage(0, core.getDescriptorSet(voxelizationDescriptorSet).vulkanHandle);
+
+	std::vector<glm::mat4> modelMatrices;
+	std::vector<vkcv::DrawcallInfo> drawcalls;
+	std::vector<vkcv::DrawcallInfo> shadowDrawcalls;
+	std::vector<vkcv::DrawcallInfo> voxelizationDrawcalls;
+	for (const auto& position : instancePositions) {
+		modelMatrices.push_back(glm::translate(glm::mat4(1.f), position));
+		drawcalls.push_back(vkcv::DrawcallInfo(loadedMesh, { descriptorUsage }));
+		shadowDrawcalls.push_back(vkcv::DrawcallInfo(loadedMesh, {}));
+		voxelizationDrawcalls.push_back(vkcv::DrawcallInfo(loadedMesh, { voxelizationDescriptorUsage }));
+	}
+	modelMatrices.back() *= glm::scale(glm::mat4(1.f), glm::vec3(10.f, 1.f, 10.f));
+
+	std::vector<std::array<glm::mat4, 2>> mainPassMatrices;
+	std::vector<glm::mat4> mvpLight;
+	std::vector<std::array<glm::mat4, 2>> voxelizationMatrices;
 
 	auto start = std::chrono::system_clock::now();
 	const auto appStartTime = start;
@@ -247,7 +295,7 @@ int main(int argc, const char** argv) {
 			shadowProjectionSize,
 			-shadowProjectionSize,
 			shadowProjectionSize);
-		
+
 		glm::mat4 vulkanCorrectionMatrix(1.f);
 		vulkanCorrectionMatrix[2][2] = 0.5;
 		vulkanCorrectionMatrix[3][2] = 0.5;
@@ -260,17 +308,29 @@ int main(int argc, const char** argv) {
 
 		const glm::mat4 viewProjectionCamera = cameraManager.getCamera().getProjection() * cameraManager.getCamera().getView();
 
+		const float voxelizationHalfExtent = 0.5f * voxelizationExtent;
+		const glm::mat4 voxelizationProjection = vulkanCorrectionMatrix * glm::ortho(
+			-voxelizationHalfExtent,
+			 voxelizationHalfExtent,
+			-voxelizationHalfExtent,
+			 voxelizationHalfExtent,
+			-voxelizationHalfExtent,
+			 voxelizationHalfExtent);
+
 		mainPassMatrices.clear();
 		mvpLight.clear();
+		voxelizationMatrices.clear();
 		for (const auto& m : modelMatrices) {
 			mainPassMatrices.push_back({ viewProjectionCamera * m, m });
 			mvpLight.push_back(lightInfo.lightMatrix* m);
+			voxelizationMatrices.push_back({ voxelizationProjection * m, m });
 		}
 
 		vkcv::PushConstantData pushConstantData((void*)mainPassMatrices.data(), 2 * sizeof(glm::mat4));
 		const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
 
-		vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4));
+		const vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4));
+		const vkcv::PushConstantData voxelizationPushConstantData((void*)voxelizationMatrices.data(), 2 * sizeof(glm::mat4));
 
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
@@ -282,6 +342,15 @@ int main(int argc, const char** argv) {
 			shadowDrawcalls,
 			{ shadowMap.getHandle() });
 
+		core.prepareImageForStorage(cmdStream, voxelImage.getHandle());
+		core.recordDrawcallsToCmdStream(
+			cmdStream,
+			voxelizationPass,
+			voxelizationPipe,
+			voxelizationPushConstantData,
+			voxelizationDrawcalls,
+			{ voxelizationDummyRenderTarget.getHandle() });
+
 		core.prepareImageForSampling(cmdStream, shadowMap.getHandle());
 
 		core.recordDrawcallsToCmdStream(
diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp
index 599eae46..bf296710 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -133,7 +133,7 @@ int main(int argc, const char** argv) {
 	vkcv::DescriptorWrites setWrites;
 	setWrites.sampledImageWrites	= { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) };
 	setWrites.samplerWrites			= { vkcv::SamplerDescriptorWrite(1, sampler) };
-	core.writeResourceDescription(descriptorSet, 0, setWrites);
+	core.writeDescriptorSet(descriptorSet, setWrites);
 
 	vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight).getHandle();
 
diff --git a/src/vkcv/Context.cpp b/src/vkcv/Context.cpp
index b53a1a2c..a0b2e0a3 100644
--- a/src/vkcv/Context.cpp
+++ b/src/vkcv/Context.cpp
@@ -275,7 +275,12 @@ namespace vkcv
 		deviceCreateInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
 		deviceCreateInfo.ppEnabledLayerNames = validationLayers.data();
 #endif
-		
+
+		// FIXME: check if device feature is supported
+		vk::PhysicalDeviceFeatures deviceFeatures;
+		deviceFeatures.fragmentStoresAndAtomics = true;
+		deviceCreateInfo.pEnabledFeatures = &deviceFeatures;
+
 		// Ablauf
 		// qCreateInfos erstellen --> braucht das Device
 		// device erstellen
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 9ed83d2a..b7fc5aa9 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -217,7 +217,7 @@ namespace vkcv
 				targetHandle = m_ImageManager->getVulkanImageView(handle);
 				const bool isDepthImage = isDepthFormat(m_ImageManager->getImageFormat(handle));
 				const vk::ImageLayout targetLayout = 
-					isDepthFormat ? vk::ImageLayout::eDepthStencilAttachmentOptimal : vk::ImageLayout::eColorAttachmentOptimal;
+					isDepthImage ? vk::ImageLayout::eDepthStencilAttachmentOptimal : vk::ImageLayout::eColorAttachmentOptimal;
 				m_ImageManager->recordImageLayoutTransition(handle, targetLayout, cmdBuffer);
 			}
 			attachmentsViews.push_back(targetHandle);
@@ -392,9 +392,9 @@ namespace vkcv
 		return m_SamplerManager->createSampler(magFilter, minFilter, mipmapMode, addressMode);
 	}
 
-	Image Core::createImage(vk::Format format, uint32_t width, uint32_t height, uint32_t depth)
+	Image Core::createImage(vk::Format format, uint32_t width, uint32_t height, uint32_t depth, bool supportStorage, bool supportColorAttachment)
 	{
-    	return Image::create(m_ImageManager.get(), format, width, height, depth);
+    	return Image::create(m_ImageManager.get(), format, width, height, depth, supportStorage, supportColorAttachment);
 	}
 
     DescriptorSetHandle Core::createDescriptorSet(const std::vector<DescriptorBinding>& bindings)
@@ -402,10 +402,9 @@ namespace vkcv
         return m_DescriptorManager->createDescriptorSet(bindings);
     }
 
-	void Core::writeResourceDescription(DescriptorSetHandle handle, size_t setIndex, const DescriptorWrites &writes) {
-		m_DescriptorManager->writeResourceDescription(
+	void Core::writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites &writes) {
+		m_DescriptorManager->writeDescriptorSet(
 			handle, 
-			setIndex, 
 			writes, 
 			*m_ImageManager, 
 			*m_BufferManager, 
@@ -465,4 +464,10 @@ namespace vkcv
 			m_ImageManager->recordImageLayoutTransition(image, vk::ImageLayout::eShaderReadOnlyOptimal, cmdBuffer);
 		}, nullptr);
 	}
+
+	void Core::prepareImageForStorage(const CommandStreamHandle cmdStream, const ImageHandle image) {
+		recordCommandsToStream(cmdStream, [image, this](const vk::CommandBuffer cmdBuffer) {
+			m_ImageManager->recordImageLayoutTransition(image, vk::ImageLayout::eGeneral, cmdBuffer);
+		}, nullptr);
+	}
 }
diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp
index a2efecbe..7d3ebe6a 100644
--- a/src/vkcv/DescriptorManager.cpp
+++ b/src/vkcv/DescriptorManager.cpp
@@ -90,9 +90,8 @@ namespace vkcv
 		vk::DescriptorType type;
     };
 
-	void DescriptorManager::writeResourceDescription(
+	void DescriptorManager::writeDescriptorSet(
 		const DescriptorSetHandle	&handle,
-		size_t					setIndex,
 		const DescriptorWrites	&writes,
 		const ImageManager		&imageManager, 
 		const BufferManager		&bufferManager,
diff --git a/src/vkcv/DescriptorManager.hpp b/src/vkcv/DescriptorManager.hpp
index d8607b93..d18be64f 100644
--- a/src/vkcv/DescriptorManager.hpp
+++ b/src/vkcv/DescriptorManager.hpp
@@ -23,9 +23,8 @@ namespace vkcv
 
         DescriptorSetHandle createDescriptorSet(const std::vector<DescriptorBinding> &descriptorBindings);
 
-		void writeResourceDescription(
+		void writeDescriptorSet(
 			const DescriptorSetHandle	&handle,
-			size_t					setIndex,
 			const DescriptorWrites  &writes,
 			const ImageManager      &imageManager,
 			const BufferManager     &bufferManager,
diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp
index f861daeb..9f7fdadc 100644
--- a/src/vkcv/Image.cpp
+++ b/src/vkcv/Image.cpp
@@ -19,9 +19,9 @@ namespace vkcv{
 		}
 	}
 
-	Image Image::create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth)
+	Image Image::create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth, bool supportStorage, bool supportColorAttachment)
 	{
-		return Image(manager, manager->createImage(width, height, depth, format));
+		return Image(manager, manager->createImage(width, height, depth, format, supportStorage, supportColorAttachment));
 	}
 	
 	vk::Format Image::getFormat() const {
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index cdfd32b0..80849375 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -80,7 +80,7 @@ namespace vkcv {
 		}
 	}
 
-	ImageHandle ImageManager::createImage(uint32_t width, uint32_t height, uint32_t depth, vk::Format format)
+	ImageHandle ImageManager::createImage(uint32_t width, uint32_t height, uint32_t depth, vk::Format format, bool supportStorage, bool supportColorAttachment)
 	{
 		const vk::PhysicalDevice& physicalDevice = m_core->getContext().getPhysicalDevice();
 		
@@ -90,6 +90,12 @@ namespace vkcv {
 		vk::ImageUsageFlags imageUsageFlags = (
 				vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst
 		);
+		if (supportStorage) {
+			imageUsageFlags |= vk::ImageUsageFlagBits::eStorage;
+		}
+		if (supportColorAttachment) {
+			imageUsageFlags |= vk::ImageUsageFlagBits::eColorAttachment;
+		}
 		
 		const bool isDepthFormat = isDepthImageFormat(format);
 		
diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp
index b9fccb25..316ad231 100644
--- a/src/vkcv/ImageManager.hpp
+++ b/src/vkcv/ImageManager.hpp
@@ -67,7 +67,7 @@ namespace vkcv {
 		ImageManager& operator=(ImageManager&& other) = delete;
 		ImageManager& operator=(const ImageManager& other) = delete;
 		
-		ImageHandle createImage(uint32_t width, uint32_t height, uint32_t depth, vk::Format format);
+		ImageHandle createImage(uint32_t width, uint32_t height, uint32_t depth, vk::Format format, bool supportStorage, bool supportColorAttachment);
 		
 		ImageHandle createSwapchainImage();
 		
-- 
GitLab