From 46438771ea5dc291586654ae737d7f8250f36901 Mon Sep 17 00:00:00 2001
From: Alexander Gauggel <agauggel@uni-koblenz.de>
Date: Thu, 10 Jun 2021 02:06:45 +0200
Subject: [PATCH] [#70] Add voxel visualisation and fix voxelisation bugs

---
 include/vkcv/Core.hpp                         |   1 +
 include/vkcv/PipelineConfig.hpp               |   6 +-
 modules/camera/include/vkcv/camera/Camera.hpp |   2 +-
 modules/camera/src/vkcv/camera/Camera.cpp     |   4 +
 .../resources/shaders/compile.bat             |   6 +
 .../resources/shaders/voxelVisualisation.frag |   9 ++
 .../resources/shaders/voxelVisualisation.geom | 103 ++++++++++++++++++
 .../resources/shaders/voxelVisualisation.vert |  30 +++++
 .../shaders/voxelVisualisation_frag.spv       | Bin 0 -> 484 bytes
 .../shaders/voxelVisualisation_geom.spv       | Bin 0 -> 9608 bytes
 .../shaders/voxelVisualisation_vert.spv       | Bin 0 -> 3072 bytes
 .../resources/shaders/voxelization.geom       |   3 +-
 .../resources/shaders/voxelization_geom.spv   | Bin 2888 -> 3004 bytes
 projects/cmd_sync_test/src/main.cpp           |  91 +++++++++++++++-
 src/vkcv/Core.cpp                             |   6 +
 src/vkcv/ImageManager.cpp                     |  16 +++
 src/vkcv/ImageManager.hpp                     |   4 +
 src/vkcv/PipelineConfig.cpp                   |   6 +-
 src/vkcv/PipelineManager.cpp                  |  15 ++-
 19 files changed, 289 insertions(+), 13 deletions(-)
 create mode 100644 projects/cmd_sync_test/resources/shaders/voxelVisualisation.frag
 create mode 100644 projects/cmd_sync_test/resources/shaders/voxelVisualisation.geom
 create mode 100644 projects/cmd_sync_test/resources/shaders/voxelVisualisation.vert
 create mode 100644 projects/cmd_sync_test/resources/shaders/voxelVisualisation_frag.spv
 create mode 100644 projects/cmd_sync_test/resources/shaders/voxelVisualisation_geom.spv
 create mode 100644 projects/cmd_sync_test/resources/shaders/voxelVisualisation_vert.spv

diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index c21ee3d6..061a2bd7 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -260,5 +260,6 @@ namespace vkcv
 		void prepareSwapchainImageForPresent(const CommandStreamHandle handle);
 		void prepareImageForSampling(const CommandStreamHandle cmdStream, const ImageHandle image);
 		void prepareImageForStorage(const CommandStreamHandle cmdStream, const ImageHandle image);
+		void recordImageMemoryBarrier(const CommandStreamHandle cmdStream, const ImageHandle image);
     };
 }
diff --git a/include/vkcv/PipelineConfig.hpp b/include/vkcv/PipelineConfig.hpp
index 2d1d1488..55f659b8 100644
--- a/include/vkcv/PipelineConfig.hpp
+++ b/include/vkcv/PipelineConfig.hpp
@@ -13,6 +13,8 @@
 
 namespace vkcv {
 
+    enum class PrimitiveTopology{PointList, LineList, TriangleList };
+
     struct PipelineConfig {
         /**
          *  Constructor for the pipeline. Creates a pipeline using @p vertexCode, @p fragmentCode as well as the
@@ -31,7 +33,8 @@ namespace vkcv {
             const std::vector<VertexAttribute>          &vertexAttributes,
             const std::vector<vk::DescriptorSetLayout>  &descriptorLayouts,
             bool                                        useDynamicViewport,
-            bool                                        useConservativeRasterization = false);
+            bool                                        useConservativeRasterization = false,
+            PrimitiveTopology                           primitiveTopology = PrimitiveTopology::TriangleList);
 
         ShaderProgram                           m_ShaderProgram;
         uint32_t                                m_Height;
@@ -41,6 +44,7 @@ namespace vkcv {
         std::vector<vk::DescriptorSetLayout>    m_DescriptorLayouts;
         bool                                    m_UseDynamicViewport;
         bool                                    m_UseConservativeRasterization;
+        PrimitiveTopology                       m_PrimitiveTopology;
     };
 
 }
\ No newline at end of file
diff --git a/modules/camera/include/vkcv/camera/Camera.hpp b/modules/camera/include/vkcv/camera/Camera.hpp
index ff8fda81..f21dcc48 100644
--- a/modules/camera/include/vkcv/camera/Camera.hpp
+++ b/modules/camera/include/vkcv/camera/Camera.hpp
@@ -95,7 +95,7 @@ namespace vkcv {
 
         void moveRight(int action);
 
-
+        void setSpeed(float speed);
     };
 
 }
diff --git a/modules/camera/src/vkcv/camera/Camera.cpp b/modules/camera/src/vkcv/camera/Camera.cpp
index af89ed86..f77d55fc 100644
--- a/modules/camera/src/vkcv/camera/Camera.cpp
+++ b/modules/camera/src/vkcv/camera/Camera.cpp
@@ -176,4 +176,8 @@ namespace vkcv {
     void Camera::moveRight(int action){
         m_right = static_cast<bool>(action);
     }
+
+    void Camera::setSpeed(float speed) {
+        m_cameraSpeed = speed;
+    }
 }
\ No newline at end of file
diff --git a/projects/cmd_sync_test/resources/shaders/compile.bat b/projects/cmd_sync_test/resources/shaders/compile.bat
index d8f0abf7..26239841 100644
--- a/projects/cmd_sync_test/resources/shaders/compile.bat
+++ b/projects/cmd_sync_test/resources/shaders/compile.bat
@@ -1,8 +1,14 @@
 %VULKAN_SDK%\Bin32\glslc.exe shader.vert -o vert.spv
 %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.geom -o voxelization_geom.spv
 %VULKAN_SDK%\Bin32\glslc.exe voxelization.frag -o voxelization_frag.spv
+
+%VULKAN_SDK%\Bin32\glslc.exe voxelVisualisation.vert -o voxelVisualisation_vert.spv
+%VULKAN_SDK%\Bin32\glslc.exe voxelVisualisation.geom -o voxelVisualisation_geom.spv
+%VULKAN_SDK%\Bin32\glslc.exe voxelVisualisation.frag -o voxelVisualisation_frag.spv
 pause
\ No newline at end of file
diff --git a/projects/cmd_sync_test/resources/shaders/voxelVisualisation.frag b/projects/cmd_sync_test/resources/shaders/voxelVisualisation.frag
new file mode 100644
index 00000000..e1a363c7
--- /dev/null
+++ b/projects/cmd_sync_test/resources/shaders/voxelVisualisation.frag
@@ -0,0 +1,9 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location=0) in vec3 inColor;
+layout(location=0) out vec3 outColor;
+
+void main()	{
+    outColor = inColor;
+}
\ No newline at end of file
diff --git a/projects/cmd_sync_test/resources/shaders/voxelVisualisation.geom b/projects/cmd_sync_test/resources/shaders/voxelVisualisation.geom
new file mode 100644
index 00000000..ff0b8b6f
--- /dev/null
+++ b/projects/cmd_sync_test/resources/shaders/voxelVisualisation.geom
@@ -0,0 +1,103 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(points) in;
+layout (triangle_strip, max_vertices = 24) out;
+
+layout( push_constant ) uniform constants{
+    mat4 viewProjection;
+};
+
+layout(location = 0) in float passCubeHalf[1];
+
+layout(location = 0) out vec3 passColor;
+
+void main()	{
+    float cubeHalf = passCubeHalf[0];
+    // right
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, 1, 1), 1);
+    passColor = vec3(1, 0, 0);
+    EmitVertex();
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, 1, -1), 1);
+    passColor = vec3(1, 0, 0);
+    EmitVertex();
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, -1, 1), 1);
+    passColor = vec3(1, 0, 0);
+    EmitVertex();
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, -1, -1), 1);
+    passColor = vec3(1, 0, 0);
+    EmitVertex();
+    EndPrimitive();
+    // left
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, 1, 1), 1);
+    passColor = vec3(1, 0, 0);
+    EmitVertex();
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, 1, -1), 1);
+    passColor = vec3(1, 0, 0);
+    EmitVertex();
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, -1, 1), 1);
+    passColor = vec3(1, 0, 0);
+    EmitVertex();
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, -1, -1), 1);
+    passColor = vec3(1, 0, 0);
+    EmitVertex();
+    EndPrimitive();
+    // back
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1,   1, -1), 1);
+    passColor = vec3(1, 0, 0);
+    EmitVertex();                                                                  
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1,  -1, -1), 1);
+    passColor = vec3(1, 0, 0);
+    EmitVertex();                                                                  
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1,  1, -1), 1);
+    passColor = vec3(1, 0, 0);
+    EmitVertex();                                                                  
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, -1, -1), 1);
+    passColor = vec3(0, 0, 1);
+    EmitVertex();
+    EndPrimitive();
+    // front
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1,   1, 1), 1);
+    passColor = vec3(0, 0, 1);
+    EmitVertex();                                                                  
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1,  -1, 1), 1);
+    passColor = vec3(0, 0, 1);
+    EmitVertex();                                                                  
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1,  1, 1), 1);
+    passColor = vec3(0, 0, 1);
+    EmitVertex();                                                                  
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, -1, 1), 1);
+    passColor = vec3(0, 0, 1);
+    EmitVertex();
+    EndPrimitive();
+    // bot
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1,  1,  1), 1);
+    passColor = vec3(0, 1, 0);
+    EmitVertex(); 
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1,  1, -1), 1);
+    passColor = vec3(0, 1, 0);
+    EmitVertex();
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, 1,  1), 1);
+    passColor = vec3(0, 1, 0);
+    EmitVertex();  
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, 1, -1), 1);
+    passColor = vec3(0, 1, 0);
+    passColor = vec3(0, 1, 0);
+    EmitVertex();
+    EndPrimitive();
+    // top
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1,  -1,  1), 1);
+    passColor = vec3(0, 1, 0);
+    EmitVertex();   
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1,  -1, -1), 1);
+    passColor = vec3(0, 1, 0);
+    EmitVertex();  
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, -1,  1), 1);
+    passColor = vec3(0, 1, 0);
+    EmitVertex();   
+    gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, -1, -1), 1);
+    passColor = vec3(0, 1, 0);
+    passColor = vec3(0, 1, 0);
+    EmitVertex();
+    EndPrimitive();
+}
\ No newline at end of file
diff --git a/projects/cmd_sync_test/resources/shaders/voxelVisualisation.vert b/projects/cmd_sync_test/resources/shaders/voxelVisualisation.vert
new file mode 100644
index 00000000..270b9ac5
--- /dev/null
+++ b/projects/cmd_sync_test/resources/shaders/voxelVisualisation.vert
@@ -0,0 +1,30 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) out float passCubeHalf;
+
+layout( push_constant ) uniform constants{
+    mat4 viewProjection;
+};
+
+layout(set=0, binding=0, r8) uniform image3D  voxelImage;
+layout(set=0, binding=1) uniform voxelizationInfo{
+    float extent;
+} voxelInfo;
+
+
+void main()	{
+    passCubeHalf        = voxelInfo.extent / float(imageSize(voxelImage).x) * 0.5f;
+    int voxelResolution = imageSize(voxelImage).x;
+    int slicePixelCount = voxelResolution * voxelResolution;
+    int z               = gl_VertexIndex / slicePixelCount;
+    int index2D         = gl_VertexIndex % slicePixelCount;
+    int y               = index2D / voxelResolution;
+    int x               = index2D % voxelResolution;
+    vec3 position       = (vec3(x, y, z) / voxelResolution - 0.5) * voxelInfo.extent + passCubeHalf;
+	gl_Position         = vec4(position, 1.0);
+    
+    if(imageLoad(voxelImage, ivec3(x,y,z)).x == 0){
+        gl_Position.x /= 0; // clip
+    }
+}
\ No newline at end of file
diff --git a/projects/cmd_sync_test/resources/shaders/voxelVisualisation_frag.spv b/projects/cmd_sync_test/resources/shaders/voxelVisualisation_frag.spv
new file mode 100644
index 0000000000000000000000000000000000000000..0d182806527c32ddab5e234ecf03929ae26277e6
GIT binary patch
literal 484
zcmZQ(Qf6mhU}WH8;AP-tfB-=TCI&_Z1_o{hHZbk(6YQf`T#}+^Vrl?V!N<T1qQG+e
z4D1Xn3=CkLo0ypglHdgEWnd6sU<Qk`GcYhPGcYqSF)%P3Vg#w>WN`P1cMNihFHS8;
zEJ`d%jW5nfOi3+@&rixqO)e=0so?^targIk_i>F+E+~jEE~(5(jnB!<ON~#-EJ{r-
z$t+6+spCLWmzkHGQ<{RTfR%v->;{<ISQ%IuK<>*gEpg7z$u9!A7o>)pfq@}24<h8w
zzyeky2a;r9U;yz!@-V(I0~6RUieO(cfZ1XU%nWP{3=9ek3=HB7EDRt&gZQA3Qea?V
z-~fjK16bS|$_KdvCeOtHiUou`h!3(86c1($tYAAp;S6#TNY4)jCI(gp1_oQGT|Ce*
Uw_{)iyB);;%fQHB!NAA>0NYD9ZvX%Q

literal 0
HcmV?d00001

diff --git a/projects/cmd_sync_test/resources/shaders/voxelVisualisation_geom.spv b/projects/cmd_sync_test/resources/shaders/voxelVisualisation_geom.spv
new file mode 100644
index 0000000000000000000000000000000000000000..6a804b752393f0d84c0287580c50c71bb9413492
GIT binary patch
literal 9608
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

literal 0
HcmV?d00001

diff --git a/projects/cmd_sync_test/resources/shaders/voxelVisualisation_vert.spv b/projects/cmd_sync_test/resources/shaders/voxelVisualisation_vert.spv
new file mode 100644
index 0000000000000000000000000000000000000000..52a320bb31926efffb96e7384f76836ca1690dec
GIT binary patch
literal 3072
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

literal 0
HcmV?d00001

diff --git a/projects/cmd_sync_test/resources/shaders/voxelization.geom b/projects/cmd_sync_test/resources/shaders/voxelization.geom
index 1546a75d..23084853 100644
--- a/projects/cmd_sync_test/resources/shaders/voxelization.geom
+++ b/projects/cmd_sync_test/resources/shaders/voxelization.geom
@@ -24,7 +24,8 @@ void main()	{
         else{
             gl_Position = gl_in[i].gl_Position.xzyw;
         }
-        passPos = passPosIn[i].xzy;
+        gl_Position.z = gl_Position.z * 0.5 + 0.5;  // xyz are kept in NDC range [-1, 1] so swizzling works, but vulkan needs final z in range [0, 1]
+        passPos = passPosIn[i];
         EmitVertex();
     }
     EndPrimitive();
diff --git a/projects/cmd_sync_test/resources/shaders/voxelization_geom.spv b/projects/cmd_sync_test/resources/shaders/voxelization_geom.spv
index abf747e5ff0d15043e493e01d08972e04df6f993..df9871889f968450232a4b8355c9493e50de76ee 100644
GIT binary patch
delta 316
zcmX>hwnv<onMs+Qfq{{Mi-DIRe<E)%W9G(0aVEyh$(2mk88bHrGRrgRvN13)Xfv=d
zfanwkFtAr(U}4B$U|?WoU|?WpU|_I@@-sKrvU0Q5J2J2_Br-5CBrz~BI5RLX2rw`(
z=rAxa*g}m-hRU@vu!4<EWnf?ciGhr7go>pxFff3`L1HlT($UPbV_;?giGkFD%;slc
zV8~)%Fk(<*U|?`$U}X?wU|`5*U|`^bNPryxa&Hb)9ArQy0|P_O<fmNns<{jdV4V#N
ctPCKd@)#HxKw=^c3=ANZAXD=uYjVp20P6N21poj5

delta 199
zcmdlZenO0wnMs+Qfq{{Mi-DIRb0TjrW75V%aVEy3$(2mk8Iv{#GRrgZvNJF+STnFN
zBr-5CByFC{%FP;U%fQ0G&%nTt%)r2)#K6Gd$iT`V$iTpm!oa}52NegY0clNziu*Hg
zFo5`J3=9k)F%V{8U|?Wk06Bnx!H$8MA&G&3A#HLbx4ddP)Z_*RR<KPO3=9k)F%brc
NN|3D?ljm~F0{}PE7Nr0H

diff --git a/projects/cmd_sync_test/src/main.cpp b/projects/cmd_sync_test/src/main.cpp
index 3fb53692..894e4a8a 100644
--- a/projects/cmd_sync_test/src/main.cpp
+++ b/projects/cmd_sync_test/src/main.cpp
@@ -21,6 +21,7 @@ int main(int argc, const char** argv) {
 	vkcv::CameraManager cameraManager(window, windowWidth, windowHeight);
 	cameraManager.getCamera().setPosition(glm::vec3(0.f, 0.f, 3.f));
 	cameraManager.getCamera().setNearFar(0.1, 30);
+    cameraManager.getCamera().setSpeed(25.f);
 
 	window.initEvents();
 
@@ -83,10 +84,11 @@ int main(int argc, const char** argv) {
 		core.getSwapchainImageFormat()
 	);
 	
+	const vk::Format depthBufferFormat = vk::Format::eD32Sfloat;
 	const vkcv::AttachmentDescription depth_attachment(
-			vkcv::AttachmentOperation::STORE,
-			vkcv::AttachmentOperation::CLEAR,
-			vk::Format::eD32Sfloat
+		vkcv::AttachmentOperation::STORE,
+		vkcv::AttachmentOperation::CLEAR,
+		depthBufferFormat
 	);
 
 	vkcv::PassConfig trianglePassDefinition({ present_color_attachment, depth_attachment });
@@ -237,6 +239,64 @@ int main(int argc, const char** argv) {
 	voxelizationDescriptorWrites.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(1, voxelizationInfoBuffer.getHandle()) };
 	core.writeDescriptorSet(voxelizationDescriptorSet, voxelizationDescriptorWrites);
 
+	const size_t voxelCount = voxelResolution * voxelResolution * voxelResolution;
+
+	vkcv::ShaderProgram voxelVisualisationShader;
+	voxelVisualisationShader.addShader(vkcv::ShaderStage::VERTEX, "resources/shaders/voxelVisualisation_vert.spv");
+	voxelVisualisationShader.addShader(vkcv::ShaderStage::GEOMETRY, "resources/shaders/voxelVisualisation_geom.spv");
+	voxelVisualisationShader.addShader(vkcv::ShaderStage::FRAGMENT, "resources/shaders/voxelVisualisation_frag.spv");
+	voxelVisualisationShader.reflectShader(vkcv::ShaderStage::VERTEX);
+	voxelVisualisationShader.reflectShader(vkcv::ShaderStage::GEOMETRY);
+	voxelVisualisationShader.reflectShader(vkcv::ShaderStage::FRAGMENT);
+
+	const std::vector<vkcv::DescriptorBinding> voxelVisualisationDescriptorBindings = {
+		vkcv::DescriptorBinding(vkcv::DescriptorType::IMAGE_STORAGE,    1, vkcv::ShaderStage::VERTEX),
+		vkcv::DescriptorBinding(vkcv::DescriptorType::UNIFORM_BUFFER,   1, vkcv::ShaderStage::VERTEX) };
+	vkcv::DescriptorSetHandle voxelVisualisationDescriptorSet = core.createDescriptorSet(voxelVisualisationDescriptorBindings);
+
+	const vkcv::AttachmentDescription voxelVisualisationColorAttachments(
+		vkcv::AttachmentOperation::STORE,
+		vkcv::AttachmentOperation::LOAD,
+		core.getSwapchainImageFormat()
+	);
+
+	const vkcv::AttachmentDescription voxelVisualisationDepthAttachments(
+		vkcv::AttachmentOperation::STORE,
+		vkcv::AttachmentOperation::LOAD,
+		depthBufferFormat
+	);
+
+	vkcv::PassConfig voxelVisualisationPassDefinition({ voxelVisualisationColorAttachments, voxelVisualisationDepthAttachments });
+	vkcv::PassHandle voxelVisualisationPass = core.createPass(voxelVisualisationPassDefinition);
+
+	const vkcv::PipelineConfig voxelVisualisationPipeConfig(
+		voxelVisualisationShader, 
+		0, 
+		0, 
+		voxelVisualisationPass,
+		{}, 
+		{ core.getDescriptorSet(voxelVisualisationDescriptorSet).layout },
+		true,
+		false,
+		vkcv::PrimitiveTopology::PointList);	// points are extended to cubes in the geometry shader
+	const vkcv::PipelineHandle voxelVisualisationPipe = core.createGraphicsPipeline(voxelVisualisationPipeConfig);
+
+	vkcv::Buffer<uint16_t> voxelVisualisationIndexBuffer = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, voxelCount);
+	std::vector<uint16_t> voxelIndexData;
+	for (int i = 0; i < voxelCount; i++) {
+		voxelIndexData.push_back(i);
+	}
+
+	vkcv::DescriptorWrites voxelVisualisationDescriptorWrite;
+	voxelVisualisationDescriptorWrite.storageImageWrites  = { vkcv::StorageImageDescriptorWrite(0,  voxelImage.getHandle()) };
+	voxelVisualisationDescriptorWrite.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(1, voxelizationInfoBuffer.getHandle()) };
+	core.writeDescriptorSet(voxelVisualisationDescriptorSet, voxelVisualisationDescriptorWrite);
+
+	voxelVisualisationIndexBuffer.fill(voxelIndexData);
+	const vkcv::DrawcallInfo voxelVisualisationDrawcall(
+		vkcv::Mesh({}, voxelVisualisationIndexBuffer.getVulkanHandle(), voxelCount),
+		{ vkcv::DescriptorSetUsage(0, core.getDescriptorSet(voxelVisualisationDescriptorSet).vulkanHandle) });
+
 	const std::vector<glm::vec3> instancePositions = {
 		glm::vec3(0.f, -2.f, 0.f),
 		glm::vec3(3.f,  0.f, 0.f),
@@ -264,6 +324,13 @@ int main(int argc, const char** argv) {
 	std::vector<glm::mat4> mvpLight;
 	std::vector<std::array<glm::mat4, 2>> voxelizationMatrices;
 
+	bool renderVoxelVis = false;
+	window.e_key.add([&renderVoxelVis](int key ,int scancode, int action, int mods) {
+		if (key == GLFW_KEY_V && action == GLFW_PRESS) {
+			renderVoxelVis = !renderVoxelVis;
+		}
+	});
+
 	auto start = std::chrono::system_clock::now();
 	const auto appStartTime = start;
 	while (window.isWindowOpen()) {
@@ -275,7 +342,7 @@ int main(int argc, const char** argv) {
 		}
 		
 		if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) {
-			depthBuffer = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight).getHandle();
+			depthBuffer = core.createImage(depthBufferFormat, swapchainWidth, swapchainHeight).getHandle();
 			
 			windowWidth = swapchainWidth;
 			windowHeight = swapchainHeight;
@@ -311,7 +378,7 @@ 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(
+		const glm::mat4 voxelizationProjection = glm::ortho(
 			-voxelizationHalfExtent,
 			 voxelizationHalfExtent,
 			-voxelizationHalfExtent,
@@ -362,6 +429,20 @@ int main(int argc, const char** argv) {
 			pushConstantData,
 			drawcalls,
 			renderTargets);
+
+		if (renderVoxelVis) {
+			const vkcv::PushConstantData voxelVisualisationPushConstantData((void*)&viewProjectionCamera, sizeof(glm::mat4));
+
+			core.recordImageMemoryBarrier(cmdStream, voxelImage.getHandle());
+			core.recordDrawcallsToCmdStream(
+				cmdStream,
+				voxelVisualisationPass,
+				voxelVisualisationPipe,
+				voxelVisualisationPushConstantData,
+				{ voxelVisualisationDrawcall },
+				renderTargets);
+		}
+
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
 
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index b7fc5aa9..0d3bf032 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -470,4 +470,10 @@ namespace vkcv
 			m_ImageManager->recordImageLayoutTransition(image, vk::ImageLayout::eGeneral, cmdBuffer);
 		}, nullptr);
 	}
+
+	void Core::recordImageMemoryBarrier(const CommandStreamHandle cmdStream, const ImageHandle image) {
+		recordCommandsToStream(cmdStream, [image, this](const vk::CommandBuffer cmdBuffer) {
+			m_ImageManager->recordImageMemoryBarrier(image, cmdBuffer);
+		}, nullptr);
+	}
 }
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index 80849375..cb0ebc73 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -295,6 +295,22 @@ namespace vkcv {
 		recordImageBarrier(cmdBuffer, transitionBarrier);
 		image.m_layout = newLayout;
 	}
+
+	void ImageManager::recordImageMemoryBarrier(
+		const ImageHandle& handle,
+		vk::CommandBuffer cmdBuffer) {
+
+		const uint64_t id = handle.getId();
+
+		if (id >= m_images.size()) {
+			std::cerr << "Error: ImageManager::recordImageMemoryBarrier invalid handle" << std::endl;
+			return;
+		}
+
+		auto& image = m_images[id];
+		const auto transitionBarrier = createImageLayoutTransitionBarrier(image, image.m_layout);
+		recordImageBarrier(cmdBuffer, transitionBarrier);
+	}
 	
 	void ImageManager::fillImage(const ImageHandle& handle, void* data, size_t size)
 	{
diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp
index 316ad231..6b3bea33 100644
--- a/src/vkcv/ImageManager.hpp
+++ b/src/vkcv/ImageManager.hpp
@@ -86,6 +86,10 @@ namespace vkcv {
 			vk::ImageLayout newLayout, 
 			vk::CommandBuffer cmdBuffer);
 
+		void recordImageMemoryBarrier(
+			const ImageHandle& handle,
+			vk::CommandBuffer cmdBuffer);
+
 		void fillImage(const ImageHandle& handle, void* data, size_t size);
 		
 		[[nodiscard]]
diff --git a/src/vkcv/PipelineConfig.cpp b/src/vkcv/PipelineConfig.cpp
index 306e4685..46911736 100644
--- a/src/vkcv/PipelineConfig.cpp
+++ b/src/vkcv/PipelineConfig.cpp
@@ -16,7 +16,8 @@ namespace vkcv {
 		const std::vector<VertexAttribute>          &vertexAttributes,
 		const std::vector<vk::DescriptorSetLayout>  &descriptorLayouts,
 		bool                                        useDynamicViewport,
-		bool                                        useConservativeRasterization)
+		bool                                        useConservativeRasterization,
+        PrimitiveTopology                           primitiveTopology)
 		:
 		m_ShaderProgram(shaderProgram),
 		m_Height(height),
@@ -25,6 +26,7 @@ namespace vkcv {
 		m_VertexAttributes(vertexAttributes),
 		m_DescriptorLayouts(descriptorLayouts),
 		m_UseDynamicViewport(useDynamicViewport),
-		m_UseConservativeRasterization(useConservativeRasterization)
+		m_UseConservativeRasterization(useConservativeRasterization),
+        m_PrimitiveTopology(primitiveTopology)
 		{}
 }
diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp
index 5c7e830e..1971222a 100644
--- a/src/vkcv/PipelineManager.cpp
+++ b/src/vkcv/PipelineManager.cpp
@@ -32,6 +32,15 @@ namespace vkcv
 		}
 	}
 
+    vk::PrimitiveTopology primitiveTopologyToVulkanPrimitiveTopology(const PrimitiveTopology topology) {
+        switch (topology) {
+        case(PrimitiveTopology::PointList):     return vk::PrimitiveTopology::ePointList;
+        case(PrimitiveTopology::LineList):      return vk::PrimitiveTopology::eLineList;
+        case(PrimitiveTopology::TriangleList):  return vk::PrimitiveTopology::eTriangleList;
+        default: std::cout << "Error: Unknown primitive topology type" << std::endl; return vk::PrimitiveTopology::eTriangleList;
+        }
+    }
+
     PipelineHandle PipelineManager::createPipeline(const PipelineConfig &config, PassManager& passManager)
     {
 		const vk::RenderPass &pass = passManager.getVkPass(config.m_PassHandle);
@@ -114,9 +123,9 @@ namespace vkcv
 
         // input assembly state
         vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(
-                {},
-                vk::PrimitiveTopology::eTriangleList,
-                false
+            {},
+            primitiveTopologyToVulkanPrimitiveTopology(config.m_PrimitiveTopology),
+            false
         );
 
         // viewport state
-- 
GitLab