From 9f3c40232afb7877fc6a09ee4ed965d2f0d9e021 Mon Sep 17 00:00:00 2001
From: Tobias Frisch <tfrisch@uni-koblenz.de>
Date: Fri, 27 Jan 2023 23:12:29 +0100
Subject: [PATCH] Adjust mesh shader example to use EXT extension

Signed-off-by: Tobias Frisch <tfrisch@uni-koblenz.de>
---
 .../mesh_shader/assets/shaders/common.inc     |   2 +-
 .../mesh_shader/assets/shaders/meshlet.inc    |  22 +--
 .../mesh_shader/assets/shaders/shader.frag    |  15 +-
 .../mesh_shader/assets/shaders/shader.mesh    |  32 ++---
 .../mesh_shader/assets/shaders/shader.task    |  37 +++--
 .../mesh_shader/assets/shaders/shader.vert    |  18 ++-
 projects/mesh_shader/src/main.cpp             |  35 +++--
 src/vkcv/GraphicsPipelineManager.cpp          | 133 +++++++++++++-----
 8 files changed, 177 insertions(+), 117 deletions(-)

diff --git a/projects/mesh_shader/assets/shaders/common.inc b/projects/mesh_shader/assets/shaders/common.inc
index 280ffee2..962a814b 100644
--- a/projects/mesh_shader/assets/shaders/common.inc
+++ b/projects/mesh_shader/assets/shaders/common.inc
@@ -1,4 +1,4 @@
-struct ObjectMatrices{
+struct ObjectMatrices {
     mat4 model;
     mat4 mvp;
 };
\ No newline at end of file
diff --git a/projects/mesh_shader/assets/shaders/meshlet.inc b/projects/mesh_shader/assets/shaders/meshlet.inc
index 0594f62c..f79a0875 100644
--- a/projects/mesh_shader/assets/shaders/meshlet.inc
+++ b/projects/mesh_shader/assets/shaders/meshlet.inc
@@ -1,8 +1,14 @@
-struct Meshlet{
-    uint    vertexOffset;
-    uint    vertexCount;
-    uint    indexOffset;
-    uint    indexCount;
-    vec3    meanPosition;
-    float   boundingSphereRadius;
-};
\ No newline at end of file
+
+struct Task {
+    uint meshletIndices[32];
+    mat4 mvp;
+};
+
+struct Meshlet {
+    uint  vertexOffset;
+    uint  vertexCount;
+    uint  indexOffset;
+    uint  indexCount;
+    vec3  meanPosition;
+    float boundingSphereRadius;
+};
diff --git a/projects/mesh_shader/assets/shaders/shader.frag b/projects/mesh_shader/assets/shaders/shader.frag
index f4f6982f..399a680d 100644
--- a/projects/mesh_shader/assets/shaders/shader.frag
+++ b/projects/mesh_shader/assets/shaders/shader.frag
@@ -1,12 +1,12 @@
 #version 450
 #extension GL_ARB_separate_shader_objects : enable
 
-layout(location = 0) in  vec3 passNormal;
-layout(location = 1) in  flat uint passTaskIndex;
+layout(location = 0) in vec3 passNormal;
+layout(location = 1) in flat uint passTaskIndex;
+
 layout(location = 0) out vec3 outColor;
 
-uint lowbias32(uint x)
-{
+uint lowbias32(uint x) {
     x ^= x >> 16;
     x *= 0x7feb352dU;
     x ^= x >> 15;
@@ -15,15 +15,16 @@ uint lowbias32(uint x)
     return x;
 }
 
-float hashToFloat(uint hash){
+float hashToFloat(uint hash) {
     return (hash % 255) / 255.f;
 }
 
-vec3 colorFromIndex(uint i){
+vec3 colorFromIndex(uint i) {
     return vec3(
         hashToFloat(lowbias32(i+0)),
         hashToFloat(lowbias32(i+1)),
-        hashToFloat(lowbias32(i+2)));
+        hashToFloat(lowbias32(i+2))
+    );
 }
 
 void main() {
diff --git a/projects/mesh_shader/assets/shaders/shader.mesh b/projects/mesh_shader/assets/shaders/shader.mesh
index a6b0cb62..0d97389b 100644
--- a/projects/mesh_shader/assets/shaders/shader.mesh
+++ b/projects/mesh_shader/assets/shaders/shader.mesh
@@ -13,32 +13,25 @@ layout(max_vertices=64, max_primitives=126) out;
 layout(location = 0) out vec3 passNormal[];
 layout(location = 1) out uint passTaskIndex[];
 
-struct Vertex
-{
-    vec3 position;  float padding0;
-    vec3 normal;    float padding1;
+struct Vertex {
+    vec3  position;
+    float padding0;
+    vec3  normal;
+    float padding1;
 };
 
-layout(std430, binding = 0) readonly buffer vertexBuffer
-{
+layout(std430, binding = 0) readonly buffer vertexBuffer {
     Vertex vertices[];
 };
 
-layout(std430, binding = 1) readonly buffer indexBuffer
-{
+layout(std430, binding = 1) readonly buffer indexBuffer {
     uint localIndices[]; // breaks for 16 bit indices
 };
 
-layout(std430, binding = 2) readonly buffer meshletBuffer
-{
+layout(std430, binding = 2) readonly buffer meshletBuffer {
     Meshlet meshlets[];
 };
 
-struct Task {
-    uint meshletIndices[32];
-    mat4 mvp;
-};
-
 taskPayloadSharedEXT Task IN;
 
 void main()	{
@@ -46,17 +39,16 @@ void main()	{
     Meshlet meshlet = meshlets[meshletIndex];
     
     // set vertices
-    for(uint i = 0; i < 2; i++){
-    
+    for (uint i = 0; i < 2; i++) {
         uint workIndex = gl_LocalInvocationID.x + 32 * i;
-        if(workIndex >= meshlet.vertexCount){
+        if (workIndex >= meshlet.vertexCount) {
             break;
         }
     
         uint vertexIndex    = meshlet.vertexOffset + workIndex;
         Vertex vertex       = vertices[vertexIndex];
     
-        gl_MeshVerticesEXT[workIndex].gl_Position    = IN.mvp * vec4(vertex.position, 1);
+        gl_MeshVerticesEXT[workIndex].gl_Position   = IN.mvp * vec4(vertex.position, 1);
         passNormal[workIndex]                       = vertex.normal;
         passTaskIndex[workIndex]                    = meshletIndex;
     }
@@ -64,7 +56,7 @@ void main()	{
     // set local indices
     for (uint i = 0; i < 12; i++) {
         uint workIndex = gl_LocalInvocationID.x + i * 32;
-        if(workIndex >= meshlet.indexCount){
+        if (workIndex >= meshlet.indexCount) {
             break;
         }
         
diff --git a/projects/mesh_shader/assets/shaders/shader.task b/projects/mesh_shader/assets/shaders/shader.task
index a6b1ad61..be09706c 100644
--- a/projects/mesh_shader/assets/shaders/shader.task
+++ b/projects/mesh_shader/assets/shaders/shader.task
@@ -8,37 +8,30 @@
 
 layout(local_size_x=32, local_size_y=1, local_size_z=1) in;
 
-struct Task {
-    uint meshletIndices[32];
-    mat4 mvp;
-};
-
 taskPayloadSharedEXT Task OUT;
 
-layout( push_constant ) uniform constants{
-    uint matrixIndex;
+layout( push_constant ) uniform constants {
     uint meshletCount;
+    uint matrixIndex;
 };
 
 // TODO: reuse mesh stage binding at location 2 after required fix in framework
-layout(std430, binding = 5) readonly buffer meshletBuffer
-{
+layout(std430, binding = 5) readonly buffer meshletBuffer {
     Meshlet meshlets[];
 };
 
-struct Plane{
-    vec3    pointOnPlane;
-    float   padding0;
-    vec3    normal;
-    float   padding1;
+struct Plane {
+    vec3  pointOnPlane;
+    float padding0;
+    vec3  normal;
+    float padding1;
 };
 
-layout(set=0, binding=3, std140) uniform cameraPlaneBuffer{
+layout(set=0, binding = 3, std140) uniform cameraPlaneBuffer {
     Plane cameraPlanes[6];
 };
 
-layout(std430, binding = 4) readonly buffer matrixBuffer
-{
+layout(std430, binding = 4) readonly buffer matrixBuffer {
     ObjectMatrices objectMatrices[];
 };
 
@@ -46,9 +39,9 @@ shared uint taskCount;
 
 bool isSphereInsideFrustum(vec3 spherePos, float sphereRadius, Plane cameraPlanes[6]){
     bool isInside = true;
-    for(int i = 0; i < 6; i++){
-        Plane p     = cameraPlanes[i];
-        isInside    = isInside && dot(p.normal, spherePos - p.pointOnPlane) - sphereRadius < 0;
+    for (int i = 0; i < 6; i++) {
+        Plane p  = cameraPlanes[i];
+        isInside = isInside && dot(p.normal, spherePos - p.pointOnPlane) - sphereRadius < 0;
     }
     return isInside;
 }
@@ -64,6 +57,8 @@ void main() {
     if (gl_LocalInvocationID.x == 0) {
         taskCount = 0;
     }
+
+    memoryBarrierShared();
     
     // TODO: scaling support
     vec3 meshletPositionWorld = (vec4(meshlet.meanPosition, 1) * objectMatrices[matrixIndex].model).xyz;
@@ -72,6 +67,8 @@ void main() {
         OUT.meshletIndices[outIndex] = gl_GlobalInvocationID.x;
     }
 
+    memoryBarrierShared();
+
     if (gl_LocalInvocationID.x == 0) {
         OUT.mvp = objectMatrices[matrixIndex].mvp;
         EmitMeshTasksEXT(taskCount, 1, 1);
diff --git a/projects/mesh_shader/assets/shaders/shader.vert b/projects/mesh_shader/assets/shaders/shader.vert
index fca50579..0da4f058 100644
--- a/projects/mesh_shader/assets/shaders/shader.vert
+++ b/projects/mesh_shader/assets/shaders/shader.vert
@@ -8,22 +8,20 @@ layout(location = 0) in vec3 inPosition;
 layout(location = 1) in vec3 inNormal;
 
 layout(location = 0) out vec3 passNormal;
-layout(location = 1) out uint dummyOutput;
+layout(location = 1) out flat uint passTaskIndex;
 
-layout(std430, binding = 0) readonly buffer matrixBuffer
-{
+layout(std430, binding = 0) readonly buffer matrixBuffer {
     ObjectMatrices objectMatrices[];
 };
 
-layout( push_constant ) uniform constants{
-    uint matrixIndex;
+layout( push_constant ) uniform constants {
     uint padding; // pad to same size as mesh shader constants
+    uint matrixIndex;
 };
 
-
 void main()	{
-	gl_Position = objectMatrices[matrixIndex].mvp * vec4(inPosition, 1.0);
-	passNormal  = inNormal;
-    
-    dummyOutput = padding * 0;  // padding must be used, else compiler shrinks constant size
+	passNormal = inNormal;
+    passTaskIndex = 0;
+
+    gl_Position = objectMatrices[matrixIndex].mvp * vec4(inPosition, 1.0);
 }
\ No newline at end of file
diff --git a/projects/mesh_shader/src/main.cpp b/projects/mesh_shader/src/main.cpp
index 8afbbb26..23d8b028 100644
--- a/projects/mesh_shader/src/main.cpp
+++ b/projects/mesh_shader/src/main.cpp
@@ -94,8 +94,10 @@ int main(int argc, const char** argv) {
 		applicationName,
 		VK_MAKE_VERSION(0, 0, 1),
 		{ vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute },
-		features
+		features,
+		{ VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME }
 	);
+	
 	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, 1280, 720, true);
 	vkcv::Window &window = core.getWindow(windowHandle);
 
@@ -106,7 +108,6 @@ int main(int argc, const char** argv) {
     vkcv::asset::loadScene(path, mesh);
 
     assert(!mesh.vertexGroups.empty());
-
     auto vertexBuffer = vkcv::buffer<uint8_t>(
 			core,
             vkcv::BufferType::VERTEX,
@@ -199,7 +200,7 @@ int main(int argc, const char** argv) {
 	}
 
 	vkcv::ShaderProgram bunnyShaderProgram{};
-	vkcv::shader::GLSLCompiler compiler (vkcv::shader::GLSLCompileTarget::MESH_SHADING);
+	vkcv::shader::GLSLCompiler compiler;
 	
 	compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("assets/shaders/shader.vert"),
 					 [&bunnyShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
@@ -249,17 +250,19 @@ int main(int argc, const char** argv) {
 
 	// mesh shader
 	vkcv::ShaderProgram meshShaderProgram;
-	compiler.compile(vkcv::ShaderStage::TASK, std::filesystem::path("assets/shaders/shader.task"),
+	vkcv::shader::GLSLCompiler mesh_compiler (vkcv::shader::GLSLCompileTarget::MESH_SHADING);
+	
+	mesh_compiler.compile(vkcv::ShaderStage::TASK, std::filesystem::path("assets/shaders/shader.task"),
 		[&meshShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
 		meshShaderProgram.addShader(shaderStage, path);
 	});
-
-	compiler.compile(vkcv::ShaderStage::MESH, std::filesystem::path("assets/shaders/shader.mesh"),
+	
+	mesh_compiler.compile(vkcv::ShaderStage::MESH, std::filesystem::path("assets/shaders/shader.mesh"),
 		[&meshShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
 		meshShaderProgram.addShader(shaderStage, path);
 	});
-
-	compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("assets/shaders/shader.frag"),
+	
+	mesh_compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("assets/shaders/shader.frag"),
 		[&meshShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
 		meshShaderProgram.addShader(shaderStage, path);
 	});
@@ -343,11 +346,15 @@ int main(int argc, const char** argv) {
 
 		matrixBuffer.fill({ objectMatrices });
 
-		struct PushConstants {
-			uint32_t matrixIndex;
+		struct MeshletPushConstants {
 			uint32_t meshletCount;
+			uint32_t matrixIndex;
+		};
+		
+		MeshletPushConstants pushConstants {
+			static_cast<uint32_t>(meshShaderModelData.meshlets.size()),
+			0
 		};
-		PushConstants pushConstants{ 0, static_cast<uint32_t>(meshShaderModelData.meshlets.size()) };
 
 		if (updateFrustumPlanes) {
 			const CameraPlanes cameraPlanes = computeCameraPlanes(camera);
@@ -357,7 +364,7 @@ int main(int argc, const char** argv) {
 		const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
-		vkcv::PushConstants pushConstantData = vkcv::pushConstants<PushConstants>();
+		vkcv::PushConstants pushConstantData = vkcv::pushConstants<MeshletPushConstants>();
 		pushConstantData.appendDrawcall(pushConstants);
 
 		if (useMeshShader) {
@@ -378,14 +385,14 @@ int main(int argc, const char** argv) {
 			vkcv::InstanceDrawcall drawcall (vertexData);
 			drawcall.useDescriptorSet(0, vertexShaderDescriptorSet);
 
-			core.recordDrawcallsToCmdStream(
+			/*core.recordDrawcallsToCmdStream(
 				cmdStream,
 				bunnyPipeline,
 				pushConstantData,
 				{ drawcall },
 				{ renderTargets },
 				windowHandle
-			);
+			);*/
 		}
 
 		core.prepareSwapchainImageForPresent(cmdStream);
diff --git a/src/vkcv/GraphicsPipelineManager.cpp b/src/vkcv/GraphicsPipelineManager.cpp
index 8747a5e9..767616f7 100644
--- a/src/vkcv/GraphicsPipelineManager.cpp
+++ b/src/vkcv/GraphicsPipelineManager.cpp
@@ -132,17 +132,24 @@ namespace vkcv {
 
 		assert(outCreateInfo);
 		std::vector<uint32_t> code = shaderProgram.getShaderBinary(stage);
-		vk::ShaderModuleCreateInfo vertexModuleInfo({}, code.size() * sizeof(uint32_t),
-													code.data());
+		vk::ShaderModuleCreateInfo vertexModuleInfo(
+				{},
+				code.size() * sizeof(uint32_t),
+				code.data()
+		);
 		vk::ShaderModule shaderModule;
 		if (device.createShaderModule(&vertexModuleInfo, nullptr, &shaderModule)
 			!= vk::Result::eSuccess)
 			return false;
 
 		const static auto entryName = "main";
-
-		*outCreateInfo = vk::PipelineShaderStageCreateInfo({}, shaderStageToVkShaderStage(stage),
-														   shaderModule, entryName, nullptr);
+		*outCreateInfo = vk::PipelineShaderStageCreateInfo(
+				vk::PipelineShaderStageCreateFlags(),
+				shaderStageToVkShaderStage(stage),
+				shaderModule,
+				entryName,
+				nullptr
+		);
 		return true;
 	}
 
@@ -157,7 +164,8 @@ namespace vkcv {
 	static void fillVertexInputDescription(
 		std::vector<vk::VertexInputAttributeDescription> &vertexAttributeDescriptions,
 		std::vector<vk::VertexInputBindingDescription> &vertexBindingDescriptions,
-		const bool existsVertexShader, const GraphicsPipelineConfig &config) {
+		const bool existsVertexShader,
+		const GraphicsPipelineConfig &config) {
 
 		if (existsVertexShader) {
 			const VertexLayout &layout = config.getVertexLayout();
@@ -214,7 +222,9 @@ namespace vkcv {
 	static vk::PipelineTessellationStateCreateInfo
 	createPipelineTessellationStateCreateInfo(const GraphicsPipelineConfig &config) {
 		vk::PipelineTessellationStateCreateInfo pipelineTessellationStateCreateInfo(
-			{}, config.getTesselationControlPoints());
+			{},
+			config.getTesselationControlPoints()
+		);
 
 		return pipelineTessellationStateCreateInfo;
 	}
@@ -235,8 +245,13 @@ namespace vkcv {
 
 		scissor = vk::Rect2D({ 0, 0 }, { config.getWidth(), config.getHeight() });
 
-		vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo({}, 1, &viewport, 1,
-																			&scissor);
+		vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(
+				{},
+				1,
+				&viewport,
+				1,
+				&scissor
+		);
 
 		return pipelineViewportStateCreateInfo;
 	}
@@ -276,8 +291,18 @@ namespace vkcv {
 		}
 
 		vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo(
-			{}, config.isDepthClampingEnabled(), false, vk::PolygonMode::eFill, cullMode,
-			vk::FrontFace::eCounterClockwise, false, 0.f, 0.f, 0.f, 1.f);
+			{},
+			config.isDepthClampingEnabled(),
+			false,
+			vk::PolygonMode::eFill,
+			cullMode,
+			vk::FrontFace::eCounterClockwise,
+			false,
+			0.f,
+			0.f,
+			0.f,
+			1.f
+		);
 
 		static vk::PipelineRasterizationConservativeStateCreateInfoEXT conservativeRasterization;
 
@@ -376,11 +401,17 @@ namespace vkcv {
 		static vk::PushConstantRange pushConstantRange;
 
 		const size_t pushConstantsSize = config.getShaderProgram().getPushConstantsSize();
-		pushConstantRange =
-			vk::PushConstantRange(vk::ShaderStageFlagBits::eAll, 0, pushConstantsSize);
+		pushConstantRange = vk::PushConstantRange(
+				vk::ShaderStageFlagBits::eAll,
+				0,
+				pushConstantsSize
+		);
 
-		vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo({}, (descriptorSetLayouts),
-															  (pushConstantRange));
+		vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo (
+				vk::PipelineLayoutCreateFlags(),
+				descriptorSetLayouts,
+				pushConstantRange
+		);
 
 		if (pushConstantsSize == 0) {
 			pipelineLayoutCreateInfo.pushConstantRangeCount = 0;
@@ -397,9 +428,17 @@ namespace vkcv {
 	static vk::PipelineDepthStencilStateCreateInfo
 	createPipelineDepthStencilStateCreateInfo(const GraphicsPipelineConfig &config) {
 		const vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilCreateInfo(
-			vk::PipelineDepthStencilStateCreateFlags(), config.getDepthTest() != DepthTest::None,
-			config.isWritingDepth(), depthTestToVkCompareOp(config.getDepthTest()), false, false,
-			{}, {}, 0.0f, 1.0f);
+			vk::PipelineDepthStencilStateCreateFlags(),
+			config.getDepthTest() != DepthTest::None,
+			config.isWritingDepth(),
+			depthTestToVkCompareOp(config.getDepthTest()),
+			false,
+			false,
+			{},
+			{},
+			0.0f,
+			1.0f
+		);
 
 		return pipelineDepthStencilCreateInfo;
 	}
@@ -449,7 +488,7 @@ namespace vkcv {
 			 || (existsTaskShader && existsMeshShader));
 
 		if (!validGeometryStages) {
-			vkcv_log(LogLevel::ERROR, "Requires vertex or task and mesh shader");
+			vkcv_log(LogLevel::ERROR, "Requires a valid geometry shader stage");
 			return {};
 		}
 
@@ -613,13 +652,16 @@ namespace vkcv {
 		}
 
 		// pipeline layout
-		vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo =
-			createPipelineLayoutCreateInfo(config, descriptorSetLayouts);
+		vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = createPipelineLayoutCreateInfo(
+				config,
+				descriptorSetLayouts
+		);
 
-		vk::PipelineLayout vkPipelineLayout {};
-		if (getCore().getContext().getDevice().createPipelineLayout(&pipelineLayoutCreateInfo,
-																	nullptr, &vkPipelineLayout)
-			!= vk::Result::eSuccess) {
+		const auto& pipelineLayout = (
+				getCore().getContext().getDevice().createPipelineLayout(pipelineLayoutCreateInfo)
+		);
+		
+		if (!pipelineLayout) {
 			destroyShaderModules();
 			return {};
 		}
@@ -637,33 +679,50 @@ namespace vkcv {
 				break;
 			}
 		}
+		
+		const bool usesTesselation = (
+				existsTessellationControlShader &&
+				existsTessellationEvaluationShader
+		);
 
 		// Get all setting structs together and create the Pipeline
 		const vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo(
-			{}, static_cast<uint32_t>(shaderStages.size()), shaderStages.data(),
-			&pipelineVertexInputStateCreateInfo,
-			&pipelineInputAssemblyStateCreateInfo,
-			&pipelineTessellationStateCreateInfo,
+			{},
+			static_cast<uint32_t>(shaderStages.size()),
+			shaderStages.data(),
+			existsVertexShader? &pipelineVertexInputStateCreateInfo : nullptr,
+			existsVertexShader? &pipelineInputAssemblyStateCreateInfo : nullptr,
+			usesTesselation? &pipelineTessellationStateCreateInfo : nullptr,
 			&pipelineViewportStateCreateInfo,
 			&pipelineRasterizationStateCreateInfo,
 			&pipelineMultisampleStateCreateInfo,
 			p_depthStencilCreateInfo,
 			&pipelineColorBlendStateCreateInfo,
 			&dynamicStateCreateInfo,
-			vkPipelineLayout,
+			pipelineLayout,
 			pass,
 			0,
 			{},
 			0
 		);
-
-		vk::Pipeline vkPipeline {};
-		if (getCore().getContext().getDevice().createGraphicsPipelines(
-				nullptr, 1, &graphicsPipelineCreateInfo, nullptr, &vkPipeline)
-			!= vk::Result::eSuccess) {
+		
+		vkcv_log(LogLevel::RAW_INFO, "STAGES: %lu", shaderStages.size());
+		for (const auto& shaderStage : shaderStages) {
+			vkcv_log(LogLevel::RAW_INFO, "STAGE: %s %s %s",
+					 shaderStage.pName,
+					 vk::to_string(shaderStage.stage).c_str(),
+					 vk::to_string(shaderStage.flags).c_str()
+			 );
+		}
+		
+		auto pipelineResult = getCore().getContext().getDevice().createGraphicsPipeline(
+				nullptr, graphicsPipelineCreateInfo
+		);
+		
+		if (pipelineResult.result != vk::Result::eSuccess) {
 			// Catch runtime error if the creation of the pipeline fails.
 			// Destroy everything to keep the memory clean.
-			getCore().getContext().getDevice().destroy(vkPipelineLayout);
+			getCore().getContext().getDevice().destroy(pipelineLayout);
 			destroyShaderModules();
 			return {};
 		}
@@ -672,7 +731,7 @@ namespace vkcv {
 		destroyShaderModules();
 
 		// Hand over Handler to main Application
-		return add({ vkPipeline, vkPipelineLayout, config });
+		return add({ pipelineResult.value, pipelineLayout, config });
 	}
 
 	vk::Pipeline
-- 
GitLab