From fc5b7930f426d173cd9a248c9bcd972d3be53ef9 Mon Sep 17 00:00:00 2001
From: Tobias Frisch <tfrisch@uni-koblenz.de>
Date: Sat, 26 Nov 2022 22:53:05 +0100
Subject: [PATCH] Implement ray tracing pipeline inside of framework with own
 pipeline manager

Signed-off-by: Tobias Frisch <tfrisch@uni-koblenz.de>
---
 config/Sources.cmake                        |   6 +
 include/vkcv/BufferTypes.hpp                |   1 +
 include/vkcv/Context.hpp                    |   8 +
 include/vkcv/Core.hpp                       |  40 +-
 include/vkcv/GraphicsPipelineConfig.hpp     |   3 +-
 include/vkcv/Handles.hpp                    |  10 +
 include/vkcv/RayTracingPipelineConfig.hpp   |  31 ++
 projects/rtx_ambient_occlusion/src/main.cpp |  23 +-
 src/vkcv/BufferManager.cpp                  |   4 +
 src/vkcv/Context.cpp                        |  32 +-
 src/vkcv/Core.cpp                           |  92 +++-
 src/vkcv/GraphicsPipelineConfig.cpp         |   7 +-
 src/vkcv/GraphicsPipelineManager.cpp        |  37 +-
 src/vkcv/GraphicsPipelineManager.hpp        |   6 +-
 src/vkcv/RayTracingPipelineConfig.cpp       |  14 +
 src/vkcv/RayTracingPipelineManager.cpp      | 533 ++++++++++++++++++++
 src/vkcv/RayTracingPipelineManager.hpp      | 118 +++++
 17 files changed, 880 insertions(+), 85 deletions(-)
 create mode 100644 include/vkcv/RayTracingPipelineConfig.hpp
 create mode 100644 src/vkcv/RayTracingPipelineConfig.cpp
 create mode 100644 src/vkcv/RayTracingPipelineManager.cpp
 create mode 100644 src/vkcv/RayTracingPipelineManager.hpp

diff --git a/config/Sources.cmake b/config/Sources.cmake
index 1b3af31e..8c13596c 100644
--- a/config/Sources.cmake
+++ b/config/Sources.cmake
@@ -73,6 +73,9 @@ set(vkcv_sources
 
 		${vkcv_source}/vkcv/GraphicsPipelineManager.hpp
 		${vkcv_source}/vkcv/GraphicsPipelineManager.cpp
+		
+		${vkcv_source}/vkcv/RayTracingPipelineManager.hpp
+		${vkcv_source}/vkcv/RayTracingPipelineManager.cpp
         
         ${vkcv_include}/vkcv/QueueManager.hpp
         ${vkcv_source}/vkcv/QueueManager.cpp
@@ -140,6 +143,9 @@ set(vkcv_sources
 		${vkcv_source}/vkcv/VertexData.cpp
 		
 		${vkcv_include}/vkcv/Result.hpp
+		
+		${vkcv_include}/vkcv/RayTracingPipelineConfig.hpp
+		${vkcv_source}/vkcv/RayTracingPipelineConfig.cpp
 )
 
 if (BUILD_CLANG_FORMAT)
diff --git a/include/vkcv/BufferTypes.hpp b/include/vkcv/BufferTypes.hpp
index 85bc0f2a..78f2d167 100644
--- a/include/vkcv/BufferTypes.hpp
+++ b/include/vkcv/BufferTypes.hpp
@@ -17,6 +17,7 @@ namespace vkcv {
 		STORAGE,
 		STAGING,
 		INDIRECT,
+		SHADER_BINDING,
 
 		UNKNOWN
 	};
diff --git a/include/vkcv/Context.hpp b/include/vkcv/Context.hpp
index 9146c591..ce56589d 100644
--- a/include/vkcv/Context.hpp
+++ b/include/vkcv/Context.hpp
@@ -58,6 +58,13 @@ namespace vkcv {
 		 * @return Vulkan device
 		 */
 		[[nodiscard]] const vk::Device &getDevice() const;
+		
+		/**
+		 * @brief Returns a dynamic dispatch loader of the context.
+		 *
+		 * @return Dynamic dispatch loader
+		 */
+		[[nodiscard]] const vk::DispatchLoaderDynamic &getDispatchLoaderDynamic() const;
 
 		/**
 		 * @brief Returns the feature manager of the context.
@@ -113,6 +120,7 @@ namespace vkcv {
 		vk::Instance m_Instance;
 		vk::PhysicalDevice m_PhysicalDevice;
 		vk::Device m_Device;
+		vk::DispatchLoaderDynamic m_DispatchDynamic;
 		FeatureManager m_FeatureManager;
 		QueueManager m_QueueManager;
 		vma::Allocator m_Allocator;
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index 1cec2a71..ce5389f8 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -24,6 +24,7 @@
 #include "ImageConfig.hpp"
 #include "PassConfig.hpp"
 #include "PushConstants.hpp"
+#include "RayTracingPipelineConfig.hpp"
 #include "Result.hpp"
 #include "SamplerTypes.hpp"
 #include "Window.hpp"
@@ -37,6 +38,7 @@ namespace vkcv {
 	class PassManager;
 	class GraphicsPipelineManager;
 	class ComputePipelineManager;
+	class RayTracingPipelineManager;
 	class DescriptorSetLayoutManager;
 	class DescriptorSetManager;
 	class BufferManager;
@@ -71,6 +73,7 @@ namespace vkcv {
 		std::unique_ptr<PassManager> m_PassManager;
 		std::unique_ptr<GraphicsPipelineManager> m_GraphicsPipelineManager;
 		std::unique_ptr<ComputePipelineManager> m_ComputePipelineManager;
+		std::unique_ptr<RayTracingPipelineManager> m_RayTracingPipelineManager;
 		std::unique_ptr<DescriptorSetLayoutManager> m_DescriptorSetLayoutManager;
 		std::unique_ptr<DescriptorSetManager> m_DescriptorSetManager;
 		std::unique_ptr<BufferManager> m_BufferManager;
@@ -180,6 +183,17 @@ namespace vkcv {
 		 */
 		[[nodiscard]] ComputePipelineHandle
 		createComputePipeline(const ComputePipelineConfig &config);
+		
+		/**
+		 * Creates a basic vulkan ray tracing pipeline using @p shader program and returns it using
+		 * the @p handle. Fixed Functions for pipeline are set with standard values.
+		 *
+		 * @param config a pipeline config object from the pipeline config class
+		 * layout
+		 * @return True if pipeline creation was successful, False if not
+		 */
+		[[nodiscard]] RayTracingPipelineHandle
+		createRayTracingPipeline(const RayTracingPipelineConfig &config);
 
 		/**
 		 * Creates a basic vulkan render pass using @p config from the render pass config class and
@@ -553,29 +567,23 @@ namespace vkcv {
 									   const WindowHandle &windowHandle);
 
 		/**
-		 * Records the rtx ray generation to the @p cmdStreamHandle.
-		 * Currently only supports @p closestHit, @p rayGen and @c miss shaderstages @c.
+		 * Records the ray generation via ray tracing pipeline to the @p cmdStreamHandle.
 		 *
 		 * @param cmdStreamHandle The command stream handle which receives relevant commands for
 		 * drawing.
-		 * @param rtxPipeline The raytracing pipeline from the RTXModule.
-		 * @param rtxPipelineLayout The raytracing pipeline layout from the RTXModule.
-		 * @param rgenRegion The shader binding table region for ray generation shaders.
-		 * @param rmissRegion The shader binding table region for ray miss shaders.
-		 * @param rchitRegion The shader binding table region for ray closest hit shaders.
-		 * @param rcallRegion The shader binding table region for callable shaders.
+		 * @param rayTracingPipeline The raytracing pipeline
+		 * @param dispatchSize How many work groups are dispatched
 		 * @param descriptorSetUsages The descriptor set usages.
 		 * @param pushConstants The push constants.
 		 * @param windowHandle The window handle defining in which window to render.
 		 */
-		void recordRayGenerationToCmdStream(
-			CommandStreamHandle cmdStreamHandle, vk::Pipeline rtxPipeline,
-			vk::PipelineLayout rtxPipelineLayout, vk::StridedDeviceAddressRegionKHR rgenRegion,
-			vk::StridedDeviceAddressRegionKHR rmissRegion,
-			vk::StridedDeviceAddressRegionKHR rchitRegion,
-			vk::StridedDeviceAddressRegionKHR rcallRegion,
-			const std::vector<DescriptorSetUsage> &descriptorSetUsages,
-			const PushConstants &pushConstants, const WindowHandle &windowHandle);
+		void recordRayGenerationToCmdStream(const CommandStreamHandle &cmdStreamHandle,
+											const RayTracingPipelineHandle &rayTracingPipeline,
+											const DispatchSize &dispatchSize,
+											const std::vector<DescriptorSetUsage>
+											        &descriptorSetUsages,
+											const PushConstants &pushConstants,
+											const vkcv::WindowHandle &windowHandle);
 
 		/**
 		 * @brief Record a compute shader dispatch into a command stream
diff --git a/include/vkcv/GraphicsPipelineConfig.hpp b/include/vkcv/GraphicsPipelineConfig.hpp
index 80084cb1..2d67501b 100644
--- a/include/vkcv/GraphicsPipelineConfig.hpp
+++ b/include/vkcv/GraphicsPipelineConfig.hpp
@@ -80,7 +80,8 @@ namespace vkcv {
 	public:
 		GraphicsPipelineConfig();
 
-		GraphicsPipelineConfig(const ShaderProgram &program, const PassHandle &pass,
+		GraphicsPipelineConfig(const ShaderProgram &program,
+							   const PassHandle &pass,
 							   const VertexLayout &vertexLayout,
 							   const std::vector<DescriptorSetLayoutHandle> &layouts);
 
diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp
index 3e92ee15..c814bdea 100644
--- a/include/vkcv/Handles.hpp
+++ b/include/vkcv/Handles.hpp
@@ -130,6 +130,16 @@ namespace vkcv {
 	private:
 		using Handle::Handle;
 	};
+	
+	/**
+	 * @brief Handle class for ray tracing pipelines.
+	 */
+	class RayTracingPipelineHandle : public Handle {
+		friend class RayTracingPipelineManager;
+	
+	private:
+		using Handle::Handle;
+	};
 
 	/**
 	 * @brief Handle class for descriptor set layouts.
diff --git a/include/vkcv/RayTracingPipelineConfig.hpp b/include/vkcv/RayTracingPipelineConfig.hpp
new file mode 100644
index 00000000..37ca1c4e
--- /dev/null
+++ b/include/vkcv/RayTracingPipelineConfig.hpp
@@ -0,0 +1,31 @@
+#pragma once
+/**
+ * @authors Tobias Frisch
+ * @file vkcv/RayTracingPipelineConfig.hpp
+ * @brief Ray tracing pipeline config struct to hand over required information to pipeline creation.
+ */
+ 
+#include "PipelineConfig.hpp"
+
+namespace vkcv {
+	
+	/**
+	 * @brief Class to configure a ray tracing pipeline before its creation.
+	 */
+	class RayTracingPipelineConfig : public PipelineConfig {
+	public:
+		RayTracingPipelineConfig();
+		
+		RayTracingPipelineConfig(const ShaderProgram &program,
+								 const std::vector<DescriptorSetLayoutHandle> &layouts);
+		
+		RayTracingPipelineConfig(const RayTracingPipelineConfig &other) = default;
+		RayTracingPipelineConfig(RayTracingPipelineConfig &&other) = default;
+		
+		~RayTracingPipelineConfig() = default;
+		
+		RayTracingPipelineConfig &operator=(const RayTracingPipelineConfig &other) = default;
+		RayTracingPipelineConfig &operator=(RayTracingPipelineConfig &&other) = default;
+	};
+
+}
diff --git a/projects/rtx_ambient_occlusion/src/main.cpp b/projects/rtx_ambient_occlusion/src/main.cpp
index 2a9d5d20..8845a7f2 100644
--- a/projects/rtx_ambient_occlusion/src/main.cpp
+++ b/projects/rtx_ambient_occlusion/src/main.cpp
@@ -80,15 +80,11 @@ int main(int argc, const char** argv) {
 	    glm::vec4 camera_up;         // for computing ray direction
 	    glm::vec4 camera_forward;    // for computing ray direction
 	};
-
-	uint32_t pushConstantSize = sizeof(RaytracingPushConstantData);
-
-    rtxModule.createRTXPipelineAndLayout(pushConstantSize, descriptorSetLayoutHandles, rtxShaderProgram);
-
-	vk::Pipeline rtxPipeline = rtxModule.getPipeline();
-	vk::PipelineLayout rtxPipelineLayout = rtxModule.getPipelineLayout();
-
-	vkcv::rtx::ShaderBindingTableRegions rtxRegions = rtxModule.createRegions();
+	
+	auto rtxPipeline = core.createRayTracingPipeline(vkcv::RayTracingPipelineConfig(
+			rtxShaderProgram,
+			descriptorSetLayoutHandles
+	));
 
 	vkcv::ImageHandle depthBuffer;
 
@@ -133,14 +129,11 @@ int main(int argc, const char** argv) {
 		core.recordRayGenerationToCmdStream(
 			cmdStream,
 			rtxPipeline,
-			rtxPipelineLayout,
-			rtxRegions.rgenRegion,
-			rtxRegions.rmissRegion,
-			rtxRegions.rchitRegion,
-			rtxRegions.rcallRegion,
+			vkcv::DispatchSize(swapchainWidth, swapchainHeight),
 			{ vkcv::useDescriptorSet(0, rtxShaderDescriptorSet) },
 			pushConstantsRTX,
-			windowHandle);
+			windowHandle
+		);
 
 		core.prepareSwapchainImageForPresent(cmdStream);
 		core.submitCommandStream(cmdStream);
diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp
index b443f8ca..0c605d66 100644
--- a/src/vkcv/BufferManager.cpp
+++ b/src/vkcv/BufferManager.cpp
@@ -129,6 +129,10 @@ namespace vkcv {
 			usageFlags = vk::BufferUsageFlagBits::eStorageBuffer
 						| vk::BufferUsageFlagBits::eIndirectBuffer;
 			break;
+		case BufferType::SHADER_BINDING:
+			usageFlags = vk::BufferUsageFlagBits::eShaderBindingTableKHR
+						| vk::BufferUsageFlagBits::eShaderDeviceAddressKHR;
+			break;
 		default:
 			vkcv_log(LogLevel::WARNING, "Unknown buffer type");
 			break;
diff --git a/src/vkcv/Context.cpp b/src/vkcv/Context.cpp
index 1d92268e..43bf5de2 100644
--- a/src/vkcv/Context.cpp
+++ b/src/vkcv/Context.cpp
@@ -6,9 +6,13 @@
 namespace vkcv {
 	
 	Context::Context(Context &&other) noexcept :
-		m_Instance(other.m_Instance), m_PhysicalDevice(other.m_PhysicalDevice),
-		m_Device(other.m_Device), m_FeatureManager(std::move(other.m_FeatureManager)),
-		m_QueueManager(std::move(other.m_QueueManager)), m_Allocator(other.m_Allocator) {
+		m_Instance(other.m_Instance),
+		m_PhysicalDevice(other.m_PhysicalDevice),
+		m_Device(other.m_Device),
+		m_DispatchDynamic(other.m_DispatchDynamic),
+		m_FeatureManager(std::move(other.m_FeatureManager)),
+		m_QueueManager(std::move(other.m_QueueManager)),
+		m_Allocator(other.m_Allocator) {
 		other.m_Instance = nullptr;
 		other.m_PhysicalDevice = nullptr;
 		other.m_Device = nullptr;
@@ -19,6 +23,7 @@ namespace vkcv {
 		m_Instance = other.m_Instance;
 		m_PhysicalDevice = other.m_PhysicalDevice;
 		m_Device = other.m_Device;
+		m_DispatchDynamic = other.m_DispatchDynamic;
 		m_FeatureManager = std::move(other.m_FeatureManager);
 		m_QueueManager = std::move(other.m_QueueManager);
 		m_Allocator = other.m_Allocator;
@@ -35,9 +40,18 @@ namespace vkcv {
 					 FeatureManager &&featureManager, QueueManager &&queueManager,
 					 vma::Allocator &&allocator) noexcept :
 		m_Instance(instance),
-		m_PhysicalDevice(physicalDevice), m_Device(device),
-		m_FeatureManager(std::move(featureManager)), m_QueueManager(std::move(queueManager)),
-		m_Allocator(allocator) {}
+		m_PhysicalDevice(physicalDevice),
+		m_Device(device),
+		m_DispatchDynamic(),
+		m_FeatureManager(std::move(featureManager)),
+		m_QueueManager(std::move(queueManager)),
+		m_Allocator(allocator) {
+		m_DispatchDynamic.init(
+				m_Instance,
+				(PFN_vkGetInstanceProcAddr) m_Instance.getProcAddr("vkGetInstanceProcAddr"),
+				m_Device
+		);
+	}
 
 	Context::~Context() noexcept {
 		m_Allocator.destroy();
@@ -56,6 +70,10 @@ namespace vkcv {
 	const vk::Device &Context::getDevice() const {
 		return m_Device;
 	}
+	
+	const vk::DispatchLoaderDynamic &Context::getDispatchLoaderDynamic() const {
+		return m_DispatchDynamic;
+	}
 
 	const FeatureManager &Context::getFeatureManager() const {
 		return m_FeatureManager;
@@ -453,6 +471,8 @@ namespace vkcv {
 			vmaFlags |= vma::AllocatorCreateFlagBits::eAmdDeviceCoherentMemory;
 		}
 		
+		vmaFlags |= vma::AllocatorCreateFlagBits::eBufferDeviceAddress;
+		
 		const vma::AllocatorCreateInfo allocatorCreateInfo(
 				vmaFlags,
 				physicalDevice,
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 7fde9196..9b4639f9 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -15,6 +15,7 @@
 #include "GraphicsPipelineManager.hpp"
 #include "ImageManager.hpp"
 #include "PassManager.hpp"
+#include "RayTracingPipelineManager.hpp"
 #include "SamplerManager.hpp"
 #include "WindowManager.hpp"
 #include "vkcv/BlitDownsampler.hpp"
@@ -90,6 +91,7 @@ namespace vkcv {
 		m_Context(std::move(context)), m_PassManager(std::make_unique<PassManager>()),
 		m_GraphicsPipelineManager(std::make_unique<GraphicsPipelineManager>()),
 		m_ComputePipelineManager(std::make_unique<ComputePipelineManager>()),
+		m_RayTracingPipelineManager(std::make_unique<RayTracingPipelineManager>()),
 		m_DescriptorSetLayoutManager(std::make_unique<DescriptorSetLayoutManager>()),
 		m_DescriptorSetManager(std::make_unique<DescriptorSetManager>()),
 		m_BufferManager(std::make_unique<BufferManager>()),
@@ -108,6 +110,7 @@ namespace vkcv {
 		m_PassManager->init(*this);
 		m_GraphicsPipelineManager->init(*this);
 		m_ComputePipelineManager->init(*this);
+		m_RayTracingPipelineManager->init(*this);
 		m_DescriptorSetLayoutManager->init(*this);
 		m_DescriptorSetManager->init(*this, *m_DescriptorSetLayoutManager);
 		m_BufferManager->init(*this);
@@ -146,6 +149,13 @@ namespace vkcv {
 
 		return m_ComputePipelineManager->createComputePipeline(config.getShaderProgram(), layouts);
 	}
+	
+	RayTracingPipelineHandle Core::createRayTracingPipeline(
+			const vkcv::RayTracingPipelineConfig &config) {
+		return m_RayTracingPipelineManager->createPipeline(config,
+														   *m_DescriptorSetLayoutManager,
+														   *m_BufferManager);
+	}
 
 	PassHandle Core::createPass(const PassConfig &config) {
 		return m_PassManager->createPass(config);
@@ -624,46 +634,80 @@ namespace vkcv {
 	}
 
 	void Core::recordRayGenerationToCmdStream(
-		CommandStreamHandle cmdStreamHandle, vk::Pipeline rtxPipeline,
-		vk::PipelineLayout rtxPipelineLayout, vk::StridedDeviceAddressRegionKHR rgenRegion,
-		vk::StridedDeviceAddressRegionKHR rmissRegion,
-		vk::StridedDeviceAddressRegionKHR rchitRegion,
-		vk::StridedDeviceAddressRegionKHR rcallRegion,
+		const CommandStreamHandle &cmdStreamHandle,
+		const RayTracingPipelineHandle &rayTracingPipeline,
+		const DispatchSize &dispatchSize,
 		const std::vector<DescriptorSetUsage> &descriptorSetUsages,
-		const PushConstants &pushConstants, const WindowHandle &windowHandle) {
+		const PushConstants &pushConstants,
+		const vkcv::WindowHandle &windowHandle) {
+		
+		const SwapchainHandle swapchainHandle = getWindow(windowHandle).getSwapchain();
+		
+		const vk::Pipeline pipeline = m_RayTracingPipelineManager->getVkPipeline(
+				rayTracingPipeline
+		);
+		
+		const vk::PipelineLayout pipelineLayout = m_RayTracingPipelineManager->getVkPipelineLayout(
+				rayTracingPipeline
+		);
+		
+		const vk::StridedDeviceAddressRegionKHR *rayGenAddress = (
+				m_RayTracingPipelineManager->getRayGenShaderBindingTableAddress(rayTracingPipeline)
+		);
+		
+		const vk::StridedDeviceAddressRegionKHR *rayMissAddress = (
+				m_RayTracingPipelineManager->getMissShaderBindingTableAddress(rayTracingPipeline)
+		);
+		
+		const vk::StridedDeviceAddressRegionKHR *rayHitAddress = (
+				m_RayTracingPipelineManager->getHitShaderBindingTableAddress(rayTracingPipeline)
+		);
+		
+		const vk::StridedDeviceAddressRegionKHR *rayCallAddress = (
+				m_RayTracingPipelineManager->getCallShaderBindingTableAddress(rayTracingPipeline)
+		);
 
 		auto submitFunction = [&](const vk::CommandBuffer &cmdBuffer) {
-			cmdBuffer.bindPipeline(vk::PipelineBindPoint::eRayTracingKHR, rtxPipeline);
+			cmdBuffer.bindPipeline(vk::PipelineBindPoint::eRayTracingKHR, pipeline);
+			
 			for (const auto &usage : descriptorSetUsages) {
 				cmdBuffer.bindDescriptorSets(
-					vk::PipelineBindPoint::eRayTracingKHR, rtxPipelineLayout, usage.location,
+					vk::PipelineBindPoint::eRayTracingKHR,
+					pipelineLayout,
+					usage.location,
 					{ m_DescriptorSetManager->getDescriptorSet(usage.descriptorSet).vulkanHandle },
-					usage.dynamicOffsets);
+					usage.dynamicOffsets
+				);
 			}
 
 			if (pushConstants.getSizePerDrawcall() > 0) {
 				cmdBuffer.pushConstants(
-					rtxPipelineLayout,
-					(vk::ShaderStageFlagBits::eClosestHitKHR | vk::ShaderStageFlagBits::eMissKHR
-					 | vk::ShaderStageFlagBits::eRaygenKHR), // TODO: add Support for eAnyHitKHR,
-															 // eCallableKHR, eIntersectionKHR
-					0, pushConstants.getSizePerDrawcall(), pushConstants.getData());
+					pipelineLayout,
+					vk::ShaderStageFlagBits::eAll,
+					0,
+					pushConstants.getSizePerDrawcall(),
+					pushConstants.getData()
+				);
 			}
-
-			auto m_rtxDispatcher = vk::DispatchLoaderDynamic(
-				(PFN_vkGetInstanceProcAddr)m_Context.getInstance().getProcAddr(
-					"vkGetInstanceProcAddr"));
-			m_rtxDispatcher.init(m_Context.getInstance());
-
-			cmdBuffer.traceRaysKHR(&rgenRegion, &rmissRegion, &rchitRegion, &rcallRegion,
-								   getWindow(windowHandle).getWidth(),
-								   getWindow(windowHandle).getHeight(), 1, m_rtxDispatcher);
+			
+			cmdBuffer.traceRaysKHR(
+					rayGenAddress,
+					rayMissAddress,
+					rayHitAddress,
+					rayCallAddress,
+					dispatchSize.x(),
+					dispatchSize.y(),
+					dispatchSize.z(),
+					m_Context.getDispatchLoaderDynamic()
+			);
 		};
+		
 		recordCommandsToStream(cmdStreamHandle, submitFunction, nullptr);
 	}
 
 	void Core::recordComputeDispatchToCmdStream(
-		const CommandStreamHandle &cmdStreamHandle, const ComputePipelineHandle &computePipeline,
+		const CommandStreamHandle &cmdStreamHandle,
+		const ComputePipelineHandle &computePipeline,
 		const DispatchSize &dispatchSize,
 		const std::vector<DescriptorSetUsage> &descriptorSetUsages,
 		const PushConstants &pushConstants) {
diff --git a/src/vkcv/GraphicsPipelineConfig.cpp b/src/vkcv/GraphicsPipelineConfig.cpp
index 987cd3a7..383c0de1 100644
--- a/src/vkcv/GraphicsPipelineConfig.cpp
+++ b/src/vkcv/GraphicsPipelineConfig.cpp
@@ -9,10 +9,13 @@ namespace vkcv {
 		m_Height(std::numeric_limits<uint32_t>::max()) {}
 
 	GraphicsPipelineConfig::GraphicsPipelineConfig(
-		const ShaderProgram &program, const PassHandle &pass, const VertexLayout &vertexLayout,
+		const ShaderProgram &program,
+		const PassHandle &pass,
+		const VertexLayout &vertexLayout,
 		const std::vector<DescriptorSetLayoutHandle> &layouts) :
 		PipelineConfig(program, layouts),
-		m_PassHandle(pass), m_VertexLayout(vertexLayout),
+		m_PassHandle(pass),
+		m_VertexLayout(vertexLayout),
 		m_Width(std::numeric_limits<uint32_t>::max()),
 		m_Height(std::numeric_limits<uint32_t>::max()) {}
 
diff --git a/src/vkcv/GraphicsPipelineManager.cpp b/src/vkcv/GraphicsPipelineManager.cpp
index 9e1af7f7..4ac862e4 100644
--- a/src/vkcv/GraphicsPipelineManager.cpp
+++ b/src/vkcv/GraphicsPipelineManager.cpp
@@ -100,7 +100,7 @@ namespace vkcv {
 		}
 	}
 
-	vk::ShaderStageFlagBits shaderStageToVkShaderStage(ShaderStage stage) {
+	static vk::ShaderStageFlagBits shaderStageToVkShaderStage(ShaderStage stage) {
 		switch (stage) {
 		case ShaderStage::VERTEX:
 			return vk::ShaderStageFlagBits::eVertex;
@@ -123,10 +123,12 @@ namespace vkcv {
 			return vk::ShaderStageFlagBits::eAll;
 		}
 	}
-
-	bool createPipelineShaderStageCreateInfo(const ShaderProgram &shaderProgram, ShaderStage stage,
-											 vk::Device device,
-											 vk::PipelineShaderStageCreateInfo* outCreateInfo) {
+	
+	static bool createPipelineShaderStageCreateInfo(
+			const ShaderProgram &shaderProgram,
+			ShaderStage stage,
+			vk::Device device,
+			vk::PipelineShaderStageCreateInfo* outCreateInfo) {
 
 		assert(outCreateInfo);
 		std::vector<uint32_t> code = shaderProgram.getShaderBinary(stage);
@@ -152,7 +154,7 @@ namespace vkcv {
 	 * @param existsVertexShader
 	 * @param config
 	 */
-	void fillVertexInputDescription(
+	static void fillVertexInputDescription(
 		std::vector<vk::VertexInputAttributeDescription> &vertexAttributeDescriptions,
 		std::vector<vk::VertexInputBindingDescription> &vertexBindingDescriptions,
 		const bool existsVertexShader, const GraphicsPipelineConfig &config) {
@@ -186,7 +188,7 @@ namespace vkcv {
 	 * @param vertexBindingDescriptions
 	 * @return Pipeline Vertex Input State Create Info Struct
 	 */
-	vk::PipelineVertexInputStateCreateInfo createPipelineVertexInputStateCreateInfo(
+	static vk::PipelineVertexInputStateCreateInfo createPipelineVertexInputStateCreateInfo(
 		std::vector<vk::VertexInputAttributeDescription> &vertexAttributeDescriptions,
 		std::vector<vk::VertexInputBindingDescription> &vertexBindingDescriptions) {
 
@@ -201,15 +203,15 @@ namespace vkcv {
 	 * @param config provides data for primitive topology.
 	 * @return Pipeline Input Assembly State Create Info Struct
 	 */
-	vk::PipelineInputAssemblyStateCreateInfo
+	static vk::PipelineInputAssemblyStateCreateInfo
 	createPipelineInputAssemblyStateCreateInfo(const GraphicsPipelineConfig &config) {
 		vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(
 			{}, primitiveTopologyToVulkanPrimitiveTopology(config.getPrimitiveTopology()), false);
 
 		return pipelineInputAssemblyStateCreateInfo;
 	}
-
-	vk::PipelineTessellationStateCreateInfo
+	
+	static vk::PipelineTessellationStateCreateInfo
 	createPipelineTessellationStateCreateInfo(const GraphicsPipelineConfig &config) {
 		vk::PipelineTessellationStateCreateInfo pipelineTessellationStateCreateInfo(
 			{}, config.getTesselationControlPoints());
@@ -223,7 +225,7 @@ namespace vkcv {
 	 * @param config provides with and height of the output window
 	 * @return Pipeline Viewport State Create Info Struct
 	 */
-	vk::PipelineViewportStateCreateInfo
+	static vk::PipelineViewportStateCreateInfo
 	createPipelineViewportStateCreateInfo(const GraphicsPipelineConfig &config) {
 		static vk::Viewport viewport;
 		static vk::Rect2D scissor;
@@ -250,7 +252,7 @@ namespace vkcv {
 	 * @param config sets Depth Clamping and Culling Mode
 	 * @return Pipeline Rasterization State Create Info Struct
 	 */
-	vk::PipelineRasterizationStateCreateInfo createPipelineRasterizationStateCreateInfo(
+	static vk::PipelineRasterizationStateCreateInfo createPipelineRasterizationStateCreateInfo(
 		const GraphicsPipelineConfig &config,
 		const vk::PhysicalDeviceConservativeRasterizationPropertiesEXT
 			&conservativeRasterProperties) {
@@ -300,7 +302,7 @@ namespace vkcv {
 	 * @param config set MSAA Sample Count Flag
 	 * @return Pipeline Multisample State Create Info Struct
 	 */
-	vk::PipelineMultisampleStateCreateInfo
+	static vk::PipelineMultisampleStateCreateInfo
 	createPipelineMultisampleStateCreateInfo(const GraphicsPipelineConfig &config,
 											 const PassConfig &passConfig) {
 		vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo(
@@ -316,7 +318,7 @@ namespace vkcv {
 	 * @param config sets blend mode
 	 * @return
 	 */
-	vk::PipelineColorBlendStateCreateInfo
+	static vk::PipelineColorBlendStateCreateInfo
 	createPipelineColorBlendStateCreateInfo(const GraphicsPipelineConfig &config,
 											const PassConfig &passConfig) {
 		static std::vector<vk::PipelineColorBlendAttachmentState> colorBlendAttachmentStates;
@@ -368,7 +370,7 @@ namespace vkcv {
 	 * @param config sets Push Constant Size and Descriptor Layouts.
 	 * @return Pipeline Layout Create Info Struct
 	 */
-	vk::PipelineLayoutCreateInfo createPipelineLayoutCreateInfo(
+	static vk::PipelineLayoutCreateInfo createPipelineLayoutCreateInfo(
 		const GraphicsPipelineConfig &config,
 		const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts) {
 		static vk::PushConstantRange pushConstantRange;
@@ -392,7 +394,7 @@ namespace vkcv {
 	 * @param config sets if depth test in enabled or not.
 	 * @return Pipeline Layout Create Info Struct
 	 */
-	vk::PipelineDepthStencilStateCreateInfo
+	static vk::PipelineDepthStencilStateCreateInfo
 	createPipelineDepthStencilStateCreateInfo(const GraphicsPipelineConfig &config) {
 		const vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilCreateInfo(
 			vk::PipelineDepthStencilStateCreateFlags(), config.getDepthTest() != DepthTest::None,
@@ -407,7 +409,7 @@ namespace vkcv {
 	 * @param config sets whenever a dynamic viewport is used or not.
 	 * @return Pipeline Dynamic State Create Info Struct
 	 */
-	vk::PipelineDynamicStateCreateInfo
+	static vk::PipelineDynamicStateCreateInfo
 	createPipelineDynamicStateCreateInfo(const GraphicsPipelineConfig &config) {
 		static std::vector<vk::DynamicState> dynamicStates;
 		dynamicStates.clear();
@@ -651,6 +653,7 @@ namespace vkcv {
 			!= 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);
 			destroyShaderModules();
 			return {};
 		}
diff --git a/src/vkcv/GraphicsPipelineManager.hpp b/src/vkcv/GraphicsPipelineManager.hpp
index 602e1a96..867bd46f 100644
--- a/src/vkcv/GraphicsPipelineManager.hpp
+++ b/src/vkcv/GraphicsPipelineManager.hpp
@@ -2,11 +2,9 @@
 
 /**
  * @authors Mark Mints
- * @file src/vkcv/PipelineManager.hpp
- * @brief Creation and handling of Graphic Pipelines
+ * @file src/vkcv/GraphicsPipelineManager.hpp
+ * @brief Creation and handling of graphic pipelines
  */
-// TODO: Edit @brief: add graphics pipeline, but only then when the compute part is in an own class.
-// TODO: More authors? Do we need authors (general question)?
 
 #include "DescriptorSetLayoutManager.hpp"
 #include "HandleManager.hpp"
diff --git a/src/vkcv/RayTracingPipelineConfig.cpp b/src/vkcv/RayTracingPipelineConfig.cpp
new file mode 100644
index 00000000..cec0bdec
--- /dev/null
+++ b/src/vkcv/RayTracingPipelineConfig.cpp
@@ -0,0 +1,14 @@
+
+#include "vkcv/RayTracingPipelineConfig.hpp"
+
+namespace vkcv {
+	
+	RayTracingPipelineConfig::RayTracingPipelineConfig() :
+		PipelineConfig() {}
+	
+	RayTracingPipelineConfig::RayTracingPipelineConfig(
+			const ShaderProgram &program,
+			const std::vector<DescriptorSetLayoutHandle> &layouts) :
+			PipelineConfig(program, layouts) {}
+	
+}
diff --git a/src/vkcv/RayTracingPipelineManager.cpp b/src/vkcv/RayTracingPipelineManager.cpp
new file mode 100644
index 00000000..010d234a
--- /dev/null
+++ b/src/vkcv/RayTracingPipelineManager.cpp
@@ -0,0 +1,533 @@
+
+#include "RayTracingPipelineManager.hpp"
+
+#include "vkcv/Core.hpp"
+#include "vkcv/Logger.hpp"
+
+namespace vkcv {
+	
+	uint64_t RayTracingPipelineManager::getIdFrom(const RayTracingPipelineHandle &handle) const {
+		return handle.getId();
+	}
+	
+	RayTracingPipelineHandle RayTracingPipelineManager::createById(uint64_t id, const HandleDestroyFunction &destroy) {
+		return RayTracingPipelineHandle(id, destroy);
+	}
+	
+	void RayTracingPipelineManager::destroyById(uint64_t id) {
+		auto &pipeline = getById(id);
+		
+		if (pipeline.m_handle) {
+			getCore().getContext().getDevice().destroy(pipeline.m_handle);
+			pipeline.m_handle = nullptr;
+		}
+		
+		if (pipeline.m_layout) {
+			getCore().getContext().getDevice().destroy(pipeline.m_layout);
+			pipeline.m_layout = nullptr;
+		}
+	}
+	
+	RayTracingPipelineManager::RayTracingPipelineManager() noexcept :
+		HandleManager<RayTracingPipelineEntry, RayTracingPipelineHandle>() {}
+	
+	RayTracingPipelineManager::~RayTracingPipelineManager() noexcept {
+		clear();
+	}
+	
+	static vk::ShaderStageFlagBits shaderStageToVkShaderStage(ShaderStage stage) {
+		switch (stage) {
+			case ShaderStage::RAY_GEN:
+				return vk::ShaderStageFlagBits::eRaygenKHR;
+			case ShaderStage::RAY_ANY_HIT:
+				return vk::ShaderStageFlagBits::eAnyHitKHR;
+			case ShaderStage::RAY_CLOSEST_HIT:
+				return vk::ShaderStageFlagBits::eClosestHitKHR;
+			case ShaderStage::RAY_MISS:
+				return vk::ShaderStageFlagBits::eMissKHR;
+			case ShaderStage::RAY_INTERSECTION:
+				return vk::ShaderStageFlagBits::eIntersectionKHR;
+			case ShaderStage::RAY_CALLABLE:
+				return vk::ShaderStageFlagBits::eCallableKHR;
+			default:
+				vkcv_log(LogLevel::ERROR, "Unknown shader stage");
+				return vk::ShaderStageFlagBits::eAll;
+		}
+	}
+	
+	static bool createPipelineShaderStageCreateInfo(
+			const ShaderProgram &shaderProgram,
+			ShaderStage stage,
+			vk::Device device,
+			vk::PipelineShaderStageCreateInfo* outCreateInfo) {
+		
+		assert(outCreateInfo);
+		std::vector<uint32_t> code = shaderProgram.getShaderBinary(stage);
+		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
+		);
+		
+		return true;
+	}
+	
+	/**
+	 * Creates a Pipeline Layout Create Info Struct.
+	 * @param config sets Push Constant Size and Descriptor Layouts.
+	 * @return Pipeline Layout Create Info Struct
+	 */
+	static vk::PipelineLayoutCreateInfo createPipelineLayoutCreateInfo(
+			const RayTracingPipelineConfig &config,
+			const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts) {
+		static vk::PushConstantRange pushConstantRange;
+		
+		const size_t pushConstantsSize = config.getShaderProgram().getPushConstantsSize();
+		pushConstantRange =vk::PushConstantRange(
+				vk::ShaderStageFlagBits::eAll,
+				0,
+				pushConstantsSize
+		);
+		
+		vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo({}, (descriptorSetLayouts),
+															  (pushConstantRange));
+		
+		if (pushConstantsSize == 0) {
+			pipelineLayoutCreateInfo.pushConstantRangeCount = 0;
+		}
+		
+		return pipelineLayoutCreateInfo;
+	}
+	
+	RayTracingPipelineHandle
+	RayTracingPipelineManager::createPipeline(const RayTracingPipelineConfig &config,
+											  const DescriptorSetLayoutManager &descriptorManager,
+											  BufferManager &bufferManager) {
+		const auto &program = config.getShaderProgram();
+		
+		const auto &dynamicDispatch = getCore().getContext().getDispatchLoaderDynamic();
+		
+		const bool existsRayGenShader = program.existsShader(ShaderStage::RAY_GEN);
+		const bool existsRayAnyHitShader = program.existsShader(ShaderStage::RAY_ANY_HIT);
+		const bool existsRayClosestHitShader = program.existsShader(ShaderStage::RAY_CLOSEST_HIT);
+		const bool existsRayMissShader = program.existsShader(ShaderStage::RAY_MISS);
+		const bool existsRayIntersectionShader = program.existsShader(ShaderStage::RAY_INTERSECTION);
+		const bool existsRayCallableShader = program.existsShader(ShaderStage::RAY_CALLABLE);
+		
+		if (!existsRayGenShader) {
+			vkcv_log(LogLevel::ERROR, "Requires ray generation shader code");
+			return {};
+		}
+		
+		uint32_t anyHitStageIndex = VK_SHADER_UNUSED_KHR;
+		uint32_t closestHitStageIndex = VK_SHADER_UNUSED_KHR;
+		uint32_t intersectionStageIndex = VK_SHADER_UNUSED_KHR;
+		
+		std::vector<vk::PipelineShaderStageCreateInfo> shaderStages;
+		shaderStages.reserve(
+				(existsRayGenShader? 1 : 0)
+				+ (existsRayAnyHitShader? 1 : 0)
+				+ (existsRayClosestHitShader? 1 : 0)
+				+ (existsRayMissShader? 1 : 0)
+				+ (existsRayIntersectionShader? 1 : 0)
+				+ (existsRayCallableShader? 1 : 0)
+		);
+		
+		auto destroyShaderModules = [&shaderStages, this] {
+			for (auto stage : shaderStages) {
+				getCore().getContext().getDevice().destroyShaderModule(stage.module);
+			}
+			shaderStages.clear();
+		};
+		
+		std::vector<vk::RayTracingShaderGroupCreateInfoKHR> shaderGroups;
+		shaderGroups.reserve(
+				(existsRayGenShader? 1 : 0)
+				+ (existsRayMissShader? 1 : 0)
+				+ (existsRayCallableShader? 1 : 0)
+				+ (existsRayAnyHitShader
+					|| existsRayClosestHitShader
+					|| existsRayIntersectionShader? 1 : 0)
+		);
+		
+		auto addGeneralShaderGroup = [&shaderGroups](size_t index) {
+			shaderGroups.emplace_back(
+					vk::RayTracingShaderGroupTypeKHR::eGeneral,
+					static_cast<uint32_t>(index),
+					VK_SHADER_UNUSED_KHR,
+					VK_SHADER_UNUSED_KHR,
+					VK_SHADER_UNUSED_KHR,
+					nullptr
+			);
+		};
+		
+		size_t genShaderGroupIndex = shaderGroups.capacity();
+		size_t missShaderGroupIndex = genShaderGroupIndex;
+		size_t hitShaderGroupIndex = genShaderGroupIndex;
+		size_t callShaderGroupIndex = genShaderGroupIndex;
+		
+		if (existsRayGenShader) {
+			vk::PipelineShaderStageCreateInfo createInfo;
+			const bool success = createPipelineShaderStageCreateInfo(
+					program,
+					ShaderStage::RAY_GEN,
+					getCore().getContext().getDevice(),
+					&createInfo
+			);
+			
+			if (success) {
+				genShaderGroupIndex = shaderGroups.size();
+				addGeneralShaderGroup(shaderStages.size());
+				shaderStages.push_back(createInfo);
+			} else {
+				destroyShaderModules();
+				return {};
+			}
+		}
+		
+		if (existsRayAnyHitShader) {
+			vk::PipelineShaderStageCreateInfo createInfo;
+			const bool success = createPipelineShaderStageCreateInfo(
+					program,
+					ShaderStage::RAY_ANY_HIT,
+					getCore().getContext().getDevice(),
+					&createInfo
+			);
+			
+			if (success) {
+				anyHitStageIndex = static_cast<uint32_t>(shaderStages.size());
+				shaderStages.push_back(createInfo);
+			} else {
+				destroyShaderModules();
+				return {};
+			}
+		}
+		
+		if (existsRayClosestHitShader) {
+			vk::PipelineShaderStageCreateInfo createInfo;
+			const bool success = createPipelineShaderStageCreateInfo(
+					program,
+					ShaderStage::RAY_CLOSEST_HIT,
+					getCore().getContext().getDevice(),
+					&createInfo
+			);
+			
+			if (success) {
+				closestHitStageIndex = static_cast<uint32_t>(shaderStages.size());
+				shaderStages.push_back(createInfo);
+			} else {
+				destroyShaderModules();
+				return {};
+			}
+		}
+		
+		if (existsRayMissShader) {
+			vk::PipelineShaderStageCreateInfo createInfo;
+			const bool success = createPipelineShaderStageCreateInfo(
+					program,
+					ShaderStage::RAY_MISS,
+					getCore().getContext().getDevice(),
+					&createInfo
+			);
+			
+			if (success) {
+				missShaderGroupIndex = shaderGroups.size();
+				addGeneralShaderGroup(shaderStages.size());
+				shaderStages.push_back(createInfo);
+			} else {
+				destroyShaderModules();
+				return {};
+			}
+		}
+		
+		if (existsRayIntersectionShader) {
+			vk::PipelineShaderStageCreateInfo createInfo;
+			const bool success = createPipelineShaderStageCreateInfo(
+					program,
+					ShaderStage::RAY_INTERSECTION,
+					getCore().getContext().getDevice(),
+					&createInfo
+			);
+			
+			if (success) {
+				intersectionStageIndex = static_cast<uint32_t>(shaderStages.size());
+				shaderStages.push_back(createInfo);
+			} else {
+				destroyShaderModules();
+				return {};
+			}
+		}
+		
+		if (existsRayCallableShader) {
+			vk::PipelineShaderStageCreateInfo createInfo;
+			const bool success = createPipelineShaderStageCreateInfo(
+					program,
+					ShaderStage::RAY_CALLABLE,
+					getCore().getContext().getDevice(),
+					&createInfo
+			);
+			
+			if (success) {
+				callShaderGroupIndex = shaderGroups.size();
+				addGeneralShaderGroup(shaderStages.size());
+				shaderStages.push_back(createInfo);
+			} else {
+				destroyShaderModules();
+				return {};
+			}
+		}
+		
+		if ((closestHitStageIndex != VK_SHADER_UNUSED_KHR) ||
+			(anyHitStageIndex != VK_SHADER_UNUSED_KHR) ||
+			(intersectionStageIndex != VK_SHADER_UNUSED_KHR)) {
+			hitShaderGroupIndex = shaderGroups.size();
+			shaderGroups.emplace_back(
+					intersectionStageIndex != VK_SHADER_UNUSED_KHR?
+					vk::RayTracingShaderGroupTypeKHR::eProceduralHitGroup :
+					vk::RayTracingShaderGroupTypeKHR::eTrianglesHitGroup,
+					VK_SHADER_UNUSED_KHR,
+					closestHitStageIndex,
+					anyHitStageIndex,
+					intersectionStageIndex,
+					nullptr
+			);
+		}
+		
+		std::vector<vk::DescriptorSetLayout> descriptorSetLayouts;
+		descriptorSetLayouts.reserve(config.getDescriptorSetLayouts().size());
+		for (const auto &handle : config.getDescriptorSetLayouts()) {
+			descriptorSetLayouts.push_back(
+					descriptorManager.getDescriptorSetLayout(handle).vulkanHandle);
+		}
+		
+		vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = createPipelineLayoutCreateInfo(
+				config,
+				descriptorSetLayouts
+		);
+		
+		vk::PipelineLayout pipelineLayout {};
+		if (getCore().getContext().getDevice().createPipelineLayout(&pipelineLayoutCreateInfo,
+																	nullptr, &pipelineLayout)
+			!= vk::Result::eSuccess) {
+			destroyShaderModules();
+			return {};
+		}
+		
+		vk::PhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingPipelineProperties;
+		
+		vk::PhysicalDeviceProperties2 physicalProperties2;
+		physicalProperties2.pNext = &rayTracingPipelineProperties;
+		
+		getCore().getContext().getPhysicalDevice().getProperties2(&physicalProperties2);
+		
+		vk::RayTracingPipelineCreateInfoKHR pipelineCreateInfo (
+				vk::PipelineCreateFlags(),
+				static_cast<uint32_t>(shaderStages.size()),
+				shaderStages.data(),
+				static_cast<uint32_t>(shaderGroups.size()),
+				shaderGroups.data(),
+				rayTracingPipelineProperties.maxRayRecursionDepth,
+				nullptr,
+				nullptr,
+				nullptr,
+				pipelineLayout
+		);
+		
+		const auto pipelineCreation = getCore().getContext().getDevice().createRayTracingPipelineKHR(
+				vk::DeferredOperationKHR(),
+				vk::PipelineCache(),
+				pipelineCreateInfo,
+				nullptr,
+				dynamicDispatch
+		);
+		
+		// Clean Up
+		destroyShaderModules();
+		
+		if (pipelineCreation.result != vk::Result::eSuccess) {
+			getCore().getContext().getDevice().destroy(pipelineLayout);
+			return {};
+		}
+		
+		auto pipeline = pipelineCreation.value;
+		
+		const size_t shaderGroupHandlesSize = (
+				rayTracingPipelineProperties.shaderGroupHandleSize * shaderGroups.size()
+		);
+		
+		std::vector<uint8_t> shaderGroupHandleEntries;
+		shaderGroupHandleEntries.resize(shaderGroupHandlesSize);
+		
+		if (getCore().getContext().getDevice().getRayTracingShaderGroupHandlesKHR(
+				pipeline,
+				0,
+				static_cast<uint32_t>(shaderGroups.size()),
+				static_cast<uint32_t>(shaderGroupHandlesSize),
+				shaderGroupHandleEntries.data(),
+				dynamicDispatch) != vk::Result::eSuccess) {
+			vkcv_log(LogLevel::ERROR, "Could not retrieve shader binding table group handles.");
+			
+			getCore().getContext().getDevice().destroy(pipeline);
+			getCore().getContext().getDevice().destroy(pipelineLayout);
+			return {};
+		}
+		
+		const size_t tableSizeAlignment = std::max(
+				rayTracingPipelineProperties.shaderGroupBaseAlignment,
+				rayTracingPipelineProperties.shaderGroupHandleSize
+		);
+		
+		const size_t shaderBindingTableSize = (
+				tableSizeAlignment * shaderGroups.size()
+		);
+		
+		const BufferHandle &shaderBindingTable = getCore().createBuffer(
+				BufferType::SHADER_BINDING,
+				shaderBindingTableSize
+		);
+		
+		void* mappedBindingTable = bufferManager.mapBuffer(
+				shaderBindingTable,
+				0,
+				shaderBindingTableSize
+		);
+		
+		for (size_t i = 0; i < shaderGroups.size(); i++) {
+			memcpy(
+				reinterpret_cast<uint8_t*>(mappedBindingTable)
+				+ i * tableSizeAlignment,
+				shaderGroupHandleEntries.data()
+				+ i * rayTracingPipelineProperties.shaderGroupHandleSize,
+				rayTracingPipelineProperties.shaderGroupHandleSize
+			);
+		}
+		
+		if (mappedBindingTable == nullptr) {
+			getCore().getContext().getDevice().destroy(pipeline);
+			getCore().getContext().getDevice().destroy(pipelineLayout);
+			return {};
+		}
+		
+		bufferManager.unmapBuffer(shaderBindingTable);
+		
+		const vk::BufferDeviceAddressInfoKHR shaderBindingTableAddressInfo (
+				bufferManager.getBuffer(shaderBindingTable)
+		);
+		
+		const vk::DeviceAddress bufferBaseAddress = (
+				getCore().getContext().getDevice().getBufferAddressKHR(
+						shaderBindingTableAddressInfo,
+						dynamicDispatch
+				)
+		);
+		
+		vk::StridedDeviceAddressRegionKHR rayGenAddress {};
+		vk::StridedDeviceAddressRegionKHR rayMissAddress {};
+		vk::StridedDeviceAddressRegionKHR rayHitAddress {};
+		vk::StridedDeviceAddressRegionKHR rayCallAddress {};
+		
+		if (genShaderGroupIndex < shaderGroups.size()) {
+			rayGenAddress = vk::StridedDeviceAddressRegionKHR(
+					bufferBaseAddress + genShaderGroupIndex * tableSizeAlignment,
+					shaderBindingTableSize,
+					shaderBindingTableSize
+			);
+		}
+		
+		if (missShaderGroupIndex < shaderGroups.size()) {
+			rayMissAddress = vk::StridedDeviceAddressRegionKHR(
+					bufferBaseAddress + missShaderGroupIndex * tableSizeAlignment,
+					shaderBindingTableSize,
+					shaderBindingTableSize
+			);
+		}
+		
+		if (hitShaderGroupIndex < shaderGroups.size()) {
+			rayHitAddress = vk::StridedDeviceAddressRegionKHR(
+					bufferBaseAddress + hitShaderGroupIndex * tableSizeAlignment,
+					shaderBindingTableSize,
+					shaderBindingTableSize
+			);
+		}
+		
+		if (callShaderGroupIndex < shaderGroups.size()) {
+			rayCallAddress = vk::StridedDeviceAddressRegionKHR(
+					bufferBaseAddress + callShaderGroupIndex * tableSizeAlignment,
+					shaderBindingTableSize,
+					shaderBindingTableSize
+			);
+		}
+		
+		return add({
+			pipeline,
+			pipelineLayout,
+			config,
+			shaderBindingTable,
+			rayGenAddress,
+			rayMissAddress,
+			rayHitAddress,
+			rayCallAddress
+		});
+	}
+	
+	vk::Pipeline RayTracingPipelineManager::getVkPipeline(const RayTracingPipelineHandle &handle) const {
+		auto &pipeline = (*this) [handle];
+		return pipeline.m_handle;
+	}
+	
+	vk::PipelineLayout RayTracingPipelineManager::getVkPipelineLayout(
+			const RayTracingPipelineHandle &handle) const {
+		auto &pipeline = (*this) [handle];
+		return pipeline.m_layout;
+	}
+	
+	const RayTracingPipelineConfig &
+	RayTracingPipelineManager::getPipelineConfig(const RayTracingPipelineHandle &handle) const {
+		auto &pipeline = (*this) [handle];
+		return pipeline.m_config;
+	}
+	
+	const vk::StridedDeviceAddressRegionKHR *
+	RayTracingPipelineManager::getRayGenShaderBindingTableAddress(
+			const vkcv::RayTracingPipelineHandle &handle) const {
+		auto &pipeline = (*this) [handle];
+		return &(pipeline.m_rayGenAddress);
+	}
+	
+	const vk::StridedDeviceAddressRegionKHR *
+	RayTracingPipelineManager::getMissShaderBindingTableAddress(
+			const vkcv::RayTracingPipelineHandle &handle) const {
+		auto &pipeline = (*this) [handle];
+		return &(pipeline.m_rayMissAddress);
+	}
+	
+	const vk::StridedDeviceAddressRegionKHR *
+	RayTracingPipelineManager::getHitShaderBindingTableAddress(
+			const vkcv::RayTracingPipelineHandle &handle) const {
+		auto &pipeline = (*this) [handle];
+		return &(pipeline.m_rayHitAddress);
+	}
+	
+	const vk::StridedDeviceAddressRegionKHR *
+	RayTracingPipelineManager::getCallShaderBindingTableAddress(
+			const vkcv::RayTracingPipelineHandle &handle) const {
+		auto &pipeline = (*this) [handle];
+		return &(pipeline.m_rayCallAddress);
+	}
+	
+}
diff --git a/src/vkcv/RayTracingPipelineManager.hpp b/src/vkcv/RayTracingPipelineManager.hpp
new file mode 100644
index 00000000..f2b9da8b
--- /dev/null
+++ b/src/vkcv/RayTracingPipelineManager.hpp
@@ -0,0 +1,118 @@
+#pragma once
+
+/**
+ * @authors Tobias Frisch
+ * @file src/vkcv/PipelineManager.hpp
+ * @brief Creation and handling of ray tracing pipelines
+ */
+
+#include "BufferManager.hpp"
+#include "DescriptorSetLayoutManager.hpp"
+#include "HandleManager.hpp"
+#include "PassManager.hpp"
+#include "vkcv/RayTracingPipelineConfig.hpp"
+#include <vector>
+#include <vulkan/vulkan.hpp>
+
+namespace vkcv {
+	
+	struct RayTracingPipelineEntry {
+		vk::Pipeline m_handle;
+		vk::PipelineLayout m_layout;
+		RayTracingPipelineConfig m_config;
+		BufferHandle m_shaderBindingTable;
+		vk::StridedDeviceAddressRegionKHR m_rayGenAddress;
+		vk::StridedDeviceAddressRegionKHR m_rayMissAddress;
+		vk::StridedDeviceAddressRegionKHR m_rayHitAddress;
+		vk::StridedDeviceAddressRegionKHR m_rayCallAddress;
+	};
+	
+	/**
+	 * @brief Class to manage ray tracing pipelines.
+	 */
+	class RayTracingPipelineManager :
+			public HandleManager<RayTracingPipelineEntry, RayTracingPipelineHandle> {
+	private:
+		[[nodiscard]] uint64_t getIdFrom(const RayTracingPipelineHandle &handle) const override;
+		
+		[[nodiscard]] RayTracingPipelineHandle
+		createById(uint64_t id, const HandleDestroyFunction &destroy) override;
+		
+		/**
+		 * Destroys and deallocates ray tracing pipeline represented by a given
+		 * ray tracing pipeline handle id.
+		 *
+		 * @param id Ray tracing pipeline handle id
+		 */
+		void destroyById(uint64_t id) override;
+	
+	public:
+		RayTracingPipelineManager() noexcept;
+		
+		~RayTracingPipelineManager() noexcept override; // dtor
+		
+		RayTracingPipelineManager(const RayTracingPipelineManager &other) = delete; // copy-ctor
+		RayTracingPipelineManager(RayTracingPipelineManager &&other) = delete;      // move-ctor;
+		
+		RayTracingPipelineManager &
+		operator=(const RayTracingPipelineManager &other) = delete; // copy-assign op
+		RayTracingPipelineManager &
+		operator=(RayTracingPipelineManager &&other) = delete; // move-assign op
+		
+		/**
+		 * Creates a ray tracing pipeline based on the set shader stages in the config struct.
+		 * This function is wrapped in /src/vkcv/Core.cpp by Core::createRayTracingPipeline(const
+		 * PipelineConfig &config). Therefore the passManager is filled already by the overall
+		 * context of an application. On application level it is necessary first to fill a
+		 * pipeline config struct.
+		 *
+		 * @param config Hands over all needed information for pipeline creation.
+		 * @param descriptorManager Hands over the corresponding descriptor set layouts
+		 * @param bufferManager Allows managing the shader binding table
+		 * @return A handler to the created ray tracing pipeline object.
+		 */
+		RayTracingPipelineHandle createPipeline(const RayTracingPipelineConfig &config,
+												const DescriptorSetLayoutManager &descriptorManager,
+												BufferManager &bufferManager);
+		
+		/**
+		 * Returns a vk::Pipeline object by handle.
+		 *
+		 * @param handle Directing to the requested pipeline.
+		 * @return vk::Pipeline.
+		 */
+		[[nodiscard]] vk::Pipeline getVkPipeline(const RayTracingPipelineHandle &handle) const;
+		
+		/**
+		 * Returns a vk::PipelineLayout object by handle.
+		 *
+		 * @param handle Directing to the requested pipeline.
+		 * @return vk::PipelineLayout.
+		 */
+		[[nodiscard]] vk::PipelineLayout
+		getVkPipelineLayout(const RayTracingPipelineHandle &handle) const;
+		
+		/**
+		 * Returns the corresponding pipeline config struct of a pipeline object directed by the
+		 * given handler.
+		 *
+		 * @param handle Directing to the requested pipeline.
+		 * @return Pipeline config struct
+		 */
+		[[nodiscard]] const RayTracingPipelineConfig &
+		getPipelineConfig(const RayTracingPipelineHandle &handle) const;
+		
+		[[nodiscard]] const vk::StridedDeviceAddressRegionKHR*
+		getRayGenShaderBindingTableAddress(const RayTracingPipelineHandle &handle) const;
+		
+		[[nodiscard]] const vk::StridedDeviceAddressRegionKHR*
+		getMissShaderBindingTableAddress(const RayTracingPipelineHandle &handle) const;
+		
+		[[nodiscard]] const vk::StridedDeviceAddressRegionKHR*
+		getHitShaderBindingTableAddress(const RayTracingPipelineHandle &handle) const;
+		
+		[[nodiscard]] const vk::StridedDeviceAddressRegionKHR*
+		getCallShaderBindingTableAddress(const RayTracingPipelineHandle &handle) const;
+	};
+	
+} // namespace vkcv
-- 
GitLab