1use {
4 super::{
5 DriverError,
6 device::Device,
7 merge_push_constant_ranges,
8 physical_device::khr::RayTracingPipelineProperties,
9 shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader},
10 },
11 crate::lazy_str,
12 ash::vk::{self, Handle},
13 derive_builder::Builder,
14 log::warn,
15 std::{
16 ffi::CString,
17 fmt::{Debug, Formatter},
18 hash::{Hash, Hasher},
19 sync::Arc,
20 thread::panicking,
21 },
22};
23
24#[derive(Clone)]
30#[read_only::cast]
31pub struct RayTracingPipeline {
32 pub(crate) inner: Arc<RayTracingPipelineInner>,
33}
34
35impl RayTracingPipeline {
36 #[profiling::function]
95 pub fn create<S>(
96 device: &Device,
97 info: impl Into<RayTracingPipelineInfo>,
98 shaders: impl IntoIterator<Item = S>,
99 shader_groups: impl IntoIterator<Item = RayTracingShaderGroup>,
100 ) -> Result<Self, DriverError>
101 where
102 S: TryInto<Shader>,
103 S::Error: Into<DriverError>,
104 {
105 if device.physical.vk_khr_ray_tracing_pipeline.is_none() {
106 warn!("unsupported ray tracing pipeline creation: missing ray tracing properties");
107
108 return Err(DriverError::Unsupported);
109 }
110
111 let info = info.into();
112 let shader_groups = shader_groups
113 .into_iter()
114 .map(|shader_group| shader_group.into())
115 .collect::<Vec<_>>();
116 let group_count = shader_groups.len();
117
118 let shaders = shaders
119 .into_iter()
120 .map(|shader| shader.try_into().map_err(Into::into))
121 .collect::<Result<Vec<_>, _>>()?;
122 let push_constants = shaders
123 .iter()
124 .map(|shader| shader.push_constant_range())
125 .filter_map(|mut push_const| push_const.take())
126 .collect::<Vec<_>>();
127
128 let mut descriptor_bindings = Shader::merge_descriptor_bindings(
130 shaders.iter().map(|shader| shader.descriptor_bindings()),
131 )?;
132 for (descriptor_info, _) in descriptor_bindings.values_mut() {
133 if descriptor_info.binding_count() == 0 {
134 descriptor_info.set_binding_count(info.bindless_descriptor_count);
135 }
136 }
137
138 let descriptor_info = PipelineDescriptorInfo::create(device, &descriptor_bindings)?;
139 let layouts = descriptor_info
140 .layouts
141 .values()
142 .map(|layout| layout.handle)
143 .collect::<Box<_>>();
144
145 unsafe {
146 let layout = device
147 .create_pipeline_layout(
148 &vk::PipelineLayoutCreateInfo::default()
149 .set_layouts(&layouts)
150 .push_constant_ranges(&push_constants),
151 None,
152 )
153 .map_err(|err| {
154 warn!("unable to create ray tracing pipeline layout: {err}");
155
156 DriverError::Unsupported
157 })?;
158 let entry_points: Box<[CString]> = shaders
159 .iter()
160 .map(|shader| CString::new(shader.entry_name.as_str()))
161 .collect::<Result<_, _>>()
162 .map_err(|err| {
163 warn!("invalid ray tracing shader entry name: {err}");
164
165 DriverError::InvalidData
166 })?;
167 let specialization_infos: Box<[Option<vk::SpecializationInfo>]> = shaders
168 .iter()
169 .map(|shader| shader.specialization.as_ref().map(Into::into))
170 .collect();
171 let mut shader_stages: Vec<vk::PipelineShaderStageCreateInfo> =
172 Vec::with_capacity(shaders.len());
173 let mut shader_modules = Vec::with_capacity(shaders.len());
174 for (idx, shader) in shaders.iter().enumerate() {
175 let module = device
176 .create_shader_module(
177 &vk::ShaderModuleCreateInfo::default().code(shader.spirv.words()),
178 None,
179 )
180 .map_err(|err| {
181 warn!("unable to create ray tracing shader module: {err}");
182
183 device.destroy_pipeline_layout(layout, None);
184
185 for module in shader_modules.drain(..) {
186 device.destroy_shader_module(module, None);
187 }
188
189 DriverError::Unsupported
190 })?;
191
192 shader_modules.push(module);
193
194 let mut stage = vk::PipelineShaderStageCreateInfo::default()
195 .module(module)
196 .name(entry_points[idx].as_ref())
197 .stage(shader.stage);
198
199 if let Some(specialization_info) = &specialization_infos[idx] {
200 stage = stage.specialization_info(specialization_info);
201 }
202
203 shader_stages.push(stage);
204 }
205
206 let mut dynamic_states = Vec::with_capacity(1);
207
208 if info.dynamic_stack_size {
209 dynamic_states.push(vk::DynamicState::RAY_TRACING_PIPELINE_STACK_SIZE_KHR);
210 }
211
212 let khr_ray_tracing_pipeline = Device::expect_vk_khr_ray_tracing_pipeline(device);
213
214 let handle = khr_ray_tracing_pipeline.create_ray_tracing_pipelines(
215 vk::DeferredOperationKHR::null(),
216 Device::pipeline_cache(device),
217 &[vk::RayTracingPipelineCreateInfoKHR::default()
218 .stages(&shader_stages)
219 .groups(&shader_groups)
220 .max_pipeline_ray_recursion_depth(
221 info.max_ray_recursion_depth.min(
222 device
223 .physical
224 .expect_ray_tracing_pipeline_properties()
225 .max_ray_recursion_depth,
226 ),
227 )
228 .layout(layout)
229 .dynamic_state(
230 &vk::PipelineDynamicStateCreateInfo::default()
231 .dynamic_states(&dynamic_states),
232 )],
233 None,
234 );
235
236 for shader_module in shader_modules.iter().copied() {
237 device.destroy_shader_module(shader_module, None);
238 }
239
240 let handle = handle
241 .map_err(|(pipelines, err)| {
242 warn!("unable to create ray tracing pipeline: {err}");
243
244 for pipeline in pipelines {
245 device.destroy_pipeline(pipeline, None);
246 }
247
248 device.destroy_pipeline_layout(layout, None);
249
250 DriverError::Unsupported
251 })?
252 .into_iter()
253 .find(|handle| !handle.is_null())
254 .ok_or_else(|| {
255 warn!("missing pipeline handle");
256
257 DriverError::Unsupported
258 })?;
259 let &RayTracingPipelineProperties {
260 shader_group_handle_size,
261 ..
262 } = device.physical.expect_ray_tracing_pipeline_properties();
263
264 let push_constants = merge_push_constant_ranges(&push_constants).into_boxed_slice();
265
266 let shader_group_handles = {
278 khr_ray_tracing_pipeline.get_ray_tracing_shader_group_handles(
279 handle,
280 0,
281 group_count as u32,
282 group_count * shader_group_handle_size as usize,
283 )
284 }
285 .map_err(|_| DriverError::InvalidData)?
286 .into_boxed_slice();
287
288 Ok(Self {
289 inner: Arc::new(RayTracingPipelineInner {
290 descriptor_bindings,
291 descriptor_info,
292 device: device.clone(),
293 handle,
294 info,
295 layout,
296 push_constants,
297 shader_group_handles,
298 }),
299 })
300 }
301 }
302
303 pub fn device(&self) -> &Device {
305 &self.inner.device
306 }
307
308 pub fn group_handle(&self, idx: usize) -> &[u8] {
320 let &RayTracingPipelineProperties {
321 shader_group_handle_size,
322 ..
323 } = self
324 .inner
325 .device
326 .physical
327 .expect_ray_tracing_pipeline_properties();
328 let start = idx * shader_group_handle_size as usize;
329 let end = start + shader_group_handle_size as usize;
330
331 &self.inner.shader_group_handles[start..end]
332 }
333
334 #[profiling::function]
341 pub fn group_stack_size(
342 &self,
343 group: u32,
344 group_shader: vk::ShaderGroupShaderKHR,
345 ) -> vk::DeviceSize {
346 let khr_ray_tracing_pipeline =
350 Device::expect_vk_khr_ray_tracing_pipeline(&self.inner.device);
351
352 unsafe {
353 khr_ray_tracing_pipeline.get_ray_tracing_shader_group_stack_size(
354 self.handle(),
355 group,
356 group_shader,
357 )
358 }
359 }
360
361 pub fn handle(&self) -> vk::Pipeline {
363 self.inner.handle
364 }
365
366 pub fn info(&self) -> RayTracingPipelineInfo {
368 self.inner.info
369 }
370
371 pub fn set_debug_name(&self, name: impl AsRef<str>) {
373 Device::try_set_debug_utils_object_name(&self.inner.device, self.inner.handle, &name);
374 Device::try_set_private_data_object_name(
375 &self.inner.device,
376 vk::ObjectType::PIPELINE,
377 self.inner.handle,
378 &name,
379 );
380 Device::try_set_debug_utils_object_name(
381 &self.inner.device,
382 self.inner.layout,
383 lazy_str!("{} (layout)", name.as_ref()),
384 );
385
386 for (set_idx, layout) in &self.inner.descriptor_info.layouts {
387 layout.set_debug_name(lazy_str!("{} (DS{set_idx})", name.as_ref()));
388 }
389 }
390
391 pub fn with_debug_name(self, name: impl AsRef<str>) -> Self {
393 self.set_debug_name(name);
394
395 self
396 }
397}
398
399impl Debug for RayTracingPipeline {
400 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
401 let mut res = f.debug_struct(stringify!(RayTracingPipeline));
402
403 if let Some(debug_name) = &Device::private_data_object_name(
404 &self.inner.device,
405 vk::ObjectType::PIPELINE,
406 self.inner.handle,
407 ) {
408 res.field("debug_name", debug_name);
409 }
410
411 res.field("handle", &self.inner.handle)
412 .finish_non_exhaustive()
413 }
414}
415
416impl Eq for RayTracingPipeline {}
417
418impl Hash for RayTracingPipeline {
419 fn hash<H: Hasher>(&self, state: &mut H) {
420 Arc::as_ptr(&self.inner).hash(state);
421 }
422}
423
424impl PartialEq for RayTracingPipeline {
425 fn eq(&self, other: &Self) -> bool {
426 Arc::ptr_eq(&self.inner, &other.inner)
427 }
428}
429
430#[derive(Builder, Clone, Copy, Debug, Eq, Hash, PartialEq)]
432#[builder(
433 build_fn(private, name = "fallible_build"),
434 derive(Clone, Copy, Debug),
435 pattern = "owned"
436)]
437pub struct RayTracingPipelineInfo {
438 #[builder(default = "8192")]
461 pub bindless_descriptor_count: u32,
462
463 #[builder(default)]
470 pub dynamic_stack_size: bool,
471
472 #[builder(default = "16")]
478 pub max_ray_recursion_depth: u32,
479}
480
481impl RayTracingPipelineInfo {
482 pub fn builder() -> RayTracingPipelineInfoBuilder {
484 Default::default()
485 }
486
487 pub fn into_builder(self) -> RayTracingPipelineInfoBuilder {
489 RayTracingPipelineInfoBuilder {
490 bindless_descriptor_count: Some(self.bindless_descriptor_count),
491 dynamic_stack_size: Some(self.dynamic_stack_size),
492 max_ray_recursion_depth: Some(self.max_ray_recursion_depth),
493 }
494 }
495}
496
497impl Default for RayTracingPipelineInfo {
498 fn default() -> Self {
499 Self {
500 bindless_descriptor_count: 8192,
501 dynamic_stack_size: false,
502 max_ray_recursion_depth: 16,
503 }
504 }
505}
506
507impl From<RayTracingPipelineInfoBuilder> for RayTracingPipelineInfo {
508 fn from(info: RayTracingPipelineInfoBuilder) -> Self {
509 info.build()
510 }
511}
512
513impl RayTracingPipelineInfoBuilder {
514 #[inline(always)]
516 pub fn build(self) -> RayTracingPipelineInfo {
517 self.fallible_build()
518 .expect("invalid ray tracing pipeline info")
519 }
520}
521
522#[derive(Debug)]
523pub(crate) struct RayTracingPipelineInner {
524 pub descriptor_bindings: DescriptorBindingMap,
525 pub descriptor_info: PipelineDescriptorInfo,
526 pub device: Device,
527 pub handle: vk::Pipeline,
528 pub info: RayTracingPipelineInfo,
529 pub layout: vk::PipelineLayout,
530 pub push_constants: Box<[vk::PushConstantRange]>,
531 pub shader_group_handles: Box<[u8]>,
532}
533
534impl Drop for RayTracingPipelineInner {
535 #[profiling::function]
536 fn drop(&mut self) {
537 if panicking() {
538 return;
539 }
540
541 Device::clear_private_data_object_name(&self.device, vk::ObjectType::PIPELINE, self.handle)
542 .unwrap_or_else(|err| warn!("unable to clear private data object name: {err}"));
543
544 unsafe {
545 self.device.destroy_pipeline(self.handle, None);
546 self.device.destroy_pipeline_layout(self.layout, None);
547 }
548 }
549}
550
551#[derive(Clone, Copy, Debug)]
556pub struct RayTracingShaderGroup {
557 pub any_hit_shader: Option<u32>,
561
562 pub closest_hit_shader: Option<u32>,
566
567 pub general_shader: Option<u32>,
570
571 pub intersection_shader: Option<u32>,
574
575 pub shader_group_type: RayTracingShaderGroupType,
577}
578
579impl RayTracingShaderGroup {
580 fn new(
581 shader_group_type: RayTracingShaderGroupType,
582 general_shader: impl Into<Option<u32>>,
583 intersection_shader: impl Into<Option<u32>>,
584 closest_hit_shader: impl Into<Option<u32>>,
585 any_hit_shader: impl Into<Option<u32>>,
586 ) -> Self {
587 let any_hit_shader = any_hit_shader.into();
588 let closest_hit_shader = closest_hit_shader.into();
589 let general_shader = general_shader.into();
590 let intersection_shader = intersection_shader.into();
591
592 Self {
593 any_hit_shader,
594 closest_hit_shader,
595 general_shader,
596 intersection_shader,
597 shader_group_type,
598 }
599 }
600
601 pub fn new_general(general_shader: impl Into<Option<u32>>) -> Self {
603 Self::new(
604 RayTracingShaderGroupType::General,
605 general_shader,
606 None,
607 None,
608 None,
609 )
610 }
611
612 pub fn new_procedural(
615 intersection_shader: u32,
616 closest_hit_shader: impl Into<Option<u32>>,
617 any_hit_shader: impl Into<Option<u32>>,
618 ) -> Self {
619 Self::new(
620 RayTracingShaderGroupType::ProceduralHitGroup,
621 None,
622 intersection_shader,
623 closest_hit_shader,
624 any_hit_shader,
625 )
626 }
627
628 pub fn new_triangles(closest_hit_shader: u32, any_hit_shader: impl Into<Option<u32>>) -> Self {
631 Self::new(
632 RayTracingShaderGroupType::TrianglesHitGroup,
633 None,
634 None,
635 closest_hit_shader,
636 any_hit_shader,
637 )
638 }
639}
640
641impl From<RayTracingShaderGroup> for vk::RayTracingShaderGroupCreateInfoKHR<'static> {
642 fn from(shader_group: RayTracingShaderGroup) -> Self {
643 vk::RayTracingShaderGroupCreateInfoKHR::default()
644 .ty(shader_group.shader_group_type.into())
645 .any_hit_shader(shader_group.any_hit_shader.unwrap_or(vk::SHADER_UNUSED_KHR))
646 .closest_hit_shader(
647 shader_group
648 .closest_hit_shader
649 .unwrap_or(vk::SHADER_UNUSED_KHR),
650 )
651 .general_shader(shader_group.general_shader.unwrap_or(vk::SHADER_UNUSED_KHR))
652 .intersection_shader(
653 shader_group
654 .intersection_shader
655 .unwrap_or(vk::SHADER_UNUSED_KHR),
656 )
657 }
658}
659
660#[derive(Clone, Copy, Debug)]
663pub enum RayTracingShaderGroupType {
664 General,
666
667 ProceduralHitGroup,
669
670 TrianglesHitGroup,
672}
673
674impl From<RayTracingShaderGroupType> for vk::RayTracingShaderGroupTypeKHR {
675 fn from(shader_group_type: RayTracingShaderGroupType) -> Self {
676 match shader_group_type {
677 RayTracingShaderGroupType::General => vk::RayTracingShaderGroupTypeKHR::GENERAL,
678 RayTracingShaderGroupType::ProceduralHitGroup => {
679 vk::RayTracingShaderGroupTypeKHR::PROCEDURAL_HIT_GROUP
680 }
681 RayTracingShaderGroupType::TrianglesHitGroup => {
682 vk::RayTracingShaderGroupTypeKHR::TRIANGLES_HIT_GROUP
683 }
684 }
685 }
686}
687
688#[cfg(test)]
689mod test {
690 use super::*;
691
692 type Info = RayTracingPipelineInfo;
693 type Builder = RayTracingPipelineInfoBuilder;
694
695 #[test]
696 pub fn ray_tracing_pipeline_info() {
697 let info = Info::default();
698 let builder = info.into_builder().build();
699
700 assert_eq!(info, builder);
701 }
702
703 #[test]
704 pub fn ray_tracing_pipeline_info_builder() {
705 let info = Info::default();
706 let builder = Builder::default().build();
707
708 assert_eq!(info, builder);
709 }
710}