From 36a7d8634ac52becbf119f0b1d5974a03eede163 Mon Sep 17 00:00:00 2001 From: marc0246 <40955683+marc0246@users.noreply.github.com> Date: Tue, 28 Jan 2025 14:31:10 +0100 Subject: [PATCH 1/5] Fix shader files' inconsistent file extensions --- examples/ray-tracing-auto/{raytrace.rchit => rchit.glsl} | 0 examples/ray-tracing-auto/{raytrace.rgen => rgen.glsl} | 0 examples/ray-tracing-auto/{raytrace.miss => rmiss.glsl} | 0 examples/ray-tracing-auto/scene.rs | 6 +++--- examples/ray-tracing/{raytrace.rchit => rchit.glsl} | 0 examples/ray-tracing/{raytrace.rgen => rgen.glsl} | 0 examples/ray-tracing/{raytrace.miss => rmiss.glsl} | 0 examples/ray-tracing/scene.rs | 6 +++--- 8 files changed, 6 insertions(+), 6 deletions(-) rename examples/ray-tracing-auto/{raytrace.rchit => rchit.glsl} (100%) rename examples/ray-tracing-auto/{raytrace.rgen => rgen.glsl} (100%) rename examples/ray-tracing-auto/{raytrace.miss => rmiss.glsl} (100%) rename examples/ray-tracing/{raytrace.rchit => rchit.glsl} (100%) rename examples/ray-tracing/{raytrace.rgen => rgen.glsl} (100%) rename examples/ray-tracing/{raytrace.miss => rmiss.glsl} (100%) diff --git a/examples/ray-tracing-auto/raytrace.rchit b/examples/ray-tracing-auto/rchit.glsl similarity index 100% rename from examples/ray-tracing-auto/raytrace.rchit rename to examples/ray-tracing-auto/rchit.glsl diff --git a/examples/ray-tracing-auto/raytrace.rgen b/examples/ray-tracing-auto/rgen.glsl similarity index 100% rename from examples/ray-tracing-auto/raytrace.rgen rename to examples/ray-tracing-auto/rgen.glsl diff --git a/examples/ray-tracing-auto/raytrace.miss b/examples/ray-tracing-auto/rmiss.glsl similarity index 100% rename from examples/ray-tracing-auto/raytrace.miss rename to examples/ray-tracing-auto/rmiss.glsl diff --git a/examples/ray-tracing-auto/scene.rs b/examples/ray-tracing-auto/scene.rs index 1a017a4954..dfc8b1eec3 100644 --- a/examples/ray-tracing-auto/scene.rs +++ b/examples/ray-tracing-auto/scene.rs @@ -36,7 +36,7 @@ use vulkano::{ mod raygen { vulkano_shaders::shader! { ty: "raygen", - path: "raytrace.rgen", + path: "rgen.glsl", vulkan_version: "1.2" } } @@ -44,7 +44,7 @@ mod raygen { mod closest_hit { vulkano_shaders::shader! { ty: "closesthit", - path: "raytrace.rchit", + path: "rchit.glsl", vulkan_version: "1.2" } } @@ -52,7 +52,7 @@ mod closest_hit { mod miss { vulkano_shaders::shader! { ty: "miss", - path: "raytrace.miss", + path: "rmiss.glsl", vulkan_version: "1.2" } } diff --git a/examples/ray-tracing/raytrace.rchit b/examples/ray-tracing/rchit.glsl similarity index 100% rename from examples/ray-tracing/raytrace.rchit rename to examples/ray-tracing/rchit.glsl diff --git a/examples/ray-tracing/raytrace.rgen b/examples/ray-tracing/rgen.glsl similarity index 100% rename from examples/ray-tracing/raytrace.rgen rename to examples/ray-tracing/rgen.glsl diff --git a/examples/ray-tracing/raytrace.miss b/examples/ray-tracing/rmiss.glsl similarity index 100% rename from examples/ray-tracing/raytrace.miss rename to examples/ray-tracing/rmiss.glsl diff --git a/examples/ray-tracing/scene.rs b/examples/ray-tracing/scene.rs index 9271eb8145..4e6b91a046 100644 --- a/examples/ray-tracing/scene.rs +++ b/examples/ray-tracing/scene.rs @@ -40,7 +40,7 @@ use vulkano_taskgraph::{ mod raygen { vulkano_shaders::shader! { ty: "raygen", - path: "raytrace.rgen", + path: "rgen.glsl", vulkan_version: "1.2" } } @@ -48,7 +48,7 @@ mod raygen { mod closest_hit { vulkano_shaders::shader! { ty: "closesthit", - path: "raytrace.rchit", + path: "rchit.glsl", vulkan_version: "1.2" } } @@ -56,7 +56,7 @@ mod closest_hit { mod miss { vulkano_shaders::shader! { ty: "miss", - path: "raytrace.miss", + path: "rmiss.glsl", vulkan_version: "1.2" } } From 323f195c78db9825d45482a262d0befd23ea1f3f Mon Sep 17 00:00:00 2001 From: marc0246 <40955683+marc0246@users.noreply.github.com> Date: Tue, 28 Jan 2025 14:48:07 +0100 Subject: [PATCH 2/5] Fix shader files' inconsistent formatting --- examples/ray-tracing-auto/rchit.glsl | 6 +-- examples/ray-tracing-auto/rgen.glsl | 70 ++++++++++++++-------------- examples/ray-tracing-auto/rmiss.glsl | 6 ++- examples/ray-tracing-auto/scene.rs | 6 +-- examples/ray-tracing/rchit.glsl | 6 +-- examples/ray-tracing/rgen.glsl | 70 ++++++++++++++-------------- examples/ray-tracing/rmiss.glsl | 6 ++- examples/ray-tracing/scene.rs | 6 +-- 8 files changed, 88 insertions(+), 88 deletions(-) diff --git a/examples/ray-tracing-auto/rchit.glsl b/examples/ray-tracing-auto/rchit.glsl index 52c407b96a..9a00bcae75 100644 --- a/examples/ray-tracing-auto/rchit.glsl +++ b/examples/ray-tracing-auto/rchit.glsl @@ -1,10 +1,10 @@ #version 460 #extension GL_EXT_ray_tracing : require -layout(location = 0) rayPayloadInEXT vec3 hitValue; +layout(location = 0) rayPayloadInEXT vec3 hit_value; hitAttributeEXT vec2 attribs; void main() { - vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y); - hitValue = barycentrics; + vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y); + hit_value = barycentrics; } diff --git a/examples/ray-tracing-auto/rgen.glsl b/examples/ray-tracing-auto/rgen.glsl index 8a9416e201..0f41f248a3 100644 --- a/examples/ray-tracing-auto/rgen.glsl +++ b/examples/ray-tracing-auto/rgen.glsl @@ -1,43 +1,41 @@ #version 460 #extension GL_EXT_ray_tracing : require -struct Camera { - mat4 viewProj; // Camera view * projection - mat4 viewInverse; // Camera inverse view matrix - mat4 projInverse; // Camera inverse projection matrix -}; - -layout(location = 0) rayPayloadEXT vec3 hitValue; - -layout(set = 0, binding = 0) uniform accelerationStructureEXT topLevelAS; -layout(set = 0, binding = 1) uniform _Camera { Camera camera; }; +layout(location = 0) rayPayloadEXT vec3 hit_value; + +layout(set = 0, binding = 0) uniform accelerationStructureEXT top_level_as; +layout(set = 0, binding = 1) uniform Camera { + mat4 view_proj; // Camera view * projection + mat4 view_inverse; // Camera inverse view matrix + mat4 proj_inverse; // Camera inverse projection matrix +} camera; layout(set = 1, binding = 0, rgba32f) uniform image2D image; void main() { - const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(0.5); - const vec2 inUV = pixelCenter / vec2(gl_LaunchSizeEXT.xy); - vec2 d = inUV * 2.0 - 1.0; - - vec4 origin = camera.viewInverse * vec4(0, 0, 0, 1); - vec4 target = camera.projInverse * vec4(d.x, d.y, 1, 1); - vec4 direction = camera.viewInverse * vec4(normalize(target.xyz), 0); - - uint rayFlags = gl_RayFlagsOpaqueEXT; - float tMin = 0.001; - float tMax = 10000.0; - - traceRayEXT(topLevelAS, // acceleration structure - rayFlags, // rayFlags - 0xFF, // cullMask - 0, // sbtRecordOffset - 0, // sbtRecordStride - 0, // missIndex - origin.xyz, // ray origin - tMin, // ray min range - direction.xyz, // ray direction - tMax, // ray max range - 0 // payload (location = 0) - ); - - imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hitValue, 1.0)); + const vec2 pixel_center = vec2(gl_LaunchIDEXT.xy) + vec2(0.5); + const vec2 in_uv = pixel_center / vec2(gl_LaunchSizeEXT.xy); + vec2 d = in_uv * 2.0 - 1.0; + + vec4 origin = camera.view_inverse * vec4(0, 0, 0, 1); + vec4 target = camera.proj_inverse * vec4(d.x, d.y, 1, 1); + vec4 direction = camera.view_inverse * vec4(normalize(target.xyz), 0); + + uint ray_flags = gl_RayFlagsOpaqueEXT; + float t_min = 0.001; + float t_max = 10000.0; + + traceRayEXT( + top_level_as, // acceleration structure + ray_flags, // rayFlags + 0xFF, // cullMask + 0, // sbtRecordOffset + 0, // sbtRecordStride + 0, // missIndex + origin.xyz, // ray origin + t_min, // ray min range + direction.xyz, // ray direction + t_max, // ray max range + 0); // payload (location = 0) + + imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hit_value, 1.0)); } diff --git a/examples/ray-tracing-auto/rmiss.glsl b/examples/ray-tracing-auto/rmiss.glsl index 1c584d5420..9d82130941 100644 --- a/examples/ray-tracing-auto/rmiss.glsl +++ b/examples/ray-tracing-auto/rmiss.glsl @@ -1,6 +1,8 @@ #version 460 #extension GL_EXT_ray_tracing : require -layout(location = 0) rayPayloadInEXT vec3 hitValue; +layout(location = 0) rayPayloadInEXT vec3 hit_value; -void main() { hitValue = vec3(0.0, 0.0, 0.2); } +void main() { + hit_value = vec3(0.0, 0.0, 0.2); +} diff --git a/examples/ray-tracing-auto/scene.rs b/examples/ray-tracing-auto/scene.rs index dfc8b1eec3..f9be0d0c60 100644 --- a/examples/ray-tracing-auto/scene.rs +++ b/examples/ray-tracing-auto/scene.rs @@ -209,9 +209,9 @@ impl Scene { ..Default::default() }, raygen::Camera { - viewInverse: view.inverse().to_cols_array_2d(), - projInverse: proj.inverse().to_cols_array_2d(), - viewProj: (proj * view).to_cols_array_2d(), + view_proj: (proj * view).to_cols_array_2d(), + view_inverse: view.inverse().to_cols_array_2d(), + proj_inverse: proj.inverse().to_cols_array_2d(), }, ) .unwrap(); diff --git a/examples/ray-tracing/rchit.glsl b/examples/ray-tracing/rchit.glsl index 52c407b96a..9a00bcae75 100644 --- a/examples/ray-tracing/rchit.glsl +++ b/examples/ray-tracing/rchit.glsl @@ -1,10 +1,10 @@ #version 460 #extension GL_EXT_ray_tracing : require -layout(location = 0) rayPayloadInEXT vec3 hitValue; +layout(location = 0) rayPayloadInEXT vec3 hit_value; hitAttributeEXT vec2 attribs; void main() { - vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y); - hitValue = barycentrics; + vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y); + hit_value = barycentrics; } diff --git a/examples/ray-tracing/rgen.glsl b/examples/ray-tracing/rgen.glsl index 8a9416e201..0f41f248a3 100644 --- a/examples/ray-tracing/rgen.glsl +++ b/examples/ray-tracing/rgen.glsl @@ -1,43 +1,41 @@ #version 460 #extension GL_EXT_ray_tracing : require -struct Camera { - mat4 viewProj; // Camera view * projection - mat4 viewInverse; // Camera inverse view matrix - mat4 projInverse; // Camera inverse projection matrix -}; - -layout(location = 0) rayPayloadEXT vec3 hitValue; - -layout(set = 0, binding = 0) uniform accelerationStructureEXT topLevelAS; -layout(set = 0, binding = 1) uniform _Camera { Camera camera; }; +layout(location = 0) rayPayloadEXT vec3 hit_value; + +layout(set = 0, binding = 0) uniform accelerationStructureEXT top_level_as; +layout(set = 0, binding = 1) uniform Camera { + mat4 view_proj; // Camera view * projection + mat4 view_inverse; // Camera inverse view matrix + mat4 proj_inverse; // Camera inverse projection matrix +} camera; layout(set = 1, binding = 0, rgba32f) uniform image2D image; void main() { - const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(0.5); - const vec2 inUV = pixelCenter / vec2(gl_LaunchSizeEXT.xy); - vec2 d = inUV * 2.0 - 1.0; - - vec4 origin = camera.viewInverse * vec4(0, 0, 0, 1); - vec4 target = camera.projInverse * vec4(d.x, d.y, 1, 1); - vec4 direction = camera.viewInverse * vec4(normalize(target.xyz), 0); - - uint rayFlags = gl_RayFlagsOpaqueEXT; - float tMin = 0.001; - float tMax = 10000.0; - - traceRayEXT(topLevelAS, // acceleration structure - rayFlags, // rayFlags - 0xFF, // cullMask - 0, // sbtRecordOffset - 0, // sbtRecordStride - 0, // missIndex - origin.xyz, // ray origin - tMin, // ray min range - direction.xyz, // ray direction - tMax, // ray max range - 0 // payload (location = 0) - ); - - imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hitValue, 1.0)); + const vec2 pixel_center = vec2(gl_LaunchIDEXT.xy) + vec2(0.5); + const vec2 in_uv = pixel_center / vec2(gl_LaunchSizeEXT.xy); + vec2 d = in_uv * 2.0 - 1.0; + + vec4 origin = camera.view_inverse * vec4(0, 0, 0, 1); + vec4 target = camera.proj_inverse * vec4(d.x, d.y, 1, 1); + vec4 direction = camera.view_inverse * vec4(normalize(target.xyz), 0); + + uint ray_flags = gl_RayFlagsOpaqueEXT; + float t_min = 0.001; + float t_max = 10000.0; + + traceRayEXT( + top_level_as, // acceleration structure + ray_flags, // rayFlags + 0xFF, // cullMask + 0, // sbtRecordOffset + 0, // sbtRecordStride + 0, // missIndex + origin.xyz, // ray origin + t_min, // ray min range + direction.xyz, // ray direction + t_max, // ray max range + 0); // payload (location = 0) + + imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hit_value, 1.0)); } diff --git a/examples/ray-tracing/rmiss.glsl b/examples/ray-tracing/rmiss.glsl index 1c584d5420..9d82130941 100644 --- a/examples/ray-tracing/rmiss.glsl +++ b/examples/ray-tracing/rmiss.glsl @@ -1,6 +1,8 @@ #version 460 #extension GL_EXT_ray_tracing : require -layout(location = 0) rayPayloadInEXT vec3 hitValue; +layout(location = 0) rayPayloadInEXT vec3 hit_value; -void main() { hitValue = vec3(0.0, 0.0, 0.2); } +void main() { + hit_value = vec3(0.0, 0.0, 0.2); +} diff --git a/examples/ray-tracing/scene.rs b/examples/ray-tracing/scene.rs index 4e6b91a046..2e70dfc477 100644 --- a/examples/ray-tracing/scene.rs +++ b/examples/ray-tracing/scene.rs @@ -206,9 +206,9 @@ impl SceneTask { ..Default::default() }, raygen::Camera { - viewInverse: view.inverse().to_cols_array_2d(), - projInverse: proj.inverse().to_cols_array_2d(), - viewProj: (proj * view).to_cols_array_2d(), + view_proj: (proj * view).to_cols_array_2d(), + view_inverse: view.inverse().to_cols_array_2d(), + proj_inverse: proj.inverse().to_cols_array_2d(), }, ) .unwrap(); From 6c73094587a8a0278dbd853829a130a1086e06fa Mon Sep 17 00:00:00 2001 From: marc0246 <40955683+marc0246@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:11:41 +0100 Subject: [PATCH 3/5] Fix examples' code inconsistencies --- examples/ray-tracing-auto/main.rs | 157 ++++++++++++++--------------- examples/ray-tracing-auto/scene.rs | 117 ++++++++++----------- examples/ray-tracing/main.rs | 93 +++++++++-------- examples/ray-tracing/scene.rs | 153 +++++++++++++++------------- 4 files changed, 260 insertions(+), 260 deletions(-) diff --git a/examples/ray-tracing-auto/main.rs b/examples/ray-tracing-auto/main.rs index d39d912a35..e54ffad5c9 100644 --- a/examples/ray-tracing-auto/main.rs +++ b/examples/ray-tracing-auto/main.rs @@ -24,7 +24,7 @@ use vulkano::{ acquire_next_image, Surface, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, }, sync::{self, GpuFuture}, - Version, VulkanLibrary, + Validated, Version, VulkanError, VulkanLibrary, }; use winit::{ application::ApplicationHandler, @@ -46,8 +46,8 @@ struct App { instance: Arc, device: Arc, queue: Arc, - rcx: Option, command_buffer_allocator: Arc, + rcx: Option, } pub struct RenderContext { @@ -84,11 +84,21 @@ impl App { khr_acceleration_structure: true, ..DeviceExtensions::empty() }; + let device_features = DeviceFeatures { + acceleration_structure: true, + ray_tracing_pipeline: true, + buffer_device_address: true, + synchronization2: true, + ..Default::default() + }; let (physical_device, queue_family_index) = instance .enumerate_physical_devices() .unwrap() .filter(|p| p.api_version() >= Version::V1_3) - .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter(|p| { + p.supported_extensions().contains(&device_extensions) + && p.supported_features().contains(&device_features) + }) .filter_map(|p| { p.queue_family_properties() .iter() @@ -110,6 +120,12 @@ impl App { }) .unwrap(); + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + let (device, mut queues) = Device::new( physical_device, DeviceCreateInfo { @@ -118,13 +134,7 @@ impl App { queue_family_index, ..Default::default() }], - enabled_features: DeviceFeatures { - acceleration_structure: true, - ray_tracing_pipeline: true, - buffer_device_address: true, - synchronization2: true, - ..Default::default() - }, + enabled_features: device_features, ..Default::default() }, ) @@ -141,8 +151,8 @@ impl App { instance, device, queue, - rcx: None, command_buffer_allocator, + rcx: None, } } } @@ -155,32 +165,7 @@ impl ApplicationHandler for App { .unwrap(), ); let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - - let physical_device = self.device.physical_device(); - let supported_surface_formats = physical_device - .surface_formats(&surface, Default::default()) - .unwrap(); - - // For each supported format, check if it is supported for storage images - let supported_storage_formats = supported_surface_formats - .into_iter() - .filter(|(format, _)| { - physical_device - .image_format_properties(ImageFormatInfo { - format: *format, - usage: ImageUsage::STORAGE, - ..Default::default() - }) - .unwrap() - .is_some() - }) - .collect::>(); - - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); + let window_size = window.inner_size(); let (swapchain, images) = { let surface_capabilities = self @@ -188,22 +173,35 @@ impl ApplicationHandler for App { .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); - - let (swapchain_format, swapchain_color_space) = supported_storage_formats - .first() - .map(|(format, color_space)| (*format, *color_space)) + let (image_format, image_color_space) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap() + .into_iter() + .find(|(format, _)| { + self.device + .physical_device() + .image_format_properties(ImageFormatInfo { + format: *format, + usage: ImageUsage::STORAGE, + ..Default::default() + }) + .unwrap() + .is_some() + }) .unwrap(); + Swapchain::new( self.device.clone(), surface.clone(), SwapchainCreateInfo { min_image_count: surface_capabilities.min_image_count.max(2), - image_format: swapchain_format, - image_color_space: swapchain_color_space, - image_extent: window.inner_size().into(), - // To simplify the example, we will directly write to the swapchain images - // from the ray tracing shader. This requires the images to support storage - // usage. + image_format, + image_color_space, + image_extent: window_size.into(), + // To simplify the example, we will directly write to the swapchain images from + // the ray tracing shader. This requires the images to support storage usage. image_usage: ImageUsage::STORAGE, composite_alpha: surface_capabilities .supported_composite_alpha @@ -273,13 +271,12 @@ impl ApplicationHandler for App { ) .unwrap(); + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(self.device.clone())); let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( self.device.clone(), Default::default(), )); - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(self.device.clone())); - let scene = Scene::new( self, &images, @@ -288,12 +285,15 @@ impl ApplicationHandler for App { memory_allocator.clone(), self.command_buffer_allocator.clone(), ); + + let previous_frame_end = Some(sync::now(self.device.clone()).boxed()); + self.rcx = Some(RenderContext { window, swapchain, recreate_swapchain: false, - previous_frame_end: None, scene, + previous_frame_end, }); } @@ -319,37 +319,35 @@ impl ApplicationHandler for App { return; } - // Cleanup previous frame - if let Some(previous_frame_end) = rcx.previous_frame_end.as_mut() { - previous_frame_end.cleanup_finished(); - } + rcx.previous_frame_end.as_mut().unwrap().cleanup_finished(); - // Recreate swapchain if needed if rcx.recreate_swapchain { - let (new_swapchain, new_images) = - match rcx.swapchain.recreate(SwapchainCreateInfo { + let (new_swapchain, new_images) = rcx + .swapchain + .recreate(SwapchainCreateInfo { image_extent: window_size.into(), ..rcx.swapchain.create_info() - }) { - Ok(r) => r, - Err(e) => panic!("Failed to recreate swapchain: {e:?}"), - }; + }) + .expect("failed to recreate swapchain"); rcx.swapchain = new_swapchain; rcx.scene.handle_resize(&new_images); rcx.recreate_swapchain = false; } - // Acquire next image - let (image_index, suboptimal, acquire_future) = - match acquire_next_image(rcx.swapchain.clone(), None) { - Ok(r) => r, - Err(e) => { - eprintln!("Failed to acquire next image: {e:?}"); - rcx.recreate_swapchain = true; - return; - } - }; + let (image_index, suboptimal, acquire_future) = match acquire_next_image( + rcx.swapchain.clone(), + None, + ) + .map_err(Validated::unwrap) + { + Ok(r) => r, + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + return; + } + Err(e) => panic!("failed to acquire next image: {e}"), + }; if suboptimal { rcx.recreate_swapchain = true; @@ -365,13 +363,10 @@ impl ApplicationHandler for App { rcx.scene.record_commands(image_index, &mut builder); let command_buffer = builder.build().unwrap(); - let future = rcx .previous_frame_end .take() - .unwrap_or_else(|| { - Box::new(sync::now(self.device.clone())) as Box - }) + .unwrap() .join(acquire_future) .then_execute(self.queue.clone(), command_buffer) .unwrap() @@ -384,13 +379,17 @@ impl ApplicationHandler for App { ) .then_signal_fence_and_flush(); - match future { + match future.map_err(Validated::unwrap) { Ok(future) => { - rcx.previous_frame_end = Some(Box::new(future) as Box); + rcx.previous_frame_end = Some(future.boxed()); + } + Err(VulkanError::OutOfDate) => { + rcx.recreate_swapchain = true; + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } Err(e) => { - println!("Failed to flush future: {e:?}"); - rcx.previous_frame_end = Some(Box::new(sync::now(self.device.clone()))); + println!("failed to flush future: {e}"); + rcx.previous_frame_end = Some(sync::now(self.device.clone()).boxed()); } } } diff --git a/examples/ray-tracing-auto/scene.rs b/examples/ray-tracing-auto/scene.rs index f9be0d0c60..58bc02870b 100644 --- a/examples/ray-tracing-auto/scene.rs +++ b/examples/ray-tracing-auto/scene.rs @@ -33,39 +33,8 @@ use vulkano::{ sync::GpuFuture, }; -mod raygen { - vulkano_shaders::shader! { - ty: "raygen", - path: "rgen.glsl", - vulkan_version: "1.2" - } -} - -mod closest_hit { - vulkano_shaders::shader! { - ty: "closesthit", - path: "rchit.glsl", - vulkan_version: "1.2" - } -} - -mod miss { - vulkano_shaders::shader! { - ty: "miss", - path: "rmiss.glsl", - vulkan_version: "1.2" - } -} - -#[derive(BufferContents, Vertex)] -#[repr(C)] -struct MyVertex { - #[format(R32G32B32_SFLOAT)] - position: [f32; 3], -} - pub struct Scene { - descriptor_set_0: Arc, + descriptor_set: Arc, swapchain_image_sets: Vec<(Arc, Arc)>, pipeline_layout: Arc, descriptor_set_allocator: Arc, @@ -95,7 +64,6 @@ impl Scene { .unwrap() .entry_point("main") .unwrap(); - let miss = miss::load(app.device.clone()) .unwrap() .entry_point("main") @@ -126,7 +94,6 @@ impl Scene { stages: stages.into_iter().collect(), groups: groups.into_iter().collect(), max_pipeline_ray_recursion_depth: 1, - ..RayTracingPipelineCreateInfo::layout(pipeline_layout.clone()) }, ) @@ -162,11 +129,10 @@ impl Scene { .unwrap(); // Build the bottom-level acceleration structure and then the top-level acceleration - // structure. Acceleration structures are used to accelerate ray tracing. - // The bottom-level acceleration structure contains the geometry data. - // The top-level acceleration structure contains the instances of the bottom-level - // acceleration structures. In our shader, we will trace rays against the top-level - // acceleration structure. + // structure. Acceleration structures are used to accelerate ray tracing. The bottom-level + // acceleration structure contains the geometry data. The top-level acceleration structure + // contains the instances of the bottom-level acceleration structures. In our shader, we + // will trace rays against the top-level acceleration structure. let blas = unsafe { build_acceleration_structure_triangles( vertex_buffer, @@ -176,7 +142,6 @@ impl Scene { app.queue.clone(), ) }; - let tlas = unsafe { build_top_level_acceleration_structure( vec![AccelerationStructureInstance { @@ -216,7 +181,7 @@ impl Scene { ) .unwrap(); - let descriptor_set_0 = DescriptorSet::new( + let descriptor_set = DescriptorSet::new( descriptor_set_allocator.clone(), pipeline_layout.set_layouts()[0].clone(), [ @@ -234,7 +199,7 @@ impl Scene { ShaderBindingTable::new(memory_allocator.clone(), &pipeline).unwrap(); Scene { - descriptor_set_0, + descriptor_set, swapchain_image_sets, descriptor_set_allocator, pipeline_layout, @@ -264,31 +229,59 @@ impl Scene { self.pipeline_layout.clone(), 0, vec![ - self.descriptor_set_0.clone(), + self.descriptor_set.clone(), self.swapchain_image_sets[image_index as usize].1.clone(), ], ) - .unwrap(); - - builder + .unwrap() .bind_pipeline_ray_tracing(self.pipeline.clone()) .unwrap(); let extent = self.swapchain_image_sets[0].0.image().extent(); unsafe { - builder - .trace_rays( - self.shader_binding_table.addresses().clone(), - extent[0], - extent[1], - 1, - ) - .unwrap(); + builder.trace_rays( + self.shader_binding_table.addresses().clone(), + extent[0], + extent[1], + 1, + ) } + .unwrap(); } } +mod raygen { + vulkano_shaders::shader! { + ty: "raygen", + path: "rgen.glsl", + vulkan_version: "1.2", + } +} + +mod closest_hit { + vulkano_shaders::shader! { + ty: "closesthit", + path: "rchit.glsl", + vulkan_version: "1.2", + } +} + +mod miss { + vulkano_shaders::shader! { + ty: "miss", + path: "rmiss.glsl", + vulkan_version: "1.2", + } +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32B32_SFLOAT)] + position: [f32; 3], +} + /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( images: &[Arc], @@ -306,6 +299,7 @@ fn window_size_dependent_setup( [], ) .unwrap(); + (image_view, descriptor_set) }) .collect(); @@ -314,7 +308,9 @@ fn window_size_dependent_setup( } /// A helper function to build a acceleration structure and wait for its completion. -/// # SAFETY +/// +/// # Safety +/// /// - If you are referencing a bottom-level acceleration structure in a top-level acceleration /// structure, you must ensure that the bottom-level acceleration structure is kept alive. unsafe fn build_acceleration_structure_common( @@ -340,8 +336,8 @@ unsafe fn build_acceleration_structure_common( ) .unwrap(); - // We build a new scratch buffer for each acceleration structure for simplicity. - // You may want to reuse scratch buffers if you need to build many acceleration structures. + // We create a new scratch buffer for each acceleration structure for simplicity. You may want + // to reuse scratch buffers if you need to build many acceleration structures. let scratch_buffer = Buffer::new_slice::( memory_allocator.clone(), BufferCreateInfo { @@ -370,7 +366,7 @@ unsafe fn build_acceleration_structure_common( ) }; - let acceleration = unsafe { AccelerationStructure::new(device, as_create_info).unwrap() }; + let acceleration = unsafe { AccelerationStructure::new(device, as_create_info) }.unwrap(); as_build_geometry_info.dst_acceleration_structure = Some(acceleration.clone()); as_build_geometry_info.scratch_data = Some(scratch_buffer); @@ -380,9 +376,8 @@ unsafe fn build_acceleration_structure_common( ..Default::default() }; - // For simplicity, we build a single command buffer - // that builds the acceleration structure, then waits - // for its execution to complete. + // For simplicity, we build a single command buffer that builds the acceleration structure, + // then waits for its execution to complete. let mut builder = AutoCommandBufferBuilder::primary( command_buffer_allocator, queue.queue_family_index(), diff --git a/examples/ray-tracing/main.rs b/examples/ray-tracing/main.rs index 18f26d2def..0e535e58e8 100644 --- a/examples/ray-tracing/main.rs +++ b/examples/ray-tracing/main.rs @@ -73,7 +73,6 @@ impl App { InstanceCreateInfo { flags: InstanceCreateFlags::ENUMERATE_PORTABILITY, enabled_extensions: InstanceExtensions { - ext_debug_utils: true, ext_swapchain_colorspace: true, ..required_extensions }, @@ -91,11 +90,21 @@ impl App { khr_acceleration_structure: true, ..DeviceExtensions::empty() }; + let device_features = DeviceFeatures { + acceleration_structure: true, + ray_tracing_pipeline: true, + buffer_device_address: true, + synchronization2: true, + ..Default::default() + }; let (physical_device, queue_family_index) = instance .enumerate_physical_devices() .unwrap() .filter(|p| p.api_version() >= Version::V1_3) - .filter(|p| p.supported_extensions().contains(&device_extensions)) + .filter(|p| { + p.supported_extensions().contains(&device_extensions) + && p.supported_features().contains(&device_features) + }) .filter_map(|p| { p.queue_family_properties() .iter() @@ -117,6 +126,12 @@ impl App { }) .unwrap(); + println!( + "Using device: {} (type: {:?})", + physical_device.properties().device_name, + physical_device.properties().device_type, + ); + let (device, mut queues) = Device::new( physical_device, DeviceCreateInfo { @@ -125,13 +140,7 @@ impl App { queue_family_index, ..Default::default() }], - enabled_features: DeviceFeatures { - acceleration_structure: true, - ray_tracing_pipeline: true, - buffer_device_address: true, - synchronization2: true, - ..Default::default() - }, + enabled_features: device_features, ..Default::default() }, ) @@ -162,32 +171,7 @@ impl ApplicationHandler for App { .unwrap(), ); let surface = Surface::from_window(self.instance.clone(), window.clone()).unwrap(); - - let physical_device = self.device.physical_device(); - let supported_surface_formats = physical_device - .surface_formats(&surface, Default::default()) - .unwrap(); - - // For each supported format, check if it is supported for storage images - let supported_storage_formats = supported_surface_formats - .into_iter() - .filter(|(format, _)| { - physical_device - .image_format_properties(ImageFormatInfo { - format: *format, - usage: ImageUsage::STORAGE, - ..Default::default() - }) - .unwrap() - .is_some() - }) - .collect::>(); - - println!( - "Using device: {} (type: {:?})", - physical_device.properties().device_name, - physical_device.properties().device_type, - ); + let window_size = window.inner_size(); let swapchain_id = { let surface_capabilities = self @@ -195,10 +179,23 @@ impl ApplicationHandler for App { .physical_device() .surface_capabilities(&surface, Default::default()) .unwrap(); - - let (swapchain_format, swapchain_color_space) = supported_storage_formats - .first() - .map(|(format, color_space)| (*format, *color_space)) + let (image_format, image_color_space) = self + .device + .physical_device() + .surface_formats(&surface, Default::default()) + .unwrap() + .into_iter() + .find(|(format, _)| { + self.device + .physical_device() + .image_format_properties(ImageFormatInfo { + format: *format, + usage: ImageUsage::STORAGE, + ..Default::default() + }) + .unwrap() + .is_some() + }) .unwrap(); self.resources @@ -207,10 +204,13 @@ impl ApplicationHandler for App { surface, SwapchainCreateInfo { min_image_count: surface_capabilities.min_image_count.max(3), - image_format: swapchain_format, - image_extent: window.inner_size().into(), - image_usage: ImageUsage::STORAGE | ImageUsage::COLOR_ATTACHMENT, - image_color_space: swapchain_color_space, + image_format, + image_extent: window_size.into(), + // To simplify the example, we will directly write to the swapchain images + // from the ray tracing shader. This requires the images to support storage + // usage. + image_usage: ImageUsage::STORAGE, + image_color_space, composite_alpha: surface_capabilities .supported_composite_alpha .into_iter() @@ -274,19 +274,16 @@ impl ApplicationHandler for App { ) .unwrap(), ], - push_constant_ranges: vec![], ..Default::default() }, ) .unwrap(); + let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(self.device.clone())); let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new( self.device.clone(), Default::default(), )); - - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(self.device.clone())); - let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( self.device.clone(), Default::default(), @@ -305,8 +302,8 @@ impl ApplicationHandler for App { pipeline_layout.clone(), swapchain_id, virtual_swapchain_id, - descriptor_set_allocator, memory_allocator, + descriptor_set_allocator, command_buffer_allocator, ), ) diff --git a/examples/ray-tracing/scene.rs b/examples/ray-tracing/scene.rs index 2e70dfc477..79290e26b1 100644 --- a/examples/ray-tracing/scene.rs +++ b/examples/ray-tracing/scene.rs @@ -16,7 +16,7 @@ use vulkano::{ PrimaryCommandBufferAbstract, }, descriptor_set::{ - allocator::StandardDescriptorSetAllocator, sys::RawDescriptorSet, WriteDescriptorSet, + allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet, }, device::{Device, Queue}, format::Format, @@ -37,40 +37,9 @@ use vulkano_taskgraph::{ command_buffer::RecordingCommandBuffer, resource::Resources, Id, Task, TaskContext, TaskResult, }; -mod raygen { - vulkano_shaders::shader! { - ty: "raygen", - path: "rgen.glsl", - vulkan_version: "1.2" - } -} - -mod closest_hit { - vulkano_shaders::shader! { - ty: "closesthit", - path: "rchit.glsl", - vulkan_version: "1.2" - } -} - -mod miss { - vulkano_shaders::shader! { - ty: "miss", - path: "rmiss.glsl", - vulkan_version: "1.2" - } -} - -#[derive(BufferContents, Vertex)] -#[repr(C)] -struct MyVertex { - #[format(R32G32B32_SFLOAT)] - position: [f32; 3], -} - pub struct SceneTask { - descriptor_set_0: Arc, - swapchain_image_sets: Vec<(Arc, Arc)>, + descriptor_set: Arc, + swapchain_image_sets: Vec<(Arc, Arc)>, pipeline_layout: Arc, descriptor_set_allocator: Arc, virtual_swapchain_id: Id, @@ -87,8 +56,8 @@ impl SceneTask { pipeline_layout: Arc, swapchain_id: Id, virtual_swapchain_id: Id, - descriptor_set_allocator: Arc, memory_allocator: Arc, + descriptor_set_allocator: Arc, command_buffer_allocator: Arc, ) -> Self { let pipeline = { @@ -100,7 +69,6 @@ impl SceneTask { .unwrap() .entry_point("main") .unwrap(); - let miss = miss::load(app.device.clone()) .unwrap() .entry_point("main") @@ -113,6 +81,8 @@ impl SceneTask { PipelineShaderStageCreateInfo::new(closest_hit), ]; + // Define the shader groups that will eventually turn into the shader binding table. + // The numbers are the indices of the stages in the `stages` array. let groups = [ RayTracingShaderGroupCreateInfo::General { general_shader: 0 }, RayTracingShaderGroupCreateInfo::General { general_shader: 1 }, @@ -129,7 +99,6 @@ impl SceneTask { stages: stages.into_iter().collect(), groups: groups.into_iter().collect(), max_pipeline_ray_recursion_depth: 1, - ..RayTracingPipelineCreateInfo::layout(pipeline_layout.clone()) }, ) @@ -164,6 +133,11 @@ impl SceneTask { ) .unwrap(); + // Build the bottom-level acceleration structure and then the top-level acceleration + // structure. Acceleration structures are used to accelerate ray tracing. The bottom-level + // acceleration structure contains the geometry data. The top-level acceleration structure + // contains the instances of the bottom-level acceleration structures. In our shader, we + // will trace rays against the top-level acceleration structure. let blas = unsafe { build_acceleration_structure_triangles( vertex_buffer, @@ -173,7 +147,6 @@ impl SceneTask { app.queue.clone(), ) }; - let tlas = unsafe { build_top_level_acceleration_structure( vec![AccelerationStructureInstance { @@ -213,20 +186,16 @@ impl SceneTask { ) .unwrap(); - let descriptor_set_0 = RawDescriptorSet::new( + let descriptor_set = DescriptorSet::new( descriptor_set_allocator.clone(), - &pipeline_layout.set_layouts()[0], - 0, - ) - .unwrap(); - - unsafe { - let writes = &[ + pipeline_layout.set_layouts()[0].clone(), + [ WriteDescriptorSet::acceleration_structure(0, tlas.clone()), WriteDescriptorSet::buffer(1, uniform_buffer.clone()), - ]; - descriptor_set_0.update(writes, &[]).unwrap(); - } + ], + [], + ) + .unwrap(); let swapchain_image_sets = window_size_dependent_setup( &app.resources, @@ -239,7 +208,7 @@ impl SceneTask { ShaderBindingTable::new(memory_allocator.clone(), &pipeline).unwrap(); SceneTask { - descriptor_set_0: Arc::new(descriptor_set_0), + descriptor_set, swapchain_image_sets, descriptor_set_allocator, pipeline_layout, @@ -279,12 +248,11 @@ impl Task for SceneTask { &self.pipeline_layout, 0, &[ - &self.descriptor_set_0, - &self.swapchain_image_sets[image_index as usize].1, + self.descriptor_set.as_raw(), + self.swapchain_image_sets[image_index as usize].1.as_raw(), ], &[], )?; - cbf.bind_pipeline_ray_tracing(&self.pipeline)?; let extent = self.swapchain_image_sets[0].0.image().extent(); @@ -298,48 +266,85 @@ impl Task for SceneTask { ) }?; - for (image_view, descriptor_set) in self.swapchain_image_sets.iter() { + for (_, descriptor_set) in self.swapchain_image_sets.iter() { cbf.destroy_object(descriptor_set.clone()); - cbf.destroy_object(image_view.clone()); } + cbf.destroy_object(self.blas.clone()); cbf.destroy_object(self.tlas.clone()); cbf.destroy_object(self.uniform_buffer.clone().into()); - cbf.destroy_object(self.descriptor_set_0.clone()); + cbf.destroy_object(self.descriptor_set.clone()); Ok(()) } } +mod raygen { + vulkano_shaders::shader! { + ty: "raygen", + path: "rgen.glsl", + vulkan_version: "1.2", + } +} + +mod closest_hit { + vulkano_shaders::shader! { + ty: "closesthit", + path: "rchit.glsl", + vulkan_version: "1.2", + } +} + +mod miss { + vulkano_shaders::shader! { + ty: "miss", + path: "rmiss.glsl", + vulkan_version: "1.2", + } +} + +#[derive(BufferContents, Vertex)] +#[repr(C)] +struct MyVertex { + #[format(R32G32B32_SFLOAT)] + position: [f32; 3], +} + /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( resources: &Resources, swapchain_id: Id, pipeline_layout: &Arc, descriptor_set_allocator: &Arc, -) -> Vec<(Arc, Arc)> { +) -> Vec<(Arc, Arc)> { let swapchain_state = resources.swapchain(swapchain_id).unwrap(); let images = swapchain_state.images(); let swapchain_image_sets = images .iter() .map(|image| { - let descriptor_set = RawDescriptorSet::new( + let image_view = ImageView::new_default(image.clone()).unwrap(); + let descriptor_set = DescriptorSet::new( descriptor_set_allocator.clone(), - &pipeline_layout.set_layouts()[1], - 0, + pipeline_layout.set_layouts()[1].clone(), + [WriteDescriptorSet::image_view(0, image_view.clone())], + [], ) .unwrap(); - let image_view = ImageView::new_default(image.clone()).unwrap(); - let writes = &[WriteDescriptorSet::image_view(0, image_view.clone())]; - unsafe { descriptor_set.update(writes, &[]) }.unwrap(); - (image_view, Arc::new(descriptor_set)) + + (image_view, descriptor_set) }) .collect(); swapchain_image_sets } +/// A helper function to build a acceleration structure and wait for its completion. +/// +/// # Safety +/// +/// - If you are referencing a bottom-level acceleration structure in a top-level acceleration +/// structure, you must ensure that the bottom-level acceleration structure is kept alive. unsafe fn build_acceleration_structure_common( geometries: AccelerationStructureGeometries, primitive_count: u32, @@ -349,13 +354,6 @@ unsafe fn build_acceleration_structure_common( device: Arc, queue: Arc, ) -> Arc { - let mut builder = AutoCommandBufferBuilder::primary( - command_buffer_allocator, - queue.queue_family_index(), - CommandBufferUsage::OneTimeSubmit, - ) - .unwrap(); - let mut as_build_geometry_info = AccelerationStructureBuildGeometryInfo { mode: BuildAccelerationStructureMode::Build, flags: BuildAccelerationStructureFlags::PREFER_FAST_TRACE, @@ -370,6 +368,8 @@ unsafe fn build_acceleration_structure_common( ) .unwrap(); + // We create a new scratch buffer for each acceleration structure for simplicity. You may want + // to reuse scratch buffers if you need to build many acceleration structures. let scratch_buffer = Buffer::new_slice::( memory_allocator.clone(), BufferCreateInfo { @@ -398,7 +398,7 @@ unsafe fn build_acceleration_structure_common( ) }; - let acceleration = unsafe { AccelerationStructure::new(device, as_create_info).unwrap() }; + let acceleration = unsafe { AccelerationStructure::new(device, as_create_info) }.unwrap(); as_build_geometry_info.dst_acceleration_structure = Some(acceleration.clone()); as_build_geometry_info.scratch_data = Some(scratch_buffer); @@ -408,6 +408,15 @@ unsafe fn build_acceleration_structure_common( ..Default::default() }; + // For simplicity, we build a single command buffer that builds the acceleration structure, + // then waits for its execution to complete. + let mut builder = AutoCommandBufferBuilder::primary( + command_buffer_allocator, + queue.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + ) + .unwrap(); + builder .build_acceleration_structure( as_build_geometry_info, From e5b2baf328d6e06df611900a96437a95c57a7499 Mon Sep 17 00:00:00 2001 From: marc0246 <40955683+marc0246@users.noreply.github.com> Date: Tue, 28 Jan 2025 19:25:24 +0100 Subject: [PATCH 4/5] Fix library code inconsistencies --- examples/ray-tracing-auto/scene.rs | 11 +- examples/ray-tracing/scene.rs | 9 +- .../src/command_buffer/commands/bind_push.rs | 1 + .../src/command_buffer/commands/pipeline.rs | 38 ++-- .../src/command_buffer/commands/bind_push.rs | 1 + .../src/command_buffer/commands/pipeline.rs | 71 ++++---- vulkano/src/device/mod.rs | 21 ++- vulkano/src/pipeline/ray_tracing.rs | 172 ++++++++++-------- 8 files changed, 180 insertions(+), 144 deletions(-) diff --git a/examples/ray-tracing-auto/scene.rs b/examples/ray-tracing-auto/scene.rs index 58bc02870b..f990d7d7ac 100644 --- a/examples/ray-tracing-auto/scene.rs +++ b/examples/ray-tracing-auto/scene.rs @@ -239,15 +239,8 @@ impl Scene { let extent = self.swapchain_image_sets[0].0.image().extent(); - unsafe { - builder.trace_rays( - self.shader_binding_table.addresses().clone(), - extent[0], - extent[1], - 1, - ) - } - .unwrap(); + unsafe { builder.trace_rays(self.shader_binding_table.addresses().clone(), extent) } + .unwrap(); } } diff --git a/examples/ray-tracing/scene.rs b/examples/ray-tracing/scene.rs index 79290e26b1..5473c37b92 100644 --- a/examples/ray-tracing/scene.rs +++ b/examples/ray-tracing/scene.rs @@ -257,14 +257,7 @@ impl Task for SceneTask { let extent = self.swapchain_image_sets[0].0.image().extent(); - unsafe { - cbf.trace_rays( - self.shader_binding_table.addresses(), - extent[0], - extent[1], - 1, - ) - }?; + unsafe { cbf.trace_rays(self.shader_binding_table.addresses(), extent) }?; for (_, descriptor_set) in self.swapchain_image_sets.iter() { cbf.destroy_object(descriptor_set.clone()); diff --git a/vulkano-taskgraph/src/command_buffer/commands/bind_push.rs b/vulkano-taskgraph/src/command_buffer/commands/bind_push.rs index 17a98b5029..9262a5ced9 100644 --- a/vulkano-taskgraph/src/command_buffer/commands/bind_push.rs +++ b/vulkano-taskgraph/src/command_buffer/commands/bind_push.rs @@ -117,6 +117,7 @@ impl RecordingCommandBuffer<'_> { self } + /// Binds a ray tracing pipeline for future ray tracing calls. pub unsafe fn bind_pipeline_ray_tracing( &mut self, pipeline: &Arc, diff --git a/vulkano-taskgraph/src/command_buffer/commands/pipeline.rs b/vulkano-taskgraph/src/command_buffer/commands/pipeline.rs index 8cac98bdaa..e365285c8a 100644 --- a/vulkano-taskgraph/src/command_buffer/commands/pipeline.rs +++ b/vulkano-taskgraph/src/command_buffer/commands/pipeline.rs @@ -662,31 +662,37 @@ impl RecordingCommandBuffer<'_> { self } + /// Performs a single ray tracing operation using a ray tracing pipeline. + /// + /// A ray tracing pipeline must have been bound using [`bind_pipeline_ray_tracing`]. Any + /// resources used by the ray tracing pipeline, such as descriptor sets, must have been set + /// beforehand. + /// + /// # Safety + /// + /// - The general [shader safety requirements] apply. + /// + /// [`bind_pipeline_ray_tracing`]: Self::bind_pipeline_ray_tracing + /// [shader safety requirements]: vulkano::shader#safety pub unsafe fn trace_rays( &mut self, shader_binding_table_addresses: &ShaderBindingTableAddresses, - width: u32, - height: u32, - depth: u32, + dimensions: [u32; 3], ) -> Result<&mut Self> { - Ok(unsafe { - self.trace_rays_unchecked(shader_binding_table_addresses, width, height, depth) - }) + Ok(unsafe { self.trace_rays_unchecked(shader_binding_table_addresses, dimensions) }) } pub unsafe fn trace_rays_unchecked( &mut self, shader_binding_table_addresses: &ShaderBindingTableAddresses, - width: u32, - height: u32, - depth: u32, + dimensions: [u32; 3], ) -> &mut Self { - let fns = self.device().fns(); - let raygen = shader_binding_table_addresses.raygen.to_vk(); let miss = shader_binding_table_addresses.miss.to_vk(); let hit = shader_binding_table_addresses.hit.to_vk(); let callable = shader_binding_table_addresses.callable.to_vk(); + + let fns = self.device().fns(); unsafe { (fns.khr_ray_tracing_pipeline.cmd_trace_rays_khr)( self.handle(), @@ -694,11 +700,11 @@ impl RecordingCommandBuffer<'_> { &miss, &hit, &callable, - width, - height, - depth, - ); - } + dimensions[0], + dimensions[1], + dimensions[2], + ) + }; self } diff --git a/vulkano/src/command_buffer/commands/bind_push.rs b/vulkano/src/command_buffer/commands/bind_push.rs index 5dfdea2926..a0faddabc9 100644 --- a/vulkano/src/command_buffer/commands/bind_push.rs +++ b/vulkano/src/command_buffer/commands/bind_push.rs @@ -378,6 +378,7 @@ impl AutoCommandBufferBuilder { self } + /// Binds a ray tracing pipeline for future ray tracing calls. pub fn bind_pipeline_ray_tracing( &mut self, pipeline: Arc, diff --git a/vulkano/src/command_buffer/commands/pipeline.rs b/vulkano/src/command_buffer/commands/pipeline.rs index d88364d988..350d469f97 100644 --- a/vulkano/src/command_buffer/commands/pipeline.rs +++ b/vulkano/src/command_buffer/commands/pipeline.rs @@ -1593,26 +1593,34 @@ impl AutoCommandBufferBuilder { self } + /// Performs a single ray tracing operation using a ray tracing pipeline. + /// + /// A ray tracing pipeline must have been bound using [`bind_pipeline_ray_tracing`]. Any + /// resources used by the ray tracing pipeline, such as descriptor sets, must have been set + /// beforehand. + /// + /// # Safety + /// + /// - The general [shader safety requirements] apply. + /// + /// [`bind_pipeline_ray_tracing`]: Self::bind_pipeline_ray_tracing + /// [shader safety requirements]: vulkano::shader#safety pub unsafe fn trace_rays( &mut self, shader_binding_table_addresses: ShaderBindingTableAddresses, - width: u32, - height: u32, - depth: u32, + dimensions: [u32; 3], ) -> Result<&mut Self, Box> { self.inner - .validate_trace_rays(&shader_binding_table_addresses, width, height, depth)?; + .validate_trace_rays(&shader_binding_table_addresses, dimensions)?; - Ok(self.trace_rays_unchecked(shader_binding_table_addresses, width, height, depth)) + Ok(self.trace_rays_unchecked(shader_binding_table_addresses, dimensions)) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] pub unsafe fn trace_rays_unchecked( &mut self, shader_binding_table_addresses: ShaderBindingTableAddresses, - width: u32, - height: u32, - depth: u32, + dimensions: [u32; 3], ) -> &mut Self { let pipeline = self.builder_state.pipeline_ray_tracing.as_deref().unwrap(); @@ -1620,7 +1628,7 @@ impl AutoCommandBufferBuilder { self.add_descriptor_sets_resources(&mut used_resources, pipeline); self.add_command("trace_rays", used_resources, move |out| { - out.trace_rays_unchecked(&shader_binding_table_addresses, width, height, depth); + out.trace_rays_unchecked(&shader_binding_table_addresses, dimensions); }); self @@ -4985,21 +4993,17 @@ impl RecordingCommandBuffer { pub unsafe fn trace_rays( &mut self, shader_binding_table_addresses: &ShaderBindingTableAddresses, - width: u32, - height: u32, - depth: u32, + dimensions: [u32; 3], ) -> Result<&mut Self, Box> { - self.validate_trace_rays(shader_binding_table_addresses, width, height, depth)?; + self.validate_trace_rays(shader_binding_table_addresses, dimensions)?; - Ok(self.trace_rays_unchecked(shader_binding_table_addresses, width, height, depth)) + Ok(self.trace_rays_unchecked(shader_binding_table_addresses, dimensions)) } fn validate_trace_rays( &self, _shader_binding_table_addresses: &ShaderBindingTableAddresses, - width: u32, - height: u32, - depth: u32, + dimensions: [u32; 3], ) -> Result<(), Box> { if !self.device().enabled_features().ray_tracing_pipeline { return Err(Box::new(ValidationError { @@ -5026,9 +5030,9 @@ impl RecordingCommandBuffer { let device_properties = self.device().physical_device().properties(); - let width = width as u64; - let height = height as u64; - let depth = depth as u64; + let width = dimensions[0] as u64; + let height = dimensions[1] as u64; + let depth = dimensions[2] as u64; let max_width = device_properties.max_compute_work_group_count[0] as u64 * device_properties.max_compute_work_group_size[0] as u64; @@ -5036,7 +5040,9 @@ impl RecordingCommandBuffer { if width > max_width { return Err(Box::new(ValidationError { context: "width".into(), - problem: "exceeds maxComputeWorkGroupCount[0] * maxComputeWorkGroupSize[0]".into(), + problem: "exceeds `max_compute_work_group_count[0] * \ + max_compute_work_group_size[0]`" + .into(), vuids: &["VUID-vkCmdTraceRaysKHR-width-03638"], ..Default::default() })); @@ -5048,7 +5054,9 @@ impl RecordingCommandBuffer { if height > max_height { return Err(Box::new(ValidationError { context: "height".into(), - problem: "exceeds maxComputeWorkGroupCount[1] * maxComputeWorkGroupSize[1]".into(), + problem: "exceeds `max_compute_work_group_count[1] * \ + max_compute_work_group_size[1]`" + .into(), vuids: &["VUID-vkCmdTraceRaysKHR-height-03639"], ..Default::default() })); @@ -5060,7 +5068,9 @@ impl RecordingCommandBuffer { if depth > max_depth { return Err(Box::new(ValidationError { context: "depth".into(), - problem: "exceeds maxComputeWorkGroupCount[2] * maxComputeWorkGroupSize[2]".into(), + problem: "exceeds `max_compute_work_group_count[2] * \ + max_compute_work_group_size[2]`" + .into(), vuids: &["VUID-vkCmdTraceRaysKHR-depth-03640"], ..Default::default() })); @@ -5072,7 +5082,7 @@ impl RecordingCommandBuffer { if total_invocations > max_invocations { return Err(Box::new(ValidationError { context: "width * height * depth".into(), - problem: "exceeds maxRayDispatchInvocationCount".into(), + problem: "exceeds `max_ray_dispatch_invocation_count`".into(), vuids: &["VUID-vkCmdTraceRaysKHR-width-03641"], ..Default::default() })); @@ -5085,26 +5095,23 @@ impl RecordingCommandBuffer { pub unsafe fn trace_rays_unchecked( &mut self, shader_binding_table_addresses: &ShaderBindingTableAddresses, - width: u32, - height: u32, - depth: u32, + dimensions: [u32; 3], ) -> &mut Self { - let fns = self.device().fns(); - let raygen = shader_binding_table_addresses.raygen.to_vk(); let miss = shader_binding_table_addresses.miss.to_vk(); let hit = shader_binding_table_addresses.hit.to_vk(); let callable = shader_binding_table_addresses.callable.to_vk(); + let fns = self.device().fns(); (fns.khr_ray_tracing_pipeline.cmd_trace_rays_khr)( self.handle(), &raygen, &miss, &hit, &callable, - width, - height, - depth, + dimensions[0], + dimensions[1], + dimensions[2], ); self diff --git a/vulkano/src/device/mod.rs b/vulkano/src/device/mod.rs index 6d22b516fa..d5a1582bb8 100644 --- a/vulkano/src/device/mod.rs +++ b/vulkano/src/device/mod.rs @@ -1318,13 +1318,13 @@ impl Device { group_count, )?; - unsafe { - Ok(self.ray_tracing_shader_group_handles_unchecked( + Ok(unsafe { + self.ray_tracing_shader_group_handles_unchecked( ray_tracing_pipeline, first_group, group_count, - )?) - } + ) + }?) } fn validate_ray_tracing_pipeline_properties( @@ -1351,13 +1351,14 @@ impl Device { if (first_group + group_count) as usize > ray_tracing_pipeline.groups().len() { Err(Box::new(ValidationError { - problem: "the sum of `first_group` and `group_count` must be less than or equal\ - to the number of shader groups in pipeline" + problem: "the sum of `first_group` and `group_count` must be less than or equal \ + to the number of shader groups in the pipeline" .into(), vuids: &["VUID-vkGetRayTracingShaderGroupHandlesKHR-firstGroup-02419"], ..Default::default() }))? } + // TODO: VUID-vkGetRayTracingShaderGroupHandlesKHR-pipeline-07828 Ok(()) @@ -1388,9 +1389,10 @@ impl Device { data.len(), data.as_mut_ptr().cast(), ) - .result() - .map_err(VulkanError::from)?; } + .result() + .map_err(VulkanError::from)?; + Ok(ShaderGroupHandlesData { data, handle_size }) } } @@ -2231,10 +2233,12 @@ pub struct ShaderGroupHandlesData { } impl ShaderGroupHandlesData { + #[inline] pub fn data(&self) -> &[u8] { &self.data } + #[inline] pub fn handle_size(&self) -> u32 { self.handle_size } @@ -2242,6 +2246,7 @@ impl ShaderGroupHandlesData { impl ShaderGroupHandlesData { /// Returns an iterator over the handles in the data. + #[inline] pub fn iter(&self) -> impl ExactSizeIterator { self.data().chunks_exact(self.handle_size as usize) } diff --git a/vulkano/src/pipeline/ray_tracing.rs b/vulkano/src/pipeline/ray_tracing.rs index 7095f02638..fdec9448a3 100644 --- a/vulkano/src/pipeline/ray_tracing.rs +++ b/vulkano/src/pipeline/ray_tracing.rs @@ -1,41 +1,48 @@ //! Ray tracing pipeline functionality for GPU-accelerated ray tracing. //! //! # Overview +//! //! Ray tracing pipelines enable high-performance ray tracing by defining a set of shader stages //! that handle ray generation, intersection testing, and shading calculations. The pipeline //! consists of different shader stages organized into shader groups. //! -//! # Shader Types +//! # Shader types +//! +//! ## Ray generation shader +//! +//! - Entry point for ray tracing. +//! - Generates and traces primary rays. +//! - Controls the overall ray tracing process. +//! +//! ## Intersection shaders +//! +//! - **Built-in triangle intersection** handles standard triangle geometry intersection. +//! - **Custom intersection** implements custom geometry intersection testing. //! -//! ## Ray Generation Shader -//! - Entry point for ray tracing -//! - Generates and traces primary rays -//! - Controls the overall ray tracing process +//! ## Hit shaders //! -//! ## Intersection Shaders -//! - **Built-in Triangle Intersection**: Handles standard triangle geometry intersection -//! - **Custom Intersection**: Implements custom geometry intersection testing +//! - **Closest hit** executes when a ray finds its closest intersection. +//! - **Any hit** is an optional shader that runs on every potential intersection. //! -//! ## Hit Shaders -//! - **Closest Hit**: Executes when a ray finds its closest intersection -//! - **Any Hit**: Optional shader that runs on every potential intersection +//! ## Miss shader //! -//! ## Miss Shader -//! - Executes when a ray doesn't intersect any geometry -//! - Typically handles environment mapping or background colors +//! - Executes when a ray doesn't intersect any geometry. +//! - Typically handles environment mapping or background colors. //! -//! ## Callable Shader -//! - Utility shader that can be called from other shader stages -//! - Enables code reuse across different shader stages +//! ## Callable shader +//! +//! - Utility shader that can be called from other shader stages. +//! - Enables code reuse across different shader stages. +//! +//! # Pipeline organization //! -//! # Pipeline Organization //! Shaders are organized into groups: -//! - General groups: Contains ray generation, miss, or callable shaders -//! - Triangle hit groups: Contains closest-hit and optional any-hit shaders -//! - Procedural hit groups: Contains intersection, closest-hit, and optional any-hit shaders +//! - **General groups** contain ray generation, miss, or callable shaders. +//! - **Triangle hit groups** contain closest-hit and optional any-hit shaders. +//! - **Procedural hit groups** contain intersection, closest-hit, and optional any-hit shaders. //! -//! The ray tracing pipeline uses a Shader Binding Table (SBT) to organize and access -//! these shader groups during execution. +//! The ray tracing pipeline uses a Shader Binding Table (SBT) to organize and access these shader +//! groups during execution. use super::{ cache::PipelineCache, DynamicState, Pipeline, PipelineBindPoint, PipelineCreateFlags, @@ -100,6 +107,7 @@ impl RayTracingPipeline { if let Some(cache) = &cache { assert_eq!(device, cache.device()); } + create_info .validate(device) .map_err(|err| err.add_context("create_info"))?; @@ -135,6 +143,7 @@ impl RayTracingPipeline { ) .result() .map_err(VulkanError::from)?; + output.assume_init() }; @@ -164,6 +173,7 @@ impl RayTracingPipeline { (u32, u32), DescriptorBindingRequirements, > = HashMap::default(); + for stage in &stages { for (&loc, reqs) in stage .entry_point @@ -173,7 +183,10 @@ impl RayTracingPipeline { { match descriptor_binding_requirements.entry(loc) { Entry::Occupied(entry) => { - entry.into_mut().merge(reqs).expect("Could not produce an intersection of the shader descriptor requirements"); + entry.into_mut().merge(reqs).expect( + "could not produce an intersection of the shader descriptor \ + requirements", + ); } Entry::Vacant(entry) => { entry.insert(reqs.clone()); @@ -181,12 +194,14 @@ impl RayTracingPipeline { } } } + let num_used_descriptor_sets = descriptor_binding_requirements .keys() .map(|loc| loc.0) .max() .map(|x| x + 1) .unwrap_or(0); + Arc::new(Self { handle, device: InstanceOwnedDebugWrapper(device), @@ -203,22 +218,26 @@ impl RayTracingPipeline { }) } - // Returns the shader groups that the pipeline was created with. + /// Returns the shader groups that the pipeline was created with. + #[inline] pub fn groups(&self) -> &[RayTracingShaderGroupCreateInfo] { &self.groups } - // Returns the shader stages that the pipeline was created with. + /// Returns the shader stages that the pipeline was created with. + #[inline] pub fn stages(&self) -> &[PipelineShaderStageCreateInfo] { &self.stages } /// Returns the `Device` that the pipeline was created with. + #[inline] pub fn device(&self) -> &Arc { &self.device } /// Returns the flags that the pipeline was created with. + #[inline] pub fn flags(&self) -> PipelineCreateFlags { self.flags } @@ -296,7 +315,7 @@ pub struct RayTracingPipelineCreateInfo { /// The maximum recursion depth of the pipeline. /// - /// The default value is 1. + /// The default value is `1`. pub max_pipeline_ray_recursion_depth: u32, /// The dynamic state to use. @@ -324,6 +343,8 @@ pub struct RayTracingPipelineCreateInfo { } impl RayTracingPipelineCreateInfo { + /// Returns a `RayTracingPipelineCreateInfo` with the specified `layout`. + #[inline] pub fn layout(layout: Arc) -> Self { Self { flags: PipelineCreateFlags::empty(), @@ -331,9 +352,7 @@ impl RayTracingPipelineCreateInfo { groups: SmallVec::new(), max_pipeline_ray_recursion_depth: 1, dynamic_state: Default::default(), - layout, - base_pipeline: None, _ne: crate::NonExhaustive(()), } @@ -359,11 +378,11 @@ impl RayTracingPipelineCreateInfo { if flags.intersects(PipelineCreateFlags::DERIVATIVE) { let base_pipeline = base_pipeline.as_ref().ok_or_else(|| { Box::new(ValidationError { - problem: "`flags` contains `PipelineCreateFlags::DERIVATIVE`, but \ - `base_pipeline` is `None`" + context: "flags".into(), + problem: "contains `PipelineCreateFlags::DERIVATIVE`, but `base_pipeline` is \ + `None`" .into(), - vuids: &["VUID-VkRayTracingPipelineCreateInfoKHR-flags-07984 -"], + vuids: &["VUID-VkRayTracingPipelineCreateInfoKHR-flags-07984"], ..Default::default() }) })?; @@ -381,8 +400,9 @@ impl RayTracingPipelineCreateInfo { } } else if base_pipeline.is_some() { return Err(Box::new(ValidationError { - problem: "`flags` does not contain `PipelineCreateFlags::DERIVATIVE`, but \ - `base_pipeline` is `Some`" + context: "flags".into(), + problem: "does not contain `PipelineCreateFlags::DERIVATIVE`, but `base_pipeline` \ + is `Some`" .into(), ..Default::default() })); @@ -390,7 +410,8 @@ impl RayTracingPipelineCreateInfo { if stages.is_empty() { return Err(Box::new(ValidationError { - problem: "`stages` is empty".into(), + context: "stages".into(), + problem: "is empty".into(), vuids: &["VUID-VkRayTracingPipelineCreateInfoKHR-pLibraryInfo-07999"], ..Default::default() })); @@ -408,9 +429,9 @@ impl RayTracingPipelineCreateInfo { })); } - for stage in stages { + for (stage_index, stage) in stages.iter().enumerate() { stage.validate(device).map_err(|err| { - err.add_context("stages") + err.add_context(format!("stages[{}]", stage_index)) .set_vuids(&["VUID-VkRayTracingPipelineCreateInfoKHR-pStages-parameter"]) })?; @@ -426,7 +447,7 @@ impl RayTracingPipelineCreateInfo { ) .map_err(|err| { Box::new(ValidationError { - context: "stage.entry_point".into(), + context: format!("stages[{}].entry_point", stage_index).into(), vuids: &[ "VUID-VkRayTracingPipelineCreateInfoKHR-layout-07987", "VUID-VkRayTracingPipelineCreateInfoKHR-layout-07988", @@ -440,11 +461,13 @@ impl RayTracingPipelineCreateInfo { if groups.is_empty() { return Err(Box::new(ValidationError { - problem: "`groups` is empty".into(), + context: "groups".into(), + problem: "is empty".into(), vuids: &["VUID-VkRayTracingPipelineCreateInfoKHR-flags-08700"], ..Default::default() })); } + for group in groups { group.validate(stages).map_err(|err| { err.add_context("groups") @@ -465,8 +488,9 @@ impl RayTracingPipelineCreateInfo { // ..Default::default() // })); // } + if !dynamic_state.is_empty() { - todo!("Dynamic state for ray tracing pipelines is not yet supported"); + todo!("dynamic state for ray tracing pipelines is not yet supported"); } let max_ray_recursion_depth = device @@ -474,13 +498,14 @@ impl RayTracingPipelineCreateInfo { .properties() .max_ray_recursion_depth .unwrap(); + if max_pipeline_ray_recursion_depth > max_ray_recursion_depth { return Err(Box::new(ValidationError { - problem: format!( - "`max_pipeline_ray_recursion_depth` is greater than the device's max value of {}", - max_ray_recursion_depth - ).into(), - vuids: &["VUID-VkRayTracingPipelineCreateInfoKHR-maxPipelineRayRecursionDepth-03589"], + context: "max_pipeline_ray_recursion_depth".into(), + problem: "is greater than the `max_ray_recursion_depth` device property".into(), + vuids: &[ + "VUID-VkRayTracingPipelineCreateInfoKHR-maxPipelineRayRecursionDepth-03589", + ], ..Default::default() })); } @@ -618,9 +643,10 @@ impl RayTracingPipelineCreateInfo { /// Enum representing different types of Ray Tracing Shader Groups. /// -/// Contains the index of the shader to use for each type of shader group. -/// The index corresponds to the position of the shader in the `stages` field of the -/// `RayTracingPipelineCreateInfo`. +/// Contains the index of the shader to use for each type of shader group. The index corresponds to +/// the position of the shader in the [`stages`] field of the [`RayTracingPipelineCreateInfo`]. +/// +/// [`stages`]: RayTracingPipelineCreateInfo::stages #[derive(Debug, Clone)] pub enum RayTracingShaderGroupCreateInfo { /// General shader group type, typically used for ray generation and miss shaders. @@ -630,32 +656,31 @@ pub enum RayTracingShaderGroupCreateInfo { /// - Miss shader /// - Callable shader General { - /// Index of the general shader stage + /// Index of the general shader stage. general_shader: u32, }, /// Procedural hit shader group type, used for custom intersection testing. /// - /// Used when implementing custom intersection shapes or volumes. - /// Requires an intersection shader and can optionally include closest hit - /// and any hit shaders. + /// Used when implementing custom intersection shapes or volumes. Requires an intersection + /// shader and can optionally include closest hit and any hit shaders. ProceduralHit { - /// Optional index of the closest hit shader stage + /// Optional index of the closest hit shader stage. closest_hit_shader: Option, - /// Optional index of the any hit shader stage + /// Optional index of the any hit shader stage. any_hit_shader: Option, - /// Index of the intersection shader stage + /// Index of the intersection shader stage. intersection_shader: u32, }, /// Triangle hit shader group type, used for built-in triangle intersection. /// - /// Used for standard triangle geometry intersection testing. - /// Can optionally include closest hit and any hit shaders. + /// Used for standard triangle geometry intersection testing. Can optionally include closest + /// hit and any hit shaders. TrianglesHit { - /// Optional index of the closest hit shader stage + /// Optional index of the closest hit shader stage. closest_hit_shader: Option, - /// Optional index of the any hit shader stage + /// Optional index of the any hit shader stage. any_hit_shader: Option, }, } @@ -675,7 +700,9 @@ impl RayTracingShaderGroupCreateInfo { | ExecutionModel::MissKHR | ExecutionModel::CallableKHR => Ok(()), _ => Err(Box::new(ValidationError { - problem: "general shader in GENERAL group must be a RayGeneration, Miss, or Callable shader".into(), + problem: "general shader in `GENERAL` group must be a `RayGeneration`, \ + `Miss`, or `Callable` shader" + .into(), vuids: &["VUID-VkRayTracingShaderGroupCreateInfoKHR-type-03474"], ..Default::default() })), @@ -688,7 +715,9 @@ impl RayTracingShaderGroupCreateInfo { } => { if get_shader_type(*intersection_shader) != ExecutionModel::IntersectionKHR { return Err(Box::new(ValidationError { - problem: "intersection shader in PROCEDURAL_HIT_GROUP must be an Intersection shader".into(), + problem: "intersection shader in `PROCEDURAL_HIT_GROUP` must be an \ + `Intersection` shader" + .into(), vuids: &["VUID-VkRayTracingShaderGroupCreateInfoKHR-type-03476"], ..Default::default() })); @@ -697,7 +726,7 @@ impl RayTracingShaderGroupCreateInfo { if let Some(any_hit_shader) = any_hit_shader { if get_shader_type(*any_hit_shader) != ExecutionModel::AnyHitKHR { return Err(Box::new(ValidationError { - problem: "any hit shader must be an AnyHit shader".into(), + problem: "any hit shader must be an `AnyHit` shader".into(), vuids: &[ "VUID-VkRayTracingShaderGroupCreateInfoKHR-anyHitShader-03479", ], @@ -709,7 +738,7 @@ impl RayTracingShaderGroupCreateInfo { if let Some(closest_hit_shader) = closest_hit_shader { if get_shader_type(*closest_hit_shader) != ExecutionModel::ClosestHitKHR { return Err(Box::new(ValidationError { - problem: "closest hit shader must be a ClosestHit shader".into(), + problem: "closest hit shader must be a `ClosestHit` shader".into(), vuids: &[ "VUID-VkRayTracingShaderGroupCreateInfoKHR-closestHitShader-03478", ], @@ -725,7 +754,7 @@ impl RayTracingShaderGroupCreateInfo { if let Some(any_hit_shader) = any_hit_shader { if get_shader_type(*any_hit_shader) != ExecutionModel::AnyHitKHR { return Err(Box::new(ValidationError { - problem: "any hit shader must be an AnyHit shader".into(), + problem: "any hit shader must be an `AnyHit` shader".into(), vuids: &[ "VUID-VkRayTracingShaderGroupCreateInfoKHR-anyHitShader-03479", ], @@ -737,7 +766,7 @@ impl RayTracingShaderGroupCreateInfo { if let Some(closest_hit_shader) = closest_hit_shader { if get_shader_type(*closest_hit_shader) != ExecutionModel::ClosestHitKHR { return Err(Box::new(ValidationError { - problem: "closest hit shader must be a ClosestHit shader".into(), + problem: "closest hit shader must be a `ClosestHit` shader".into(), vuids: &[ "VUID-VkRayTracingShaderGroupCreateInfoKHR-closestHitShader-03478", ], @@ -824,11 +853,6 @@ pub struct ShaderBindingTable { } impl ShaderBindingTable { - /// Returns the addresses of the shader groups in the shader binding table. - pub fn addresses(&self) -> &ShaderBindingTableAddresses { - &self.addresses - } - /// Automatically creates a shader binding table from a ray tracing pipeline. pub fn new( allocator: Arc, @@ -978,6 +1002,12 @@ impl ShaderBindingTable { _buffer: sbt_buffer, }) } + + /// Returns the addresses of the shader groups in the shader binding table. + #[inline] + pub fn addresses(&self) -> &ShaderBindingTableAddresses { + &self.addresses + } } fn new_bytes_buffer_with_alignment( From 175057dfe776f46bfb26b7d936afed14ed0e9c69 Mon Sep 17 00:00:00 2001 From: marc0246 <40955683+marc0246@users.noreply.github.com> Date: Tue, 28 Jan 2025 19:47:38 +0100 Subject: [PATCH 5/5] Fix `get_ray_tracing_pipeline_shader_group_handles` scoping --- vulkano/src/device/mod.rs | 118 ---------------------------- vulkano/src/pipeline/ray_tracing.rs | 104 ++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 125 deletions(-) diff --git a/vulkano/src/device/mod.rs b/vulkano/src/device/mod.rs index d5a1582bb8..62f8f40006 100644 --- a/vulkano/src/device/mod.rs +++ b/vulkano/src/device/mod.rs @@ -114,7 +114,6 @@ use crate::{ instance::{Instance, InstanceOwned, InstanceOwnedDebugWrapper}, macros::{impl_id_counter, vulkan_bitflags}, memory::{ExternalMemoryHandleType, MemoryFdProperties, MemoryRequirements}, - pipeline::ray_tracing::RayTracingPipeline, Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError, VulkanObject, }; @@ -1305,96 +1304,6 @@ impl Device { Ok(()) } - - pub fn ray_tracing_shader_group_handles( - &self, - ray_tracing_pipeline: &RayTracingPipeline, - first_group: u32, - group_count: u32, - ) -> Result> { - self.validate_ray_tracing_pipeline_properties( - ray_tracing_pipeline, - first_group, - group_count, - )?; - - Ok(unsafe { - self.ray_tracing_shader_group_handles_unchecked( - ray_tracing_pipeline, - first_group, - group_count, - ) - }?) - } - - fn validate_ray_tracing_pipeline_properties( - &self, - ray_tracing_pipeline: &RayTracingPipeline, - first_group: u32, - group_count: u32, - ) -> Result<(), Box> { - if !self.enabled_features().ray_tracing_pipeline - || self - .physical_device() - .properties() - .shader_group_handle_size - .is_none() - { - Err(Box::new(ValidationError { - problem: "device property `shader_group_handle_size` is empty".into(), - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceFeature( - "ray_tracing_pipeline", - )])]), - ..Default::default() - }))?; - }; - - if (first_group + group_count) as usize > ray_tracing_pipeline.groups().len() { - Err(Box::new(ValidationError { - problem: "the sum of `first_group` and `group_count` must be less than or equal \ - to the number of shader groups in the pipeline" - .into(), - vuids: &["VUID-vkGetRayTracingShaderGroupHandlesKHR-firstGroup-02419"], - ..Default::default() - }))? - } - - // TODO: VUID-vkGetRayTracingShaderGroupHandlesKHR-pipeline-07828 - - Ok(()) - } - - #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] - pub unsafe fn ray_tracing_shader_group_handles_unchecked( - &self, - ray_tracing_pipeline: &RayTracingPipeline, - first_group: u32, - group_count: u32, - ) -> Result { - let handle_size = self - .physical_device() - .properties() - .shader_group_handle_size - .unwrap(); - - let mut data = vec![0u8; (handle_size * group_count) as usize]; - let fns = self.fns(); - unsafe { - (fns.khr_ray_tracing_pipeline - .get_ray_tracing_shader_group_handles_khr)( - self.handle, - ray_tracing_pipeline.handle(), - first_group, - group_count, - data.len(), - data.as_mut_ptr().cast(), - ) - } - .result() - .map_err(VulkanError::from)?; - - Ok(ShaderGroupHandlesData { data, handle_size }) - } } impl Debug for Device { @@ -2225,33 +2134,6 @@ impl Deref for DeviceOwnedDebugWrapper { } } -/// Holds the data returned by [`Device::ray_tracing_shader_group_handles`]. -#[derive(Clone, Debug)] -pub struct ShaderGroupHandlesData { - data: Vec, - handle_size: u32, -} - -impl ShaderGroupHandlesData { - #[inline] - pub fn data(&self) -> &[u8] { - &self.data - } - - #[inline] - pub fn handle_size(&self) -> u32 { - self.handle_size - } -} - -impl ShaderGroupHandlesData { - /// Returns an iterator over the handles in the data. - #[inline] - pub fn iter(&self) -> impl ExactSizeIterator { - self.data().chunks_exact(self.handle_size as usize) - } -} - #[cfg(test)] mod tests { use crate::device::{ diff --git a/vulkano/src/pipeline/ray_tracing.rs b/vulkano/src/pipeline/ray_tracing.rs index fdec9448a3..1187a3a9c0 100644 --- a/vulkano/src/pipeline/ray_tracing.rs +++ b/vulkano/src/pipeline/ray_tracing.rs @@ -241,6 +241,74 @@ impl RayTracingPipeline { pub fn flags(&self) -> PipelineCreateFlags { self.flags } + + /// Retrieves the opaque handles of shaders in the ray tracing pipeline. + /// + /// Handles for `group_count` groups are retrieved, starting at `first_group`. The group + /// indices correspond to the [`groups`] the pipeline was created with. + /// + /// [`groups`]: Self::groups + pub fn group_handles( + &self, + first_group: u32, + group_count: u32, + ) -> Result> { + self.validate_group_handles(first_group, group_count)?; + + Ok(unsafe { self.group_handles_unchecked(first_group, group_count) }?) + } + + fn validate_group_handles( + &self, + first_group: u32, + group_count: u32, + ) -> Result<(), Box> { + if (first_group + group_count) as usize > self.groups().len() { + Err(Box::new(ValidationError { + problem: "the sum of `first_group` and `group_count` must be less than or equal \ + to the number of shader groups in the pipeline" + .into(), + vuids: &["VUID-vkGetRayTracingShaderGroupHandlesKHR-firstGroup-02419"], + ..Default::default() + }))? + } + + // TODO: VUID-vkGetRayTracingShaderGroupHandlesKHR-pipeline-07828 + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn group_handles_unchecked( + &self, + first_group: u32, + group_count: u32, + ) -> Result { + let handle_size = self + .device() + .physical_device() + .properties() + .shader_group_handle_size + .unwrap(); + + let mut data = vec![0u8; (handle_size * group_count) as usize]; + let fns = self.device().fns(); + unsafe { + (fns.khr_ray_tracing_pipeline + .get_ray_tracing_shader_group_handles_khr)( + self.device().handle(), + self.handle(), + first_group, + group_count, + data.len(), + data.as_mut_ptr().cast(), + ) + } + .result() + .map_err(VulkanError::from)?; + + Ok(ShaderGroupHandlesData { data, handle_size }) + } } impl Pipeline for RayTracingPipeline { @@ -832,6 +900,33 @@ pub(crate) struct RayTracingPipelineCreateInfoFields3Vk { pub(crate) stages_fields2_vk: SmallVec<[PipelineShaderStageCreateInfoFields2Vk; 5]>, } +/// Holds the data returned by [`RayTracingPipeline::group_handles`]. +#[derive(Clone, Debug)] +pub struct ShaderGroupHandlesData { + data: Vec, + handle_size: u32, +} + +impl ShaderGroupHandlesData { + /// Returns the opaque handle data as one blob. + #[inline] + pub fn data(&self) -> &[u8] { + &self.data + } + + /// Returns the shader group handle size of the device. + #[inline] + pub fn handle_size(&self) -> u32 { + self.handle_size + } + + /// Returns an iterator over the data of each group handle. + #[inline] + pub fn iter(&self) -> impl ExactSizeIterator { + self.data().chunks_exact(self.handle_size as usize) + } +} + /// An object that holds the strided addresses of the shader groups in a shader binding table. #[derive(Debug, Clone)] pub struct ShaderBindingTableAddresses { @@ -865,13 +960,8 @@ impl ShaderBindingTable { let mut hit_shader_handles = Vec::new(); let mut callable_shader_handles = Vec::new(); - let handle_data = ray_tracing_pipeline - .device() - .ray_tracing_shader_group_handles( - ray_tracing_pipeline, - 0, - ray_tracing_pipeline.groups().len() as u32, - )?; + let handle_data = + ray_tracing_pipeline.group_handles(0, ray_tracing_pipeline.groups().len() as u32)?; let mut handle_iter = handle_data.iter(); for group in ray_tracing_pipeline.groups() {