From 03ca4e3c36ecd03eff012b137ddf4de0312e3e51 Mon Sep 17 00:00:00 2001
From: Tobias Frisch <tfrisch@uni-koblenz.de>
Date: Mon, 28 Nov 2022 21:26:40 +0100
Subject: [PATCH] Implement top-level acceleration structures in framework and
 reduce code in realtime ray tracing example via substitution

Signed-off-by: Tobias Frisch <tfrisch@uni-koblenz.de>
---
 README.md                                     |   2 +-
 include/vkcv/Core.hpp                         |  20 +
 projects/CMakeLists.txt                       |   2 +-
 projects/rt_ambient_occlusion/.gitignore      |   1 +
 .../CMakeLists.txt                            |  18 +-
 .../README.md                                 |  10 +-
 .../resources/shaders/ambientOcclusion.rchit  |   4 +-
 .../resources/shaders/ambientOcclusion.rgen   |   0
 .../resources/shaders/ambientOcclusion.rmiss  |   0
 .../src/main.cpp                              |  63 +--
 projects/rtx_ambient_occlusion/.gitignore     |   1 -
 .../src/RTX/ASManager.cpp                     | 468 ------------------
 .../src/RTX/ASManager.hpp                     | 187 -------
 .../rtx_ambient_occlusion/src/RTX/RTX.cpp     |  30 --
 .../rtx_ambient_occlusion/src/RTX/RTX.hpp     | 100 ----
 ...occlusion.png => rt_ambient_occlusion.png} |   0
 src/vkcv/AccelerationStructureManager.cpp     | 309 +++++++++---
 src/vkcv/AccelerationStructureManager.hpp     |  16 +-
 src/vkcv/BufferManager.cpp                    |  26 +-
 src/vkcv/BufferManager.hpp                    |  10 +-
 src/vkcv/Core.cpp                             |  16 +-
 src/vkcv/DescriptorSetManager.cpp             |   7 +-
 src/vkcv/RayTracingPipelineManager.cpp        |  17 +-
 23 files changed, 365 insertions(+), 942 deletions(-)
 create mode 100644 projects/rt_ambient_occlusion/.gitignore
 rename projects/{rtx_ambient_occlusion => rt_ambient_occlusion}/CMakeLists.txt (62%)
 rename projects/{rtx_ambient_occlusion => rt_ambient_occlusion}/README.md (50%)
 rename projects/{rtx_ambient_occlusion => rt_ambient_occlusion}/resources/shaders/ambientOcclusion.rchit (93%)
 rename projects/{rtx_ambient_occlusion => rt_ambient_occlusion}/resources/shaders/ambientOcclusion.rgen (100%)
 rename projects/{rtx_ambient_occlusion => rt_ambient_occlusion}/resources/shaders/ambientOcclusion.rmiss (100%)
 rename projects/{rtx_ambient_occlusion => rt_ambient_occlusion}/src/main.cpp (75%)
 delete mode 100644 projects/rtx_ambient_occlusion/.gitignore
 delete mode 100644 projects/rtx_ambient_occlusion/src/RTX/ASManager.cpp
 delete mode 100644 projects/rtx_ambient_occlusion/src/RTX/ASManager.hpp
 delete mode 100644 projects/rtx_ambient_occlusion/src/RTX/RTX.cpp
 delete mode 100644 projects/rtx_ambient_occlusion/src/RTX/RTX.hpp
 rename screenshots/{rtx_ambient_occlusion.png => rt_ambient_occlusion.png} (100%)

diff --git a/README.md b/README.md
index db60c73d..c45910f2 100644
--- a/README.md
+++ b/README.md
@@ -70,7 +70,7 @@ targets:
  - [particle_simulation](projects/particle_simulation/README.md)
  - [path_tracer](projects/path_tracer/README.md)
  - [ray_tracer](projects/ray_tracer/README.md)
- - [rtx_ambient_occlusion](projects/rtx_ambient_occlusion/README.md)
+ - [rt_ambient_occlusion](projects/rt_ambient_occlusion/README.md)
  - [sph](projects/sph/README.md)
  - [voxelization](projects/voxelization/README.md)
 
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index d7d850f7..3e5b6e5f 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -970,6 +970,16 @@ namespace vkcv {
 		AccelerationStructureHandle createAccelerationStructure(
 				const std::vector<GeometryData> &geometryData);
 		
+		/**
+		 * @brief Creates an acceleration structure handle built with a given list of
+		 * other bottom-level acceleration structures.
+		 *
+		 * @param[in] handles List of acceleration structure handles
+		 * @return Acceleration structure handle
+		 */
+		AccelerationStructureHandle createAccelerationStructure(
+				const std::vector<AccelerationStructureHandle> &handles);
+		
 		/**
 		 * @brief the underlying vulkan handle for an acceleration structure
 		 * by its given acceleration structure handle.
@@ -990,5 +1000,15 @@ namespace vkcv {
 		[[nodiscard]] vk::Buffer getVulkanBuffer(
 				const vkcv::AccelerationStructureHandle &handle) const;
 		
+		/**
+		 * @brief Returns the device address of an acceleration structure represented
+		 * by a given acceleration structure handle.
+		 *
+		 * @param[in] handle Acceleration structure handle
+		 * @return Device address of the acceleration structure
+		 */
+		[[nodiscard]] vk::DeviceAddress getAccelerationStructureDeviceAddress(
+				const vkcv::AccelerationStructureHandle &handle) const;
+		
 	};
 } // namespace vkcv
diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt
index 3860ec7a..322eaa5b 100644
--- a/projects/CMakeLists.txt
+++ b/projects/CMakeLists.txt
@@ -15,6 +15,6 @@ add_subdirectory(mpm)
 add_subdirectory(particle_simulation)
 add_subdirectory(path_tracer)
 add_subdirectory(ray_tracer)
-add_subdirectory(rtx_ambient_occlusion)
+add_subdirectory(rt_ambient_occlusion)
 add_subdirectory(sph)
 add_subdirectory(voxelization)
\ No newline at end of file
diff --git a/projects/rt_ambient_occlusion/.gitignore b/projects/rt_ambient_occlusion/.gitignore
new file mode 100644
index 00000000..774481bc
--- /dev/null
+++ b/projects/rt_ambient_occlusion/.gitignore
@@ -0,0 +1 @@
+rt_ambient_occlusion
\ No newline at end of file
diff --git a/projects/rtx_ambient_occlusion/CMakeLists.txt b/projects/rt_ambient_occlusion/CMakeLists.txt
similarity index 62%
rename from projects/rtx_ambient_occlusion/CMakeLists.txt
rename to projects/rt_ambient_occlusion/CMakeLists.txt
index 7a64f46b..a20dab4d 100644
--- a/projects/rtx_ambient_occlusion/CMakeLists.txt
+++ b/projects/rt_ambient_occlusion/CMakeLists.txt
@@ -1,25 +1,15 @@
 cmake_minimum_required(VERSION 3.16)
-project(rtx_ambient_occlusion)
+project(rt_ambient_occlusion)
 
 # setting c++ standard for the project
 set(CMAKE_CXX_STANDARD 20)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
-set(rtx_source ${PROJECT_SOURCE_DIR}/src/RTX)
-
-set(rtx_sources
-		${rtx_source}/RTX.hpp
-		${rtx_source}/RTX.cpp
-
-		${rtx_source}/ASManager.hpp
-		${rtx_source}/ASManager.cpp
-)
-
 # adding source files to the project
-add_project(rtx_ambient_occlusion src/main.cpp ${rtx_sources})
+add_project(rt_ambient_occlusion src/main.cpp)
 
 # including headers of dependencies and the VkCV framework
-target_include_directories(rtx_ambient_occlusion SYSTEM BEFORE PRIVATE
+target_include_directories(rt_ambient_occlusion SYSTEM BEFORE PRIVATE
 		${vkcv_include}
 		${vkcv_includes}
 		${vkcv_asset_loader_include}
@@ -29,7 +19,7 @@ target_include_directories(rtx_ambient_occlusion SYSTEM BEFORE PRIVATE
 		${vkcv_shader_compiler_include})
 
 # linking with libraries from all dependencies and the VkCV framework
-target_link_libraries(rtx_ambient_occlusion
+target_link_libraries(rt_ambient_occlusion
 		vkcv ${vkcv_libraries}
 		vkcv_asset_loader ${vkcv_asset_loader_libraries}
 		vkcv_camera
diff --git a/projects/rtx_ambient_occlusion/README.md b/projects/rt_ambient_occlusion/README.md
similarity index 50%
rename from projects/rtx_ambient_occlusion/README.md
rename to projects/rt_ambient_occlusion/README.md
index d9fd98cf..14c6c552 100644
--- a/projects/rtx_ambient_occlusion/README.md
+++ b/projects/rt_ambient_occlusion/README.md
@@ -1,7 +1,7 @@
-# RTX ambient occlusion
+# RT ambient occlusion
 An example project to show usage of hardware accelerated ray tracing with the VkCV framework
 
-![Screenshot](../../screenshots/rtx_ambient_occlusion.png)
+![Screenshot](../../screenshots/rt_ambient_occlusion.png)
 
 ## Details
 
@@ -14,12 +14,6 @@ ambient occlusion in realtime.
 
 Here is a list of the used extensions:
 
-- [VK_KHR_get_physical_device_properties2](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_get_physical_device_properties2.html)
-- [VK_KHR_maintenance3](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_maintenance3.html)
-- [VK_KHR_deferred_host_operations](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_deferred_host_operations.html)
-- [VK_KHR_spirv_1_4](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_spirv_1_4.html)
-- [VK_KHR_pipeline_library](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_pipeline_library.html)
-- [VK_EXT_descriptor_indexing](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html)
 - [VK_KHR_buffer_device_address](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_buffer_device_address.html)
 - [VK_KHR_acceleration_structure](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_acceleration_structure.html)
 - [VK_KHR_ray_tracing_pipeline](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_ray_tracing_pipeline.html)
diff --git a/projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rchit b/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rchit
similarity index 93%
rename from projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rchit
rename to projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rchit
index 03806c4f..807fe544 100644
--- a/projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rchit
+++ b/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rchit
@@ -11,12 +11,12 @@ layout(location = 0) rayPayloadInEXT Payload {
   vec3 worldNormal;
 } payload;
 
-layout(binding = 2, set = 0, scalar) buffer rtxVertices
+layout(binding = 2, set = 0, scalar) buffer rtVertices
 {
     float vertices[];
 };
 
-layout(binding = 3, set = 0, scalar) buffer rtxIndices
+layout(binding = 3, set = 0, scalar) buffer rtIndices
 {
     uint16_t indices[];
 };
diff --git a/projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rgen b/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rgen
similarity index 100%
rename from projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rgen
rename to projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rgen
diff --git a/projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rmiss b/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rmiss
similarity index 100%
rename from projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rmiss
rename to projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rmiss
diff --git a/projects/rtx_ambient_occlusion/src/main.cpp b/projects/rt_ambient_occlusion/src/main.cpp
similarity index 75%
rename from projects/rtx_ambient_occlusion/src/main.cpp
rename to projects/rt_ambient_occlusion/src/main.cpp
index 1c491097..fd722518 100644
--- a/projects/rtx_ambient_occlusion/src/main.cpp
+++ b/projects/rt_ambient_occlusion/src/main.cpp
@@ -2,14 +2,13 @@
 #include <vkcv/camera/CameraManager.hpp>
 #include <vkcv/geometry/Teapot.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
-#include "RTX/RTX.hpp"
 
 /**
  * Note: This project is based on the following tutorial https://github.com/Apress/Ray-Tracing-Gems-II/tree/main/Chapter_16.
  */
 
 int main(int argc, const char** argv) {
-	const std::string applicationName = "RTX Ambient Occlusion";
+	const std::string applicationName = "Ray Tracing: Ambient Occlusion";
 	
 	vkcv::Features features;
 	features.requireExtension(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME);
@@ -49,8 +48,6 @@ int main(int argc, const char** argv) {
 			features
 	);
 
-	vkcv::rtx::ASManager asManager(&core);
-
 	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, 800, 600, true);
 	
 	vkcv::geometry::Teapot teapot (glm::vec3(0.0f), 1.0f);
@@ -65,43 +62,36 @@ int main(int argc, const char** argv) {
 
 	vkcv::shader::GLSLCompiler compiler (vkcv::shader::GLSLCompileTarget::RAY_TRACING);
 
-	vkcv::ShaderProgram rtxShaderProgram;
+	vkcv::ShaderProgram shaderProgram;
 	compiler.compile(vkcv::ShaderStage::RAY_GEN, std::filesystem::path("resources/shaders/ambientOcclusion.rgen"),
-		[&rtxShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-            rtxShaderProgram.addShader(shaderStage, path);
+		[&shaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+            shaderProgram.addShader(shaderStage, path);
 		});
 
 	compiler.compile(vkcv::ShaderStage::RAY_CLOSEST_HIT, std::filesystem::path("resources/shaders/ambientOcclusion.rchit"),
-		[&rtxShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-            rtxShaderProgram.addShader(shaderStage, path);
+		[&shaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+            shaderProgram.addShader(shaderStage, path);
 		});
 
 	compiler.compile(vkcv::ShaderStage::RAY_MISS, std::filesystem::path("resources/shaders/ambientOcclusion.rmiss"),
-		[&rtxShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
-            rtxShaderProgram.addShader(shaderStage, path);
+		[&shaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
+            shaderProgram.addShader(shaderStage, path);
 		});
 
 	std::vector<vkcv::DescriptorSetHandle> descriptorSetHandles;
 	std::vector<vkcv::DescriptorSetLayoutHandle> descriptorSetLayoutHandles;
 
-	vkcv::DescriptorSetLayoutHandle rtxShaderDescriptorSetLayout = core.createDescriptorSetLayout(rtxShaderProgram.getReflectedDescriptors().at(0));
-	vkcv::DescriptorSetHandle rtxShaderDescriptorSet = core.createDescriptorSet(rtxShaderDescriptorSetLayout);
-	descriptorSetHandles.push_back(rtxShaderDescriptorSet);
-	descriptorSetLayoutHandles.push_back(rtxShaderDescriptorSetLayout);
+	vkcv::DescriptorSetLayoutHandle shaderDescriptorSetLayout = core.createDescriptorSetLayout(shaderProgram.getReflectedDescriptors().at(0));
+	vkcv::DescriptorSetHandle shaderDescriptorSet = core.createDescriptorSet(shaderDescriptorSetLayout);
+	descriptorSetHandles.push_back(shaderDescriptorSet);
+	descriptorSetLayoutHandles.push_back(shaderDescriptorSetLayout);
 	
 	vkcv::AccelerationStructureHandle blas = core.createAccelerationStructure({ geometryData });
-	
-	asManager.add(geometryData, blas);
-
-	// init RTXModule
-	vkcv::rtx::RTXModule rtxModule (
-			&core,
-			&asManager,
-			descriptorSetHandles
-	);
+	vkcv::AccelerationStructureHandle tlas = core.createAccelerationStructure({ blas });
 	
 	{
 		vkcv::DescriptorWrites writes;
+		writes.writeAcceleration(1, { core.getVulkanAccelerationStructure(tlas) });
 		writes.writeStorageBuffer(2, geometryData.getVertexBufferBinding().buffer);
 		writes.writeStorageBuffer(3, geometryData.getIndexBuffer());
 		core.writeDescriptorSet(descriptorSetHandles[0], writes);
@@ -114,16 +104,14 @@ int main(int argc, const char** argv) {
 	    glm::vec4 camera_forward;    // for computing ray direction
 	};
 	
-	auto rtxPipeline = core.createRayTracingPipeline(vkcv::RayTracingPipelineConfig(
-			rtxShaderProgram,
+	auto pipeline = core.createRayTracingPipeline(vkcv::RayTracingPipelineConfig(
+			shaderProgram,
 			descriptorSetLayoutHandles
 	));
 
 	vkcv::ImageHandle depthBuffer;
 
 	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
-
-	vkcv::DescriptorWrites rtxWrites;
 	
 	core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
 				 uint32_t swapchainWidth, uint32_t swapchainHeight) {
@@ -149,22 +137,25 @@ int main(int argc, const char** argv) {
 		raytracingPushData.camera_up = glm::vec4(cameraManager.getActiveCamera().getUp(),0);
 		raytracingPushData.camera_forward = glm::vec4(cameraManager.getActiveCamera().getFront(),0);
 
-		vkcv::PushConstants pushConstantsRTX = vkcv::pushConstants<RaytracingPushConstantData>();
-		pushConstantsRTX.appendDrawcall(raytracingPushData);
+		vkcv::PushConstants pushConstants = vkcv::pushConstants<RaytracingPushConstantData>();
+		pushConstants.appendDrawcall(raytracingPushData);
+		
+		{
+			vkcv::DescriptorWrites writes;
+			writes.writeStorageImage(0, swapchainInput);
+			core.writeDescriptorSet(shaderDescriptorSet, writes);
+		}
 
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 
-		rtxWrites.writeStorageImage(0, swapchainInput);
-		core.writeDescriptorSet(rtxShaderDescriptorSet, rtxWrites);
-
 		core.prepareImageForStorage(cmdStream, swapchainInput);
 
 		core.recordRayGenerationToCmdStream(
 			cmdStream,
-			rtxPipeline,
+			pipeline,
 			vkcv::DispatchSize(swapchainWidth, swapchainHeight),
-			{ vkcv::useDescriptorSet(0, rtxShaderDescriptorSet) },
-			pushConstantsRTX,
+			{ vkcv::useDescriptorSet(0, shaderDescriptorSet) },
+			pushConstants,
 			windowHandle
 		);
 
diff --git a/projects/rtx_ambient_occlusion/.gitignore b/projects/rtx_ambient_occlusion/.gitignore
deleted file mode 100644
index 61b2ff94..00000000
--- a/projects/rtx_ambient_occlusion/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-rtx_ambient_occlusion
\ No newline at end of file
diff --git a/projects/rtx_ambient_occlusion/src/RTX/ASManager.cpp b/projects/rtx_ambient_occlusion/src/RTX/ASManager.cpp
deleted file mode 100644
index b18cb345..00000000
--- a/projects/rtx_ambient_occlusion/src/RTX/ASManager.cpp
+++ /dev/null
@@ -1,468 +0,0 @@
-#include "ASManager.hpp"
-#include <array>
-
-namespace vkcv::rtx {
-    
-
-    ASManager::ASManager(vkcv::Core *core) :
-    m_core(core),
-    m_device(&(core->getContext().getDevice())){
-        // INFO: Using RTX extensions implies that we cannot use the standard dispatcher from Vulkan because using RTX
-        // specific functions via vk::Device will result in validation errors. Instead we need to use a
-        // vk::DispatchLoaderDynamic which is used as dispatcher parameter of the device functions.
-        m_rtxDispatcher = vk::DispatchLoaderDynamic( (PFN_vkGetInstanceProcAddr) m_core->getContext().getInstance().getProcAddr("vkGetInstanceProcAddr") );
-        m_rtxDispatcher.init(m_core->getContext().getInstance());
-
-        // TODO: Recursive call of buildBLAS for bigger scenes. Currently, the RTX module only supports one mesh.
-    }
-
-    ASManager::~ASManager() noexcept {
-        m_rtxDispatcher = vk::DispatchLoaderDynamic( (PFN_vkGetInstanceProcAddr) m_core->getContext().getInstance().getProcAddr("vkGetInstanceProcAddr") );
-        m_rtxDispatcher.init(m_core->getContext().getInstance());
-
-        // destroy every BLAS, its data containers and free used memory blocks
-        for (size_t i=0; i < m_bottomLevelAccelerationStructures.size(); i++) {
-            BottomLevelAccelerationStructure blas = m_bottomLevelAccelerationStructures[i];
-            //m_core->getContext().getDevice().destroyAccelerationStructureKHR(blas.vulkanHandle, nullptr, m_rtxDispatcher);
-        }
-
-        // destroy the TLAS, its data containers and free used memory blocks
-        TopLevelAccelerationStructure tlas = m_topLevelAccelerationStructure;
-        m_core->getContext().getDevice().destroyAccelerationStructureKHR(tlas.vulkanHandle, nullptr, m_rtxDispatcher);
-        m_core->getContext().getDevice().destroy(tlas.tlasBuffer.vulkanHandle);
-        m_core->getContext().getDevice().destroy(tlas.gpuBufferInstances.vulkanHandle);
-
-        m_core->getContext().getDevice().freeMemory(tlas.tlasBuffer.deviceMemory);
-        m_core->getContext().getDevice().freeMemory(tlas.gpuBufferInstances.deviceMemory);
-    }
-
-
-  
-    vk::CommandPool ASManager::createCommandPool() {
-        vk::CommandPool commandPool;
-        vk::CommandPoolCreateInfo commandPoolCreateInfo;
-        commandPoolCreateInfo.setQueueFamilyIndex(m_core->getContext().getQueueManager().getComputeQueues()[0].familyIndex);
-        vk::Result res = m_device->createCommandPool(&commandPoolCreateInfo, nullptr, &commandPool);
-        if (res != vk::Result::eSuccess) {
-            vkcv_log(LogLevel::ERROR, "ASManager: command pool could not be created! (%s)", vk::to_string(res).c_str());
-        }
-        return commandPool;
-    };
-
-    vk::CommandBuffer ASManager::createAndBeginCommandBuffer(vk::CommandPool commandPool)
-    {
-        vk::CommandBufferAllocateInfo commandBufferAllocateInfo{};
-        commandBufferAllocateInfo.setLevel(vk::CommandBufferLevel::ePrimary);
-        commandBufferAllocateInfo.setCommandPool(commandPool);
-        commandBufferAllocateInfo.setCommandBufferCount(1);
-        vk::CommandBuffer commandBuffer;
-        vk::Result result = m_device->allocateCommandBuffers(&commandBufferAllocateInfo, &commandBuffer);
-        if (result != vk::Result::eSuccess) {
-            vkcv_log(LogLevel::ERROR, "ASManager: command buffer for Acceleration Strucutre Build could not be allocated! (%s)", vk::to_string(result).c_str());
-        }
-		
-		const vk::CommandBufferBeginInfo beginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
-		commandBuffer.begin(beginInfo);
-        return commandBuffer;
-    }
-
-    void ASManager::submitCommandBuffer(vk::CommandPool commandPool, vk::CommandBuffer& commandBuffer)
-    {
-        commandBuffer.end();
-
-        vk::SubmitInfo submitInfo;
-        submitInfo.setCommandBufferCount(1);
-        submitInfo.setPCommandBuffers(&commandBuffer);
-
-        vkcv::Queue queue = m_core->getContext().getQueueManager().getComputeQueues()[0];
-
-        queue.handle.submit(submitInfo);
-        queue.handle.waitIdle();
-
-        m_device->freeCommandBuffers(commandPool, 1, &commandBuffer);
-        m_device->destroyCommandPool(commandPool);
-    }
-
-    vk::DeviceAddress ASManager::getBufferDeviceAddress(vk::Buffer buffer)
-    {
-        vk::BufferDeviceAddressInfo bufferDeviceAddressInfo(buffer);
-        return m_device->getBufferAddress(bufferDeviceAddressInfo);
-    }
-
-    void ASManager::createBuffer(RTXBuffer& buffer) {
-
-        vk::BufferCreateInfo bufferCreateInfo;
-        bufferCreateInfo.setFlags(vk::BufferCreateFlags());
-        bufferCreateInfo.setUsage(buffer.bufferUsageFlagBits);
-        bufferCreateInfo.setSize(buffer.deviceSize);
-
-        buffer.vulkanHandle = m_device->createBuffer(bufferCreateInfo);
-        vk::MemoryRequirements2 memoryRequirements2;
-        vk::MemoryDedicatedRequirements dedicatedRequirements;
-        vk::BufferMemoryRequirementsInfo2 bufferRequirements;
-
-        bufferRequirements.setBuffer(buffer.vulkanHandle);
-        memoryRequirements2.pNext = &dedicatedRequirements;
-        m_device->getBufferMemoryRequirements2(&bufferRequirements, &memoryRequirements2); 
-
-        vk::PhysicalDeviceMemoryProperties physicalDeviceMemoryProperties = m_core->getContext().getPhysicalDevice().getMemoryProperties();
-
-        uint32_t memoryTypeIndex = -1;
-        for (size_t i = 0; i < physicalDeviceMemoryProperties.memoryTypeCount; i++) {
-            if ((memoryRequirements2.memoryRequirements.memoryTypeBits & (1 << i))
-                    && (physicalDeviceMemoryProperties.memoryTypes[i].propertyFlags & buffer.memoryPropertyFlagBits) == buffer.memoryPropertyFlagBits) {
-                memoryTypeIndex = i;
-                break;
-            }
-        }
-
-        vk::MemoryAllocateInfo memoryAllocateInfo(
-            memoryRequirements2.memoryRequirements.size,  // size of allocation in bytes
-            memoryTypeIndex // index identifying a memory type from the memoryTypes array of the vk::PhysicalDeviceMemoryProperties structure.
-        );
-        vk::MemoryAllocateFlagsInfo allocateFlagsInfo(
-            vk::MemoryAllocateFlagBits::eDeviceAddress  // vk::MemoryAllocateFlags
-        );
-        memoryAllocateInfo.setPNext(&allocateFlagsInfo);  // extend memory allocate info with allocate flag info
-        buffer.deviceMemory = m_device->allocateMemory(memoryAllocateInfo);
-
-        uint32_t memoryOffset = 0;
-        m_device->bindBufferMemory(buffer.vulkanHandle, buffer.deviceMemory, memoryOffset);
-
-        // only fill data in case of CPU buffer
-        if (buffer.bufferType == RTXBufferType::STAGING) {
-            void* mapped = m_device->mapMemory(buffer.deviceMemory, memoryOffset, buffer.deviceSize);
-            std::memcpy(mapped, buffer.data, buffer.deviceSize);
-            m_device->unmapMemory(buffer.deviceMemory);
-        }
-    }
-
-    void ASManager::copyFromCPUToGPU(RTXBuffer& cpuBuffer, RTXBuffer& gpuBuffer) {
-        vk::CommandPool commandPool= createCommandPool();
-        vk::CommandBuffer commandBuffer= createAndBeginCommandBuffer(commandPool);
-        vk::BufferCopy bufferCopy;
-        bufferCopy.size = cpuBuffer.deviceSize;
-        commandBuffer.copyBuffer(cpuBuffer.vulkanHandle, gpuBuffer.vulkanHandle, 1, &bufferCopy);
-
-        submitCommandBuffer(commandPool,commandBuffer);     
-
-        m_device->destroyBuffer(cpuBuffer.vulkanHandle);
-        m_device->freeMemory(cpuBuffer.deviceMemory);
-    }
-
-    void ASManager::buildBLAS(const vkcv::VertexData &vertexData) {
-        // TODO: organize hierarchical structure of multiple BLAS
-		
-		const auto& originalVertexBuffer = vertexData.getVertexBufferBindings()[0].buffer;
-		const auto& originalIndexBuffer = vertexData.getIndexBuffer();
-		
-		const auto vbSize = m_core->getBufferSize(originalVertexBuffer);
-
-        vk::DeviceAddress vertexBufferAddress = m_core->getBufferDeviceAddress(originalVertexBuffer);//getBufferDeviceAddress(vertexBuffer.vulkanHandle);
-        vk::DeviceAddress indexBufferAddress = m_core->getBufferDeviceAddress(originalIndexBuffer);//getBufferDeviceAddress(indexBuffer.vulkanHandle);
-
-        // triangle mesh data
-        vk::AccelerationStructureGeometryTrianglesDataKHR asTriangles(
-                vk::Format::eR32G32B32Sfloat,   // vertex format
-                vertexBufferAddress, // vertex buffer address (vk::DeviceOrHostAddressConstKHR)
-                3 * sizeof(float), // vertex stride (vk::DeviceSize)
-                uint32_t(vbSize / (3 * sizeof(float)) - 1), // maxVertex (uint32_t)
-                vk::IndexType::eUint16, // indexType (vk::IndexType) --> INFO: UINT16 oder UINT32!
-                indexBufferAddress, // indexData (vk::DeviceOrHostAddressConstKHR)
-                {} // transformData (vk::DeviceOrHostAddressConstKHR)
-        );
-
-        // Geometry data
-        vk::AccelerationStructureGeometryKHR asGeometry(
-                vk::GeometryTypeKHR::eTriangles, // The geometry type, e.g. triangles, AABBs, instances
-                asTriangles, // the geometry data
-                vk::GeometryFlagBitsKHR::eOpaque // This flag disables any-hit shaders to increase ray tracing performance
-        );
-
-        // Ranges for data lists
-        vk::AccelerationStructureBuildRangeInfoKHR asRangeInfo(
-                uint32_t(vertexData.getCount() / 3), // the primitiveCount (uint32_t)
-                0, // primitiveOffset (uint32_t)
-                0, // firstVertex (uint32_t)
-                0  // transformOffset (uint32_t)
-        );
-
-        // Settings and array of geometries to build into BLAS
-        vk::AccelerationStructureBuildGeometryInfoKHR asBuildInfo(
-                vk::AccelerationStructureTypeKHR::eBottomLevel, // type of the AS: bottom vs. top
-                vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace, // some flags for different purposes, e.g. efficiency
-                vk::BuildAccelerationStructureModeKHR::eBuild, // AS mode: build vs. update
-                {}, // src AS (this seems to be for copying AS)
-                {}, // dst AS (this seems to be for copying AS)
-                1, // the geometryCount. TODO: how many do we need?
-                &asGeometry // the next input entry would be a pointer to a pointer to geometries. Maybe geometryCount depends on the next entry?
-        );
-
-        // Calculate memory needed for AS
-        vk::AccelerationStructureBuildSizesInfoKHR asBuildSizesInfo;
-        m_device->getAccelerationStructureBuildSizesKHR(
-                vk::AccelerationStructureBuildTypeKHR::eDevice, // build on device instead of host
-                &asBuildInfo, // pointer to build info
-                &asRangeInfo.primitiveCount, // array of number of primitives per geometry
-                &asBuildSizesInfo,  // output pointer to store sizes
-                m_rtxDispatcher
-                );
-
-        // create buffer for acceleration structure
-        RTXBuffer blasBuffer;
-        blasBuffer.bufferType = RTXBufferType::ACCELERATION;
-        blasBuffer.deviceSize = asBuildSizesInfo.accelerationStructureSize;
-        blasBuffer.bufferUsageFlagBits = vk::BufferUsageFlagBits::eAccelerationStructureStorageKHR
-                | vk::BufferUsageFlagBits::eShaderDeviceAddress
-                | vk::BufferUsageFlagBits::eStorageBuffer;
-        blasBuffer.memoryPropertyFlagBits = { vk::MemoryPropertyFlagBits::eDeviceLocal };
-
-        createBuffer(blasBuffer); 
-
-        // Create an empty AS object
-        vk::AccelerationStructureCreateInfoKHR asCreateInfo(
-            vk::AccelerationStructureCreateFlagsKHR(), // creation flags
-            blasBuffer.vulkanHandle, // allocated AS buffer.
-            0,
-            asBuildSizesInfo.accelerationStructureSize, // size of the AS
-            asBuildInfo.type // type of the AS
-        );
-
-        // Create the intended AS object
-        vk::AccelerationStructureKHR blasKHR;
-        vk::Result res = m_device->createAccelerationStructureKHR(
-            &asCreateInfo, // AS create info
-            nullptr, // allocator callbacks
-            &blasKHR, // the AS
-            m_rtxDispatcher
-        );
-        if (res != vk::Result::eSuccess) {
-            vkcv_log(vkcv::LogLevel::ERROR, "The Bottom Level Acceleration Structure could not be build! (%s)", vk::to_string(res).c_str());
-        }
-        asBuildInfo.setDstAccelerationStructure(blasKHR);
-
-        // Create temporary scratch buffer used for building the AS
-        RTXBuffer scratchBuffer;
-        scratchBuffer.bufferType = RTXBufferType::SCRATCH;
-        scratchBuffer.deviceSize = asBuildSizesInfo.buildScratchSize;
-        scratchBuffer.bufferUsageFlagBits = vk::BufferUsageFlagBits::eShaderDeviceAddress
-            | vk::BufferUsageFlagBits::eStorageBuffer;
-        scratchBuffer.memoryPropertyFlagBits = { vk::MemoryPropertyFlagBits::eDeviceLocal };
-
-        createBuffer(scratchBuffer);
-
-        asBuildInfo.setScratchData(getBufferDeviceAddress(scratchBuffer.vulkanHandle));
-
-        // Pointer to rangeInfo, used later for build
-        vk::AccelerationStructureBuildRangeInfoKHR* pointerToRangeInfo = &asRangeInfo;
-
-        vk::CommandPool commandPool = createCommandPool();
-        vk::CommandBuffer commandBuffer = createAndBeginCommandBuffer(commandPool);
-
-        commandBuffer.buildAccelerationStructuresKHR(1, &asBuildInfo, &pointerToRangeInfo, m_rtxDispatcher);
-
-        submitCommandBuffer(commandPool, commandBuffer);
-  
-        m_core->getContext().getDevice().destroyBuffer(scratchBuffer.vulkanHandle, nullptr, m_rtxDispatcher);
-        m_core->getContext().getDevice().freeMemory(scratchBuffer.deviceMemory, nullptr, m_rtxDispatcher);
-                
-        BottomLevelAccelerationStructure blas = {
-                vertexData.getVertexBufferBindings()[0].buffer,
-                vertexData.getIndexBuffer(),
-				nullptr,
-                blasKHR
-        };
-        m_bottomLevelAccelerationStructures.push_back(blas);
-    }
-	
-	void ASManager::add(const vkcv::GeometryData &geometryData, const vkcv::AccelerationStructureHandle &blas) {
-		BottomLevelAccelerationStructure blasEntry = {
-				geometryData.getVertexBufferBinding().buffer,
-				geometryData.getIndexBuffer(),
-				m_core->getVulkanBuffer(blas),
-				m_core->getVulkanAccelerationStructure(blas)
-		};
-		
-		m_bottomLevelAccelerationStructures.push_back(blasEntry);
-	}
-
-    void ASManager::buildTLAS() {
-        // TODO: organize hierarchical structure of multiple BLAS
-
-        // We need an the device address of each BLAS --> TODO: for loop for bigger scenes
-        vk::AccelerationStructureDeviceAddressInfoKHR addressInfo(
-            m_bottomLevelAccelerationStructures[0].vulkanHandle
-        );
-        vk::DeviceAddress blasAddress = m_device->getAccelerationStructureAddressKHR(&addressInfo, m_rtxDispatcher);
-
-        std::array<std::array<float, 4>, 3> transformMatrix = {
-                std::array<float, 4>{1.f, 0.f, 0.f, 0.f},
-                std::array<float, 4>{0.f, 1.f, 0.f, 0.f},
-                std::array<float, 4>{0.f, 0.f, 1.f, 0.f},
-        };
-
-        vk::TransformMatrixKHR transformMatrixKhr(
-            transformMatrix   // std::array<std::array<float,4>,3> const&
-        );
-
-        vk::AccelerationStructureInstanceKHR accelerationStructureInstanceKhr(
-            transformMatrixKhr,    // vk::TransformMatrixKHR transform_ = {},
-            0,   // uint32_t instanceCustomIndex,
-            0xFF,    //uint32_t mask_ = {},
-            0,  // uint32_t instanceShaderBindingTableRecordOffset,
-            vk::GeometryInstanceFlagBitsKHR::eTriangleFacingCullDisable,    // vk::GeometryInstanceFlagsKHR
-            blasAddress // uint64_t accelerationStructureReference (the device address of the BLAS)
-        );
-
-        // create a buffer of instances on the device and upload the array of instances to it
-        RTXBuffer stagingBufferInstances;
-        stagingBufferInstances.bufferType = RTXBufferType::STAGING;
-        stagingBufferInstances.deviceSize = sizeof(accelerationStructureInstanceKhr);
-        stagingBufferInstances.data = &accelerationStructureInstanceKhr;
-        stagingBufferInstances.bufferUsageFlagBits = vk::BufferUsageFlagBits::eShaderDeviceAddress
-            | vk::BufferUsageFlagBits::eTransferSrc;
-        stagingBufferInstances.memoryPropertyFlagBits = vk::MemoryPropertyFlagBits::eHostCoherent
-            | vk::MemoryPropertyFlagBits::eHostVisible;
-
-        createBuffer(stagingBufferInstances);
-
-        RTXBuffer bufferInstances;
-        bufferInstances.bufferType = RTXBufferType::GPU;
-        bufferInstances.deviceSize = sizeof(accelerationStructureInstanceKhr);
-        bufferInstances.bufferUsageFlagBits = vk::BufferUsageFlagBits::eShaderDeviceAddress
-            | vk::BufferUsageFlagBits::eTransferDst
-			| vk::BufferUsageFlagBits::eAccelerationStructureBuildInputReadOnlyKHR;
-        bufferInstances.memoryPropertyFlagBits = vk::MemoryPropertyFlagBits::eDeviceLocal;
-
-        createBuffer(bufferInstances);
-        copyFromCPUToGPU(stagingBufferInstances, bufferInstances);   // automatically deletes and frees memory of stagingBufferInstances
-
-        vk::MemoryBarrier barrier;
-        barrier.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite);
-        barrier.setDstAccessMask(vk::AccessFlagBits::eAccelerationStructureWriteKHR);
-        vk::CommandPool commandPool = createCommandPool();
-        vk::CommandBuffer commandBuffer = createAndBeginCommandBuffer(commandPool);
-        commandBuffer.pipelineBarrier(
-            vk::PipelineStageFlagBits::eTransfer,
-            vk::PipelineStageFlagBits::eAccelerationStructureBuildKHR,
-            {},1,&barrier,0, nullptr,0,nullptr);
-        submitCommandBuffer(commandPool, commandBuffer);
-
-
-        // ranging information for TLAS build
-        vk::AccelerationStructureBuildRangeInfoKHR asRangeInfo(
-            1, // primitiveCount -> number of instances
-            0, // primitiveOffset
-            0, // firstVertex
-            0 //transformOffset
-        );
-
-        vk::DeviceAddress bufferInstancesAddress = getBufferDeviceAddress(bufferInstances.vulkanHandle);
-
-        vk::AccelerationStructureGeometryInstancesDataKHR asInstances(
-            false,    // vk::Bool32 arrayOfPointers
-            bufferInstancesAddress    // vk::DeviceOrHostAddressConstKHR data_ = {}
-        );
-
-        // Geometry, in this case instances of BLAS
-        vk::AccelerationStructureGeometryKHR asGeometry(
-            vk::GeometryTypeKHR::eInstances,    // vk::GeometryTypeKHR geometryType_ = vk::GeometryTypeKHR::eTriangles
-            asInstances,    // vk::AccelerationStructureGeometryDataKHR geometry_ = {}
-            {}    // vk::GeometryFlagsKHR flags_ = {}
-        );
-
-        // Finally, create the TLAS
-        vk::AccelerationStructureBuildGeometryInfoKHR asBuildInfo(
-            vk::AccelerationStructureTypeKHR::eTopLevel, // type of the AS: bottom vs. top
-            vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace, // some flags for different purposes, e.g. efficiency
-            vk::BuildAccelerationStructureModeKHR::eBuild, // AS mode: build vs. update
-            {}, // src AS (this seems to be for copying AS)
-            {}, // dst AS (this seems to be for copying AS)
-            1, // the geometryCount.
-            &asGeometry // the next input entry would be a pointer to a pointer to geometries. Maybe geometryCount depends on the next entry?
-        );
-
-        // AS size and scratch space size, given by count of instances (1)
-        vk::AccelerationStructureBuildSizesInfoKHR asSizeInfo;
-        m_core->getContext().getDevice().getAccelerationStructureBuildSizesKHR(
-            vk::AccelerationStructureBuildTypeKHR::eDevice,
-            &asBuildInfo,
-            &asRangeInfo.primitiveCount,
-            &asSizeInfo,
-            m_rtxDispatcher
-        );
-
-        // Create buffer for the TLAS
-        RTXBuffer tlasBuffer;
-        tlasBuffer.bufferType = RTXBufferType::ACCELERATION;
-        tlasBuffer.deviceSize = asSizeInfo.accelerationStructureSize;
-        tlasBuffer.bufferUsageFlagBits = vk::BufferUsageFlagBits::eAccelerationStructureStorageKHR
-            | vk::BufferUsageFlagBits::eShaderDeviceAddress
-            | vk::BufferUsageFlagBits::eStorageBuffer;
-
-        createBuffer(tlasBuffer);
-
-        // Create empty TLAS object
-        vk::AccelerationStructureCreateInfoKHR asCreateInfo(
-            {}, // creation flags
-            tlasBuffer.vulkanHandle, // allocated AS buffer.
-            0,
-            asSizeInfo.accelerationStructureSize, // size of the AS
-            asBuildInfo.type // type of the AS
-        );
-
-        vk::AccelerationStructureKHR tlas;
-        vk::Result res = m_device->createAccelerationStructureKHR(&asCreateInfo, nullptr, &tlas, m_rtxDispatcher);
-        if (res != vk::Result::eSuccess) {
-            vkcv_log(LogLevel::ERROR, "Top Level Acceleration Structure could not be created! (%s)", vk::to_string(res).c_str());
-        }
-        asBuildInfo.setDstAccelerationStructure(tlas);
-
-        // Create temporary scratch buffer used for building the AS
-        RTXBuffer tlasScratchBuffer;  // scratch buffer
-        tlasScratchBuffer.bufferType = RTXBufferType::ACCELERATION;
-        tlasScratchBuffer.deviceSize = asSizeInfo.buildScratchSize;
-        tlasScratchBuffer.bufferUsageFlagBits = vk::BufferUsageFlagBits::eShaderDeviceAddressKHR
-            | vk::BufferUsageFlagBits::eStorageBuffer;
-
-        createBuffer(tlasScratchBuffer);
-
-        vk::BufferDeviceAddressInfo tempBuildDataBufferDeviceAddressInfo(tlasScratchBuffer.vulkanHandle);
-        vk::DeviceAddress tempBuildBufferDataAddress = m_core->getContext().getDevice().getBufferAddressKHR(tempBuildDataBufferDeviceAddressInfo, m_rtxDispatcher);
-        asBuildInfo.setScratchData(tempBuildBufferDataAddress);
-
-        // Pointer to rangeInfo, used later for build
-        vk::AccelerationStructureBuildRangeInfoKHR* pointerToRangeInfo = &asRangeInfo;
-
-        // Build the TLAS.
-
-        vk::CommandPool commandPool2 = createCommandPool();
-        vk::CommandBuffer commandBuffer2 = createAndBeginCommandBuffer(commandPool2);
-        commandBuffer2.buildAccelerationStructuresKHR(1, &asBuildInfo, &pointerToRangeInfo, m_rtxDispatcher);
-        submitCommandBuffer(commandPool2, commandBuffer2);
-        
-        m_device->destroyBuffer(tlasScratchBuffer.vulkanHandle, nullptr, m_rtxDispatcher);
-        m_device->freeMemory(tlasScratchBuffer.deviceMemory, nullptr, m_rtxDispatcher);
-
-        m_topLevelAccelerationStructure = {
-                bufferInstances,
-                tlasBuffer,
-                tlasScratchBuffer,
-                tlas
-        };
-    }
-
-    TopLevelAccelerationStructure ASManager::getTLAS()
-    {
-        return m_topLevelAccelerationStructure;
-    }
-
-    BottomLevelAccelerationStructure ASManager::getBLAS(uint32_t id)
-    {
-        return m_bottomLevelAccelerationStructures[id];
-    }
-
-    const vk::DispatchLoaderDynamic& ASManager::getDispatcher() {
-        return m_rtxDispatcher;
-    }    
-}
\ No newline at end of file
diff --git a/projects/rtx_ambient_occlusion/src/RTX/ASManager.hpp b/projects/rtx_ambient_occlusion/src/RTX/ASManager.hpp
deleted file mode 100644
index 825d5e65..00000000
--- a/projects/rtx_ambient_occlusion/src/RTX/ASManager.hpp
+++ /dev/null
@@ -1,187 +0,0 @@
-#pragma once
-
-#include <vkcv/Core.hpp>
-
-namespace vkcv::rtx {
-
-    /**
-     * @brief Used for @#RTXBuffer creation depending on the @#RTXBufferType.
-     */
-    enum class RTXBufferType {
-        STAGING,
-        GPU,
-        ACCELERATION,
-        SHADER_BINDING,
-        SCRATCH
-    };
-
-    /**
-     * @brief Used as a container to handle buffer creation and destruction in RTX-specific use cases.
-     */
-    struct RTXBuffer {
-        RTXBufferType bufferType;
-        void* data;
-        vk::DeviceSize deviceSize;
-        vk::DeviceMemory deviceMemory;
-        vk::BufferUsageFlags bufferUsageFlagBits;
-        vk::MemoryPropertyFlags memoryPropertyFlagBits;
-        vk::Buffer vulkanHandle;
-    };
-
-    /**
-     * @brief Used as a container to handle bottom-level acceleration structure (BLAS) construction and destruction.
-     */
-    struct BottomLevelAccelerationStructure {
-        vkcv::BufferHandle vertexBuffer;
-		vkcv::BufferHandle indexBuffer;
-		vk::Buffer accelerationBuffer;
-        vk::AccelerationStructureKHR vulkanHandle;
-    };
-
-    /**
-     * @brief Used as a container to handle top-level acceleration structure (TLAS) construction and destruction.
-     */
-    struct TopLevelAccelerationStructure {
-        RTXBuffer gpuBufferInstances;
-        RTXBuffer tlasBuffer;
-        RTXBuffer tempBuildDataBuffer;  // scratch buffer
-        vk::AccelerationStructureKHR vulkanHandle;
-    };
-
-    /**
-     * @brief A class for managing acceleration structures (bottom, top).
-     */
-    class ASManager {
-    private:
-        Core* m_core;
-        const vk::Device* m_device;
-        std::vector<BottomLevelAccelerationStructure> m_bottomLevelAccelerationStructures;
-        TopLevelAccelerationStructure m_topLevelAccelerationStructure;
-        vk::DispatchLoaderDynamic m_rtxDispatcher;
-        
-        /**
-         * Creates a command pool.
-         */
-        vk::CommandPool createCommandPool();
-
-        /**
-         * @brief Takes a @p cmdPool, allocates a command buffer and starts recording it.
-         * @param cmdPool The command pool.
-         * @return The allocated command buffer.
-         */
-        vk::CommandBuffer createAndBeginCommandBuffer( vk::CommandPool cmdPool);
-
-        /**
-         * @brief Ends the @p commandBuffer,submits it and waits. Afterwards frees the @p commandBuffer.
-         * @param commandPool The command pool.
-         * @param commandBuffer The command buffer.
-         */
-        void submitCommandBuffer(vk::CommandPool commandPool, vk::CommandBuffer& commandBuffer);
-
-        /**
-         * @brief Gets the device address of a @p buffer.
-         * @param buffer The buffer.
-         * @return The device address of the @p buffer.
-         */
-        vk::DeviceAddress getBufferDeviceAddress(vk::Buffer buffer);
-
-        /**
-         * @brief Copies @p cpuBuffer data into a @p gpuBuffer. Typical use case is a staging buffer (namely,
-         * @p cpuBuffer) used to fill a @p gpuBuffer with @p vk::MemoryPropertyFlagBits::eDeviceLocal flag set.
-         * @p cpuBuffer is destroyed and freed after copying.
-         * @param cpuBuffer
-         * @param gpuBuffer
-         */
-        void copyFromCPUToGPU(RTXBuffer &cpuBuffer, RTXBuffer &gpuBuffer);
-
-    public:
-
-        /**
-         * @brief Constructor of @#ASManager .
-         * @param core
-         */
-        ASManager(vkcv::Core *core);
-
-        /**
-         * @brief Default destructor of @#ASManager.
-         */
-        ~ASManager();
-
-        /**
-         * @brief Returns a @#RTXBuffer object holding data of type @p T.
-         * @param data The input data of type @p T.
-         * @return A @#RTXBuffer object holding @p data of type @p T.
-         */
-        template<class T>
-        RTXBuffer makeBufferFromData(std::vector<T>& data) {
-
-            // first: Staging Buffer creation
-            RTXBuffer stagingBuffer;
-            stagingBuffer.bufferType = RTXBufferType::STAGING;
-            stagingBuffer.deviceSize = sizeof(T) * data.size();
-            stagingBuffer.data = data.data();
-            stagingBuffer.bufferUsageFlagBits = vk::BufferUsageFlagBits::eTransferSrc;
-            stagingBuffer.memoryPropertyFlagBits = vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eHostVisible;
-
-            createBuffer(stagingBuffer);
-
-            // second: create AS Buffer
-            RTXBuffer targetBuffer;
-            targetBuffer.bufferType = RTXBufferType::GPU;
-            targetBuffer.deviceSize = sizeof(T) * data.size();
-            targetBuffer.bufferUsageFlagBits = vk::BufferUsageFlagBits::eAccelerationStructureBuildInputReadOnlyKHR
-                | vk::BufferUsageFlagBits::eTransferDst
-                | vk::BufferUsageFlagBits::eStorageBuffer
-                | vk::BufferUsageFlagBits::eShaderDeviceAddress;
-            targetBuffer.memoryPropertyFlagBits = vk::MemoryPropertyFlagBits::eDeviceLocal;
-
-            createBuffer(targetBuffer);
-
-            // copy from CPU to GPU
-            copyFromCPUToGPU(stagingBuffer, targetBuffer);
-
-            return targetBuffer;
-        }
-
-        /**
-        * @brief A helper function used by @#ASManager::makeBufferFromData. Creates a fully initialized @#RTXBuffer object
-        * from partially specified @p buffer. All missing data of @p buffer will be completed by this function.
-        * @param buffer The partially specified @#RTXBuffer holding that part of information which is required for
-        * successfully creating a @p vk::Buffer object.
-        */
-        void createBuffer(RTXBuffer& buffer);
-
-        /**
-         * @brief Build a Bottom Level Acceleration Structure (BLAS) object from given @p vertexBuffer and @p indexBuffer.
-         * @param[in] vertexData The vertex data.
-         */
-        void buildBLAS(const vkcv::VertexData &vertexData);
-		
-		void add(const vkcv::GeometryData &geometryData, const vkcv::AccelerationStructureHandle &blas);
-
-        /**
-         * @brief Build a Top Level Acceleration Structure (TLAS) object from the created
-         * @#ASManager::m_accelerationStructures objects.
-         */
-        void buildTLAS();
-
-        /**
-        * @brief Returns the top-level acceleration structure (TLAS) buffer.
-        * @return A @#TopLevelAccelerationStructure object holding the TLAS.
-        */
-        TopLevelAccelerationStructure getTLAS();
-
-        /**
-         * @brief Returns the bottom-level acceleration structure at @p id.
-         * @param id The ID used for indexing.
-         * @return The specified @#BottomLevelAccelerationStructure object.
-         */
-        BottomLevelAccelerationStructure getBLAS(uint32_t id);
-
-        /**
-         * @brief Returns the dispatcher member variable for access in the @#RTXModule.
-         * @return The dispatcher member variable.
-         */
-        const vk::DispatchLoaderDynamic& getDispatcher();
-    };
-}
\ No newline at end of file
diff --git a/projects/rtx_ambient_occlusion/src/RTX/RTX.cpp b/projects/rtx_ambient_occlusion/src/RTX/RTX.cpp
deleted file mode 100644
index 4fd33be9..00000000
--- a/projects/rtx_ambient_occlusion/src/RTX/RTX.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "RTX.hpp"
-
-namespace vkcv::rtx {
-	
-	RTXModule::RTXModule(Core* core,
-						 ASManager* asManager,
-						 std::vector<vkcv::DescriptorSetHandle>& descriptorSetHandles){
-		m_core = core;
-		m_asManager = asManager;
-		// build acceleration structures BLAS then TLAS --> see ASManager
-		m_asManager->buildTLAS();
-		RTXDescriptors(descriptorSetHandles);
-	}
-	
-	
-	RTXModule::~RTXModule()
-	{}
-	
-	
-	void RTXModule::RTXDescriptors(std::vector<vkcv::DescriptorSetHandle>& descriptorSetHandles)
-	{
-		//TLAS-Descriptor-Write
-		{
-			vkcv::DescriptorWrites writes;
-			writes.writeAcceleration(1, { m_asManager->getTLAS().vulkanHandle });
-			m_core->writeDescriptorSet(descriptorSetHandles[0], writes);
-		}
-	}
-
-}
\ No newline at end of file
diff --git a/projects/rtx_ambient_occlusion/src/RTX/RTX.hpp b/projects/rtx_ambient_occlusion/src/RTX/RTX.hpp
deleted file mode 100644
index 9a71bc17..00000000
--- a/projects/rtx_ambient_occlusion/src/RTX/RTX.hpp
+++ /dev/null
@@ -1,100 +0,0 @@
-#pragma once
-
-#include <vector>
-#include "vulkan/vulkan.hpp"
-#include <vkcv/Core.hpp>
-#include "ASManager.hpp"
-
-namespace vkcv::rtx {
-
-    //struct that holds all shader binding table regions
-    struct ShaderBindingTableRegions {
-        vk::StridedDeviceAddressRegionKHR rgenRegion;
-        vk::StridedDeviceAddressRegionKHR rmissRegion;
-        vk::StridedDeviceAddressRegionKHR rchitRegion;
-        vk::StridedDeviceAddressRegionKHR rcallRegion;
-    };
-
-    class RTXModule {
-    private:
-
-        Core* m_core;
-        ASManager* m_asManager;
-        vk::Pipeline m_pipeline;
-        vk::PipelineLayout m_pipelineLayout;
-        RTXBuffer m_shaderBindingTableBuffer;
-        vk::DeviceSize m_shaderGroupBaseAlignment;
-
-    public:
-
-        /**
-         * @brief Initializes the @#RTXModule with scene data.
-         * @param core The reference to the @#Core.
-         * @param asManager The reference to the @#ASManager.
-         * @param vertexData The vertex data of the scene.
-         * @param descriptorSetHandles The descriptor set handles for RTX.
-         */
-        RTXModule(Core* core,
-				  ASManager* asManager,
-				  std::vector<vkcv::DescriptorSetHandle>& descriptorSetHandles);
-
-        /**
-         * @brief Default #RTXModule destructor.
-         */
-        ~RTXModule();
-
-        /**
-         * @brief Returns the RTX pipeline.
-         * @return The RTX pipeline.
-         */
-        vk::Pipeline getPipeline();
-
-        /**
-         * @brief Returns the shader binding table buffer.
-         * @return The shader binding table buffer.
-         */
-        vk::Buffer getShaderBindingTableBuffer();
-
-        /**
-         * @brief Returns the shader group base alignment for partitioning the shader binding table buffer.
-         * @return The shader group base alignment.
-         */
-        vk::DeviceSize getShaderGroupBaseAlignment();
-
-        /**
-         * @brief Returns the RTX pipeline layout.
-         * @return The RTX pipeline layout.
-         */
-        vk::PipelineLayout getPipelineLayout();
-
-        /**
-         * @brief Sets the shader group base alignment and creates the shader binding table by allocating a shader
-         * binding table buffer. The allocation depends on @p shaderCount and the shader group base alignment.
-         * @param shaderCount The amount of shaders to be used for RTX.
-         */
-        void createShaderBindingTable(uint32_t shaderCount);
-
-        /**
-         * @brief Divides the shader binding table into regions for each shader type
-         * (ray generation, ray miss, ray closest hit, callable) and returns them as a struct.
-         * @return The struct holding all four regions of type vk::StridedDeviceAddressRegionKHR.
-         */
-        ShaderBindingTableRegions createRegions();
-
-        /**
-         * @brief Creates Descriptor-Writes for RTX
-         * @param descriptorSetHandles The descriptorSetHandles for RTX.
-         */
-        void RTXDescriptors(std::vector<vkcv::DescriptorSetHandle>& descriptorSetHandles);
-
-        /**
-         * @brief Creates the RTX pipeline and the RTX pipeline layout. Currently, only RayGen, RayClosestHit and
-         * RayMiss are supported.
-         * @param pushConstantSize The size of the push constant used in the RTX shaders.
-         * @param descriptorSetLayouts The descriptor set layout handles.
-         * @param rtxShader The RTX shader program.
-         */
-        void createRTXPipelineAndLayout(uint32_t pushConstantSize, std::vector<DescriptorSetLayoutHandle> descriptorSetLayouts, ShaderProgram &rtxShader);
-    };
-
-}
diff --git a/screenshots/rtx_ambient_occlusion.png b/screenshots/rt_ambient_occlusion.png
similarity index 100%
rename from screenshots/rtx_ambient_occlusion.png
rename to screenshots/rt_ambient_occlusion.png
diff --git a/src/vkcv/AccelerationStructureManager.cpp b/src/vkcv/AccelerationStructureManager.cpp
index b0b213d4..919a0677 100644
--- a/src/vkcv/AccelerationStructureManager.cpp
+++ b/src/vkcv/AccelerationStructureManager.cpp
@@ -71,6 +71,20 @@ namespace vkcv {
 		return getBufferManager().getBuffer(accelerationStructure.m_storageBuffer);
 	}
 	
+	vk::DeviceAddress AccelerationStructureManager::getAccelerationStructureDeviceAddress(
+			const vkcv::AccelerationStructureHandle &handle) const {
+		auto &accelerationStructure = (*this) [handle];
+		
+		const vk::AccelerationStructureDeviceAddressInfoKHR addressInfo (
+				accelerationStructure.m_accelerationStructure
+		);
+		
+		return getCore().getContext().getDevice().getAccelerationStructureAddressKHR(
+				addressInfo,
+				getCore().getContext().getDispatchLoaderDynamic()
+		);
+	}
+	
 	static vk::Format getVertexFormat(GeometryVertexType vertexType) {
 		switch (vertexType) {
 			case GeometryVertexType::POSITION_FLOAT3:
@@ -97,6 +111,109 @@ namespace vkcv {
 		}
 	}
 	
+	static AccelerationStructureEntry buildAccelerationStructure(
+			Core& core,
+			BufferManager& bufferManager,
+			std::vector<vk::AccelerationStructureBuildGeometryInfoKHR> &geometryInfos,
+			const std::vector<std::vector<vk::AccelerationStructureBuildRangeInfoKHR>> &rangeInfos,
+			size_t accelerationStructureSize,
+			size_t scratchBufferSize,
+			vk::AccelerationStructureTypeKHR accelerationStructureType) {
+		const auto &dynamicDispatch = core.getContext().getDispatchLoaderDynamic();
+		const vk::PhysicalDevice &physicalDevice = core.getContext().getPhysicalDevice();
+		
+		vk::PhysicalDeviceAccelerationStructurePropertiesKHR accelerationStructureProperties;
+		
+		vk::PhysicalDeviceProperties2 physicalProperties2;
+		physicalProperties2.pNext = &accelerationStructureProperties;
+		physicalDevice.getProperties2(&physicalProperties2);
+		
+		const auto minScratchAlignment = (
+				accelerationStructureProperties.minAccelerationStructureScratchOffsetAlignment
+		);
+		
+		const BufferHandle &asStorageBuffer = bufferManager.createBuffer(
+				typeGuard<uint8_t>(),
+				BufferType::ACCELERATION_STRUCTURE_STORAGE,
+				BufferMemoryType::DEVICE_LOCAL,
+				accelerationStructureSize,
+				false
+		);
+		
+		const BufferHandle &asScratchBuffer = bufferManager.createBuffer(
+				vkcv::typeGuard<uint8_t>(),
+				BufferType::STORAGE,
+				BufferMemoryType::DEVICE_LOCAL,
+				scratchBufferSize,
+				false,
+				minScratchAlignment
+		);
+		
+		if ((!asStorageBuffer) || (!asScratchBuffer)) {
+			return {};
+		}
+		
+		const vk::AccelerationStructureCreateInfoKHR asCreateInfo (
+				vk::AccelerationStructureCreateFlagsKHR(),
+				bufferManager.getBuffer(asStorageBuffer),
+				0,
+				accelerationStructureSize,
+				accelerationStructureType
+		);
+		
+		vk::AccelerationStructureKHR accelerationStructure;
+		const vk::Result result = core.getContext().getDevice().createAccelerationStructureKHR(
+				&asCreateInfo,
+				nullptr,
+				&accelerationStructure,
+				dynamicDispatch
+		);
+		
+		if (result != vk::Result::eSuccess) {
+			return {};
+		}
+		
+		const vk::DeviceAddress scratchBufferAddress = bufferManager.getBufferDeviceAddress(
+				asScratchBuffer
+		);
+		
+		for (auto& geometryInfo : geometryInfos) {
+			geometryInfo.setDstAccelerationStructure(accelerationStructure);
+			geometryInfo.setScratchData(scratchBufferAddress);
+		}
+		
+		std::vector<const vk::AccelerationStructureBuildRangeInfoKHR*> pRangeInfos;
+		pRangeInfos.resize(rangeInfos.size());
+		
+		for (size_t i = 0; i < rangeInfos.size(); i++) {
+			pRangeInfos[i] = rangeInfos[i].data();
+		}
+		
+		auto cmdStream = core.createCommandStream(vkcv::QueueType::Compute);
+		
+		core.recordCommandsToStream(
+				cmdStream,
+				[&geometryInfos, &pRangeInfos, &dynamicDispatch](
+						const vk::CommandBuffer &cmdBuffer) {
+					cmdBuffer.buildAccelerationStructuresKHR(
+							static_cast<uint32_t>(geometryInfos.size()),
+							geometryInfos.data(),
+							pRangeInfos.data(),
+							dynamicDispatch
+					);
+				},
+				nullptr
+		);
+		
+		core.submitCommandStream(cmdStream, false);
+		core.getContext().getDevice().waitIdle(); // TODO: Fix that mess!
+		
+		return {
+			accelerationStructure,
+			asStorageBuffer
+		};
+	}
+	
 	AccelerationStructureHandle AccelerationStructureManager::createAccelerationStructure(
 			const std::vector<GeometryData> &geometryData) {
 		std::vector<vk::AccelerationStructureGeometryKHR> geometries;
@@ -189,96 +306,158 @@ namespace vkcv {
 			scratchBufferSize = std::max(scratchBufferSize, asBuildSizesInfo.buildScratchSize);
 		}
 		
-		const BufferHandle &asStorageBuffer = bufferManager.createBuffer(
-				typeGuard<uint8_t>(),
-				BufferType::ACCELERATION_STRUCTURE_STORAGE,
-				BufferMemoryType::DEVICE_LOCAL,
+		const auto entry = buildAccelerationStructure(
+				getCore(),
+				bufferManager,
+				geometryInfos,
+				rangeInfos,
 				accelerationStructureSize,
-				false
+				scratchBufferSize,
+				vk::AccelerationStructureTypeKHR::eBottomLevel
 		);
 		
-		vk::PhysicalDeviceAccelerationStructurePropertiesKHR accelerationStructureProperties;
+		if ((!entry.m_accelerationStructure) || (!entry.m_storageBuffer)) {
+			return {};
+		}
 		
-		vk::PhysicalDeviceProperties2 physicalProperties2;
-		physicalProperties2.pNext = &accelerationStructureProperties;
+		return add(entry);
+	}
+	
+	AccelerationStructureHandle AccelerationStructureManager::createAccelerationStructure(
+			const std::vector<AccelerationStructureHandle> &accelerationStructures) {
+		std::vector<vk::AccelerationStructureInstanceKHR> asInstances;
+		asInstances.reserve(accelerationStructures.size());
 		
-		getCore().getContext().getPhysicalDevice().getProperties2(&physicalProperties2);
+		auto& bufferManager = getBufferManager();
 		
-		const auto minScratchAlignment = (
-				accelerationStructureProperties.minAccelerationStructureScratchOffsetAlignment
-		);
+		const auto &dynamicDispatch = getCore().getContext().getDispatchLoaderDynamic();
 		
-		const BufferHandle &asScratchBuffer = bufferManager.createBuffer(
-				vkcv::TypeGuard(minScratchAlignment),
-				BufferType::STORAGE,
+		for (const auto& accelerationStructure : accelerationStructures) {
+			const vk::DeviceAddress asDeviceAddress = getAccelerationStructureDeviceAddress(
+					accelerationStructure
+			);
+			
+			const std::array<std::array<float, 4>, 3> transformMatrixValues = {
+					std::array<float, 4>{1.f, 0.f, 0.f, 0.f},
+					std::array<float, 4>{0.f, 1.f, 0.f, 0.f},
+					std::array<float, 4>{0.f, 0.f, 1.f, 0.f},
+			};
+			
+			const vk::TransformMatrixKHR transformMatrix (
+					transformMatrixValues
+			);
+			
+			const vk::GeometryInstanceFlagsKHR instanceFlags (
+					vk::GeometryInstanceFlagBitsKHR::eTriangleFacingCullDisable
+			);
+			
+			const vk::AccelerationStructureInstanceKHR asInstance (
+					transformMatrix,
+					0,
+					0xFF,
+					0,
+					instanceFlags,
+					asDeviceAddress
+			);
+			
+			asInstances.push_back(asInstance);
+		}
+		
+		const auto asInstanceTypeGuard = typeGuard<vk::AccelerationStructureInstanceKHR>();
+		
+		auto asInputBuffer = bufferManager.createBuffer(
+				asInstanceTypeGuard,
+				BufferType::ACCELERATION_STRUCTURE_INPUT,
 				BufferMemoryType::DEVICE_LOCAL,
-				(scratchBufferSize + minScratchAlignment - 1) / minScratchAlignment,
+				asInstances.size() * asInstanceTypeGuard.typeSize(),
 				false
 		);
 		
-		if ((!asStorageBuffer) || (!asScratchBuffer)) {
-			return {};
-		}
-		
-		const vk::AccelerationStructureCreateInfoKHR asCreateInfo (
-				vk::AccelerationStructureCreateFlagsKHR(),
-				bufferManager.getBuffer(asStorageBuffer),
+		bufferManager.fillBuffer(
+				asInputBuffer,
+				asInstances.data(),
 				0,
-				accelerationStructureSize,
-				vk::AccelerationStructureTypeKHR::eBottomLevel
+				0
 		);
 		
-		vk::AccelerationStructureKHR accelerationStructure;
-		const vk::Result result = getCore().getContext().getDevice().createAccelerationStructureKHR(
-				&asCreateInfo,
-				nullptr,
-				&accelerationStructure,
-				dynamicDispatch
+		const vk::AccelerationStructureBuildRangeInfoKHR asBuildRangeInfo (
+				static_cast<uint32_t>(asInstances.size()),
+				0,
+				0,
+				0
 		);
 		
-		if (result != vk::Result::eSuccess) {
-			return {};
-		}
+		auto cmdStream = getCore().createCommandStream(QueueType::Compute);
 		
-		vk::DeviceAddress scratchBufferAddress = bufferManager.getBufferDeviceAddress(
-				asScratchBuffer
-		);
-		
-		if (scratchBufferAddress % minScratchAlignment != 0) {
-			scratchBufferAddress += (
-					minScratchAlignment - (scratchBufferAddress % minScratchAlignment)
+		getCore().recordCommandsToStream(cmdStream, [](const vk::CommandBuffer &cmdBuffer) {
+			const vk::MemoryBarrier barrier (
+					vk::AccessFlagBits::eTransferWrite,
+					vk::AccessFlagBits::eAccelerationStructureReadKHR
 			);
-		}
+			
+			cmdBuffer.pipelineBarrier(
+					vk::PipelineStageFlagBits::eTransfer,
+					vk::PipelineStageFlagBits::eAccelerationStructureBuildKHR,
+					{},
+					barrier,
+					nullptr,
+					nullptr
+			);
+		}, nullptr);
 		
-		for (auto& geometryInfo : geometryInfos) {
-			geometryInfo.setDstAccelerationStructure(accelerationStructure);
-			geometryInfo.setScratchData(scratchBufferAddress);
-		}
+		getCore().submitCommandStream(cmdStream, false);
 		
-		std::vector<vk::AccelerationStructureBuildRangeInfoKHR*> pRangeInfos;
-		pRangeInfos.resize(rangeInfos.size());
+		const vk::DeviceAddress inputBufferAddress = bufferManager.getBufferDeviceAddress(
+				asInputBuffer
+		);
 		
-		for (size_t i = 0; i < rangeInfos.size(); i++) {
-			pRangeInfos[i] = rangeInfos[i].data();
-		}
+		const vk::AccelerationStructureGeometryInstancesDataKHR asInstancesData (
+				false,
+				inputBufferAddress
+		);
 		
-		auto cmdStream = getCore().createCommandStream(vkcv::QueueType::Compute);
+		const vk::AccelerationStructureGeometryKHR asGeometry (
+				vk::GeometryTypeKHR::eInstances,
+				asInstancesData,
+				{}
+		);
 		
-		getCore().recordCommandsToStream(
-				cmdStream,
-				[&geometryInfos, &pRangeInfos, &dynamicDispatch](
-						const vk::CommandBuffer &cmdBuffer) {
-			cmdBuffer.buildAccelerationStructuresKHR(
-					static_cast<uint32_t>(geometryInfos.size()),
-					geometryInfos.data(),
-					pRangeInfos.data(),
-					dynamicDispatch
-			);
-		}, nullptr);
+		std::vector<vk::AccelerationStructureBuildGeometryInfoKHR> asBuildGeometryInfos = {
+				vk::AccelerationStructureBuildGeometryInfoKHR(
+						vk::AccelerationStructureTypeKHR::eTopLevel,
+						vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace,
+						vk::BuildAccelerationStructureModeKHR::eBuild,
+						{},
+						{},
+						1,
+						&(asGeometry)
+				)
+		};
 		
-		getCore().submitCommandStream(cmdStream, false);
+		vk::AccelerationStructureBuildSizesInfoKHR asBuildSizesInfo;
+		getCore().getContext().getDevice().getAccelerationStructureBuildSizesKHR(
+				vk::AccelerationStructureBuildTypeKHR::eDevice,
+				asBuildGeometryInfos.data(),
+				&(asBuildRangeInfo.primitiveCount),
+				&(asBuildSizesInfo),
+				dynamicDispatch
+		);
+		
+		const auto entry = buildAccelerationStructure(
+				getCore(),
+				bufferManager,
+				asBuildGeometryInfos,
+				{ { asBuildRangeInfo } },
+				asBuildSizesInfo.accelerationStructureSize,
+				asBuildSizesInfo.buildScratchSize,
+				vk::AccelerationStructureTypeKHR::eTopLevel
+		);
+		
+		if ((!entry.m_accelerationStructure) || (!entry.m_storageBuffer)) {
+			return {};
+		}
 		
-		return add({ accelerationStructure, asStorageBuffer });
+		return add(entry);
 	}
 	
 }
diff --git a/src/vkcv/AccelerationStructureManager.hpp b/src/vkcv/AccelerationStructureManager.hpp
index 155b5767..d7cc67f1 100644
--- a/src/vkcv/AccelerationStructureManager.hpp
+++ b/src/vkcv/AccelerationStructureManager.hpp
@@ -20,7 +20,7 @@ namespace vkcv {
 	
 	struct AccelerationStructureEntry {
 		vk::AccelerationStructureKHR m_accelerationStructure;
-		vkcv::BufferHandle m_storageBuffer;
+		BufferHandle m_storageBuffer;
 	};
 	
 	/**
@@ -64,9 +64,23 @@ namespace vkcv {
 		
 		[[nodiscard]] vk::Buffer getVulkanBuffer(const AccelerationStructureHandle &handle) const;
 		
+		/**
+		 * @brief Returns the Vulkan device address of an acceleration
+		 * structure represented by a given acceleration structure
+		 * handle id.
+		 *
+		 * @param[in] handle Acceleration structure handle
+		 * @return Vulkan device address
+		 */
+		[[nodiscard]] vk::DeviceAddress getAccelerationStructureDeviceAddress(
+				const AccelerationStructureHandle &handle) const;
+		
 		[[nodiscard]] AccelerationStructureHandle createAccelerationStructure(
 				const std::vector<GeometryData> &geometryData);
 		
+		[[nodiscard]] AccelerationStructureHandle createAccelerationStructure(
+				const std::vector<AccelerationStructureHandle> &accelerationStructures);
+		
 	};
 	
 }
\ No newline at end of file
diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp
index b7732588..a8767ece 100644
--- a/src/vkcv/BufferManager.cpp
+++ b/src/vkcv/BufferManager.cpp
@@ -114,7 +114,7 @@ namespace vkcv {
 
 	BufferHandle BufferManager::createBuffer(const TypeGuard &typeGuard, BufferType type,
 											 BufferMemoryType memoryType, size_t size,
-											 bool readable) {
+											 bool readable, size_t alignment) {
 		vk::BufferCreateFlags createFlags;
 		vk::BufferUsageFlags usageFlags;
 
@@ -173,7 +173,7 @@ namespace vkcv {
 		const vma::Allocator &allocator = getCore().getContext().getAllocator();
 
 		vma::MemoryUsage memoryUsage;
-		bool mappable = false;
+		bool mappable;
 
 		switch (memoryType) {
 		case BufferMemoryType::DEVICE_LOCAL:
@@ -205,7 +205,7 @@ namespace vkcv {
 									| vma::AllocationCreateFlagBits::eHostAccessSequentialWrite;
 		}
 
-		auto bufferAllocation = allocator.createBuffer(
+		const auto bufferAllocation = allocator.createBufferWithAlignment(
 			vk::BufferCreateInfo(createFlags, size, usageFlags),
 			vma::AllocationCreateInfo(
 					allocationCreateFlags,
@@ -215,11 +215,12 @@ namespace vkcv {
 					0,
 					vma::Pool(),
 					nullptr
-			)
+			),
+			static_cast<vk::DeviceSize>(alignment)
 		);
 
-		vk::Buffer buffer = bufferAllocation.first;
-		vma::Allocation allocation = bufferAllocation.second;
+		const vk::Buffer buffer = bufferAllocation.first;
+		const vma::Allocation allocation = bufferAllocation.second;
 		
 		const vk::MemoryPropertyFlags finalMemoryFlags = allocator.getAllocationMemoryProperties(
 				allocation
@@ -406,8 +407,11 @@ namespace vkcv {
 		);
 	}
 
-	void BufferManager::fillBuffer(const BufferHandle &handle, const void* data, size_t size,
-								   size_t offset) {
+	void BufferManager::fillBuffer(const BufferHandle &handle,
+								   const void* data,
+								   size_t size,
+								   size_t offset,
+								   bool forceStaging) {
 		auto &buffer = (*this) [handle];
 
 		if (size == 0) {
@@ -422,7 +426,7 @@ namespace vkcv {
 
 		const size_t max_size = std::min(size, buffer.m_size - offset);
 
-		if (buffer.m_mappable) {
+		if ((buffer.m_mappable) && (!forceStaging)) {
 			void* mapped = allocator.mapMemory(buffer.m_allocation);
 			memcpy(reinterpret_cast<char*>(mapped) + offset, data, max_size);
 			allocator.unmapMemory(buffer.m_allocation);
@@ -445,7 +449,9 @@ namespace vkcv {
 		}
 	}
 
-	void BufferManager::readBuffer(const BufferHandle &handle, void* data, size_t size,
+	void BufferManager::readBuffer(const BufferHandle &handle,
+								   void* data,
+								   size_t size,
 								   size_t offset) {
 		auto &buffer = (*this) [handle];
 
diff --git a/src/vkcv/BufferManager.hpp b/src/vkcv/BufferManager.hpp
index 818c907c..891b59fc 100644
--- a/src/vkcv/BufferManager.hpp
+++ b/src/vkcv/BufferManager.hpp
@@ -79,11 +79,12 @@ namespace vkcv {
 		 * @param[in] size Size of buffer in bytes
 		 * @param[in] supportIndirect Support of indirect usage
 		 * @param[in] readable Support read functionality
+		 * @param[in] alignment (optional) Alignment for buffer memory
 		 * @return New buffer handle
 		 */
 		[[nodiscard]] BufferHandle createBuffer(const TypeGuard &typeGuard, BufferType type,
 												BufferMemoryType memoryType, size_t size,
-												bool readable);
+												bool readable, size_t alignment = 0);
 
 		/**
 		 * @brief Returns the Vulkan buffer handle of a buffer
@@ -156,8 +157,13 @@ namespace vkcv {
 		 * @param[in] data Pointer to data
 		 * @param[in] size Size of data in bytes
 		 * @param[in] offset Offset to fill in data in bytes
+		 * @param[in] forceStaging (Optional) Flag to enforce transfer via staging buffer
 		 */
-		void fillBuffer(const BufferHandle &handle, const void* data, size_t size, size_t offset);
+		void fillBuffer(const BufferHandle &handle,
+						const void* data,
+						size_t size,
+						size_t offset,
+						bool forceStaging = false);
 
 		/**
 		 * @brief Reads from a buffer represented by a given
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index db8cdf53..f2152223 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -1351,11 +1351,18 @@ namespace vkcv {
 		return m_ImageManager->getVulkanDeviceMemory(handle);
 	}
 	
-	AccelerationStructureHandle Core::createAccelerationStructure(const std::vector<GeometryData> &geometryData) {
+	AccelerationStructureHandle Core::createAccelerationStructure(
+			const std::vector<GeometryData> &geometryData) {
 		return m_AccelerationStructureManager->createAccelerationStructure(geometryData);
 	}
 	
-	vk::AccelerationStructureKHR Core::getVulkanAccelerationStructure(const AccelerationStructureHandle &handle) const {
+	AccelerationStructureHandle Core::createAccelerationStructure(
+			const std::vector<AccelerationStructureHandle> &handles) {
+		return m_AccelerationStructureManager->createAccelerationStructure(handles);
+	}
+	
+	vk::AccelerationStructureKHR Core::getVulkanAccelerationStructure(
+			const AccelerationStructureHandle &handle) const {
 		return m_AccelerationStructureManager->getVulkanAccelerationStructure(handle);
 	}
 	
@@ -1363,4 +1370,9 @@ namespace vkcv {
 		return m_AccelerationStructureManager->getVulkanBuffer(handle);
 	}
 	
+	vk::DeviceAddress Core::getAccelerationStructureDeviceAddress(
+			const vkcv::AccelerationStructureHandle &handle) const {
+		return m_AccelerationStructureManager->getAccelerationStructureDeviceAddress(handle);
+	}
+	
 } // namespace vkcv
diff --git a/src/vkcv/DescriptorSetManager.cpp b/src/vkcv/DescriptorSetManager.cpp
index e6ac1c22..b33221b1 100644
--- a/src/vkcv/DescriptorSetManager.cpp
+++ b/src/vkcv/DescriptorSetManager.cpp
@@ -22,13 +22,14 @@ namespace vkcv {
 			vk::DescriptorPoolSize(vk::DescriptorType::eUniformBuffer, 1000),
 			vk::DescriptorPoolSize(vk::DescriptorType::eStorageBuffer, 1000),
 			vk::DescriptorPoolSize(vk::DescriptorType::eUniformBufferDynamic, 1000),
-			vk::DescriptorPoolSize(vk::DescriptorType::eStorageBufferDynamic, 1000),    // for RTX
-			vk::DescriptorPoolSize(vk::DescriptorType::eAccelerationStructureKHR, 1000) // for RTX
+			vk::DescriptorPoolSize(vk::DescriptorType::eStorageBufferDynamic, 1000),
+			vk::DescriptorPoolSize(vk::DescriptorType::eAccelerationStructureKHR, 1000)
 		};
 
 		m_PoolInfo = vk::DescriptorPoolCreateInfo(
 			vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, 1000,
-			static_cast<uint32_t>(m_PoolSizes.size()), m_PoolSizes.data());
+			static_cast<uint32_t>(m_PoolSizes.size()), m_PoolSizes.data()
+		);
 
 		return allocateDescriptorPool();
 	}
diff --git a/src/vkcv/RayTracingPipelineManager.cpp b/src/vkcv/RayTracingPipelineManager.cpp
index 70f05b98..147c216d 100644
--- a/src/vkcv/RayTracingPipelineManager.cpp
+++ b/src/vkcv/RayTracingPipelineManager.cpp
@@ -403,26 +403,21 @@ namespace vkcv {
 		);
 		
 		const BufferHandle &shaderBindingTable = bufferManager.createBuffer(
-				TypeGuard(baseAlignment),
+				typeGuard<uint8_t>(),
 				BufferType::SHADER_BINDING,
 				BufferMemoryType::DEVICE_LOCAL,
-				(shaderBindingTableSize + baseAlignment - 1) / baseAlignment,
-				false
+				shaderBindingTableSize,
+				false,
+				baseAlignment
 		);
 		
-		vk::DeviceAddress bufferBaseAddress = bufferManager.getBufferDeviceAddress(
+		const vk::DeviceAddress bufferBaseAddress = bufferManager.getBufferDeviceAddress(
 				shaderBindingTable
 		);
 		
-		size_t bufferBaseOffset = 0;
-		if (bufferBaseAddress % baseAlignment != 0) {
-			bufferBaseOffset = (baseAlignment - (bufferBaseAddress % baseAlignment));
-			bufferBaseAddress += bufferBaseOffset;
-		}
-		
 		void* mappedBindingTable = bufferManager.mapBuffer(
 				shaderBindingTable,
-				bufferBaseOffset,
+				0,
 				shaderBindingTableSize
 		);
 		
-- 
GitLab