Skip to main content

vk_graph/cmd/
ray_tracing.rs

1use {
2    super::{PipelineCommand, cmd_ref::CommandRef},
3    crate::driver::{device::Device, ray_tracing::RayTracingPipeline},
4    ash::vk,
5    std::ops::Deref,
6};
7
8impl PipelineCommand<'_, RayTracingPipeline> {
9    /// Begin recording ray tracing pipeline work for this graph command.
10    pub fn record_cmd(
11        mut self,
12        func: impl FnOnce(RayTracingCommandRef<'_>) + Send + 'static,
13    ) -> Self {
14        self.record_cmd_mut(func);
15        self
16    }
17
18    /// Mutable-borrow form of [`Self::record_cmd`].
19    pub fn record_cmd_mut(&mut self, func: impl FnOnce(RayTracingCommandRef<'_>) + Send + 'static) {
20        let pipeline = self
21            .cmd
22            .cmd()
23            .expect_last_pipeline()
24            .expect_ray_tracing()
25            .clone();
26
27        self.cmd.push_exec(move |cmd| {
28            func(RayTracingCommandRef { cmd, pipeline });
29        });
30    }
31
32    pub(crate) fn record_stream_mut(
33        &mut self,
34        func: impl for<'r> Fn(RayTracingCommandRef<'r>) + Send + Sync + 'static,
35    ) {
36        let pipeline = self
37            .cmd
38            .cmd()
39            .expect_last_pipeline()
40            .expect_ray_tracing()
41            .clone();
42
43        self.cmd.push_reusable_exec(move |cmd| {
44            func(RayTracingCommandRef {
45                cmd,
46                pipeline: pipeline.clone(),
47            });
48        });
49    }
50}
51
52/// Recording interface for ray tracing commands.
53///
54/// This structure provides a strongly-typed set of methods which allow ray tracing shader code to
55/// be executed. An instance is provided to the closure argument of [`PipelineCommand::record_cmd`]
56/// which may be accessed by binding a [`RayTracingPipeline`] to a command.
57///
58/// # Examples
59///
60/// Basic usage:
61///
62/// ```no_run
63/// # use ash::vk;
64/// # use vk_graph::driver::DriverError;
65/// # use vk_graph::driver::device::{Device, DeviceInfo};
66/// # use vk_graph::driver::ray_tracing::{
67/// #     RayTracingPipeline,
68/// #     RayTracingPipelineInfo,
69/// #     RayTracingShaderGroup,
70/// # };
71/// # use vk_graph::driver::shader::Shader;
72/// # use vk_graph::Graph;
73/// # fn main() -> Result<(), DriverError> {
74/// # let device = Device::create(DeviceInfo::default())?;
75/// # let info = RayTracingPipelineInfo::default();
76/// # let my_miss_code = [0u8; 1];
77/// # let my_ray_tracing_pipeline = RayTracingPipeline::create(&device, info,
78/// #     [Shader::new_miss(my_miss_code.as_slice())],
79/// #     [RayTracingShaderGroup::new_general(0)],
80/// # )?;
81/// # let mut my_graph = Graph::default();
82/// my_graph.begin_cmd()
83///         .debug_name("my ray tracing command")
84///         .bind_pipeline(&my_ray_tracing_pipeline)
85///         .record_cmd(move |cmd| {
86///             // During this closure we have access to the ray tracing functions!
87///         });
88/// # Ok(()) }
89/// ```
90pub struct RayTracingCommandRef<'a> {
91    cmd: CommandRef<'a>,
92    pipeline: RayTracingPipeline,
93}
94
95impl RayTracingCommandRef<'_> {
96    /// Updates push constants.
97    ///
98    /// Push constants represent a high-speed path to modify constant data in pipelines that is
99    /// expected to outperform memory-backed resource updates.
100    ///
101    /// Push constant values can be updated incrementally, causing shader stages to read the new
102    /// data for push constants modified by this command, while still reading the previous data for
103    /// push constants not modified by this command.
104    ///
105    /// # Device limitations
106    ///
107    /// See [`VkPhysicalDeviceLimits::maxPushConstantsSize`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkPhysicalDeviceLimits.html)
108    /// for the limit of the current device. You may also check [gpuinfo.org] for a listing of
109    /// reported limits on other devices.
110    ///
111    /// # Examples
112    ///
113    /// Basic usage:
114    ///
115    /// ```
116    /// # vk_shader_macros::glsl!(target: vulkan1_2, r#"
117    /// #version 460
118    /// #pragma shader_stage(closest)
119    ///
120    /// layout(push_constant) uniform PushConstants {
121    ///     layout(offset = 0) uint some_val;
122    /// } push_constants;
123    ///
124    /// void main() {
125    ///     uint value = push_constants.some_val;
126    /// }
127    /// # "#);
128    /// ```
129    ///
130    /// ```no_run
131    /// # use ash::vk;
132    /// # use vk_graph::driver::DriverError;
133    /// # use vk_graph::driver::device::{Device, DeviceInfo};
134    /// # use vk_graph::driver::buffer::{Buffer, BufferInfo};
135    /// # use vk_graph::driver::ray_tracing::{
136    /// #     RayTracingPipeline,
137    /// #     RayTracingPipelineInfo,
138    /// #     RayTracingShaderGroup,
139    /// # };
140    /// # use vk_graph::driver::shader::Shader;
141    /// # use vk_graph::Graph;
142    /// # fn main() -> Result<(), DriverError> {
143    /// # let device = Device::create(DeviceInfo::default())?;
144    /// # let shader = [0u8; 1];
145    /// # let info = RayTracingPipelineInfo::default();
146    /// # let my_miss_code = [0u8; 1];
147    /// # let my_ray_tracing_pipeline = RayTracingPipeline::create(&device, info,
148    /// #     [Shader::new_miss(my_miss_code.as_slice())],
149    /// #     [RayTracingShaderGroup::new_general(0)],
150    /// # )?;
151    /// # let rgen_sbt = vk::StridedDeviceAddressRegionKHR {
152    /// #     device_address: 0,
153    /// #     stride: 0,
154    /// #     size: 0,
155    /// # };
156    /// # let hit_sbt = vk::StridedDeviceAddressRegionKHR {
157    /// #     device_address: 0,
158    /// #     stride: 0,
159    /// #     size: 0,
160    /// # };
161    /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR {
162    /// #     device_address: 0,
163    /// #     stride: 0,
164    /// #     size: 0,
165    /// # };
166    /// # let call_sbt = vk::StridedDeviceAddressRegionKHR {
167    /// #     device_address: 0,
168    /// #     stride: 0,
169    /// #     size: 0,
170    /// # };
171    /// # let mut my_graph = Graph::default();
172    /// my_graph.begin_cmd()
173    ///         .debug_name("draw a cornell box")
174    ///         .bind_pipeline(&my_ray_tracing_pipeline)
175    ///         .record_cmd(move |cmd| {
176    ///             cmd.push_constants(0, &[0xcb])
177    ///                    .trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1);
178    ///         });
179    /// # Ok(()) }
180    /// ```
181    ///
182    /// See [`vkCmdPushConstants`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdPushConstants.html).
183    #[profiling::function]
184    pub fn push_constants(&self, offset: u32, data: &[u8]) -> &Self {
185        self.cmd_push_constants(
186            self.pipeline.inner.layout,
187            &self.pipeline.inner.push_constants,
188            offset,
189            data,
190        );
191
192        self
193    }
194
195    /// Sets the stack size dynamically for a ray tracing pipeline.
196    ///
197    /// The pipeline must have been created with
198    /// [`RayTracingPipelineInfo::dynamic_stack_size`](crate::driver::ray_tracing::RayTracingPipelineInfo::dynamic_stack_size)
199    /// enabled.
200    ///
201    /// See [`vkCmdSetRayTracingPipelineStackSizeKHR`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdSetRayTracingPipelineStackSizeKHR.html).
202    #[profiling::function]
203    pub fn set_stack_size(&self, pipeline_stack_size: u32) -> &Self {
204        let khr_ray_tracing_pipeline = Device::expect_vk_khr_ray_tracing_pipeline(&self.cmd.device);
205
206        #[cfg(feature = "checked")]
207        assert!(
208            self.pipeline.inner.info.dynamic_stack_size,
209            "ray tracing pipeline was not created with dynamic_stack_size enabled"
210        );
211
212        unsafe {
213            /*
214            Checked mode catches missing dynamic_stack_size enablement early. Other Vulkan
215            validation remains the responsibility of the validation layer.
216            */
217            khr_ray_tracing_pipeline
218                .cmd_set_ray_tracing_pipeline_stack_size(self.cmd.handle, pipeline_stack_size);
219        }
220
221        self
222    }
223
224    /*
225    TODO: If the rayTraversalPrimitiveCulling or rayQuery features are enabled, the SkipTrianglesKHR
226    and SkipAABBsKHR ray flags can be specified when tracing a ray. SkipTrianglesKHR and
227    SkipAABBsKHR are mutually exclusive.
228    */
229
230    /// Ray traces using the currently-bound [`RayTracingPipeline`] and the given shader binding
231    /// tables.
232    ///
233    /// Shader binding tables must be constructed according to this [example].
234    ///
235    /// See [`vkCmdTraceRaysKHR`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysKHR.html).
236    ///
237    /// # Examples
238    ///
239    /// Basic usage:
240    ///
241    /// ```no_run
242    /// # use ash::vk;
243    /// # use vk_graph::driver::DriverError;
244    /// # use vk_graph::driver::device::{Device, DeviceInfo};
245    /// # use vk_graph::driver::buffer::{Buffer, BufferInfo};
246    /// # use vk_graph::driver::ray_tracing::{
247    /// #     RayTracingPipeline,
248    /// #     RayTracingPipelineInfo,
249    /// #     RayTracingShaderGroup,
250    /// # };
251    /// # use vk_graph::driver::shader::Shader;
252    /// # use vk_graph::Graph;
253    /// # fn main() -> Result<(), DriverError> {
254    /// # let device = Device::create(DeviceInfo::default())?;
255    /// # let shader = [0u8; 1];
256    /// # let info = RayTracingPipelineInfo::default();
257    /// # let my_miss_code = [0u8; 1];
258    /// # let my_ray_tracing_pipeline = RayTracingPipeline::create(&device, info,
259    /// #     [Shader::new_miss(my_miss_code.as_slice())],
260    /// #     [RayTracingShaderGroup::new_general(0)],
261    /// # )?;
262    /// # let rgen_sbt = vk::StridedDeviceAddressRegionKHR {
263    /// #     device_address: 0,
264    /// #     stride: 0,
265    /// #     size: 0,
266    /// # };
267    /// # let hit_sbt = vk::StridedDeviceAddressRegionKHR {
268    /// #     device_address: 0,
269    /// #     stride: 0,
270    /// #     size: 0,
271    /// # };
272    /// # let miss_sbt = vk::StridedDeviceAddressRegionKHR {
273    /// #     device_address: 0,
274    /// #     stride: 0,
275    /// #     size: 0,
276    /// # };
277    /// # let call_sbt = vk::StridedDeviceAddressRegionKHR {
278    /// #     device_address: 0,
279    /// #     stride: 0,
280    /// #     size: 0,
281    /// # };
282    /// # let mut my_graph = Graph::default();
283    /// my_graph.begin_cmd()
284    ///         .debug_name("draw a cornell box")
285    ///         .bind_pipeline(&my_ray_tracing_pipeline)
286    ///         .record_cmd(move |cmd| {
287    ///             cmd.trace_rays(&rgen_sbt, &hit_sbt, &miss_sbt, &call_sbt, 320, 200, 1);
288    ///         });
289    /// # Ok(()) }
290    /// ```
291    ///
292    /// [example]: https://github.com/attackgoat/vk-graph/blob/master/examples/ray_tracing.rs
293    #[allow(clippy::too_many_arguments)]
294    #[profiling::function]
295    pub fn trace_rays(
296        &self,
297        raygen_shader_binding_table: &vk::StridedDeviceAddressRegionKHR,
298        miss_shader_binding_table: &vk::StridedDeviceAddressRegionKHR,
299        hit_shader_binding_table: &vk::StridedDeviceAddressRegionKHR,
300        callable_shader_binding_table: &vk::StridedDeviceAddressRegionKHR,
301        width: u32,
302        height: u32,
303        depth: u32,
304    ) -> &Self {
305        let khr_ray_tracing_pipeline = Device::expect_vk_khr_ray_tracing_pipeline(&self.cmd.device);
306
307        unsafe {
308            khr_ray_tracing_pipeline.cmd_trace_rays(
309                self.cmd.handle,
310                raygen_shader_binding_table,
311                miss_shader_binding_table,
312                hit_shader_binding_table,
313                callable_shader_binding_table,
314                width,
315                height,
316                depth,
317            );
318        }
319
320        self
321    }
322
323    /// Ray traces using the currently-bound [`RayTracingPipeline`] and the given shader binding
324    /// tables.
325    ///
326    /// `indirect_device_address` is a [buffer device address] which is a pointer to a
327    /// [`vk::TraceRaysIndirectCommandKHR`] structure containing the trace ray parameters.
328    ///
329    /// See [`vkCmdTraceRaysIndirectKHR`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdTraceRaysIndirectKHR.html).
330    ///
331    /// [buffer device address]: crate::driver::buffer::Buffer::device_address
332    #[profiling::function]
333    pub fn trace_rays_indirect(
334        &self,
335        raygen_shader_binding_table: &vk::StridedDeviceAddressRegionKHR,
336        miss_shader_binding_table: &vk::StridedDeviceAddressRegionKHR,
337        hit_shader_binding_table: &vk::StridedDeviceAddressRegionKHR,
338        callable_shader_binding_table: &vk::StridedDeviceAddressRegionKHR,
339        indirect_device_address: vk::DeviceAddress,
340    ) -> &Self {
341        let khr_ray_tracing_pipeline = Device::expect_vk_khr_ray_tracing_pipeline(&self.cmd.device);
342
343        unsafe {
344            khr_ray_tracing_pipeline.cmd_trace_rays_indirect(
345                self.cmd.handle,
346                raygen_shader_binding_table,
347                miss_shader_binding_table,
348                hit_shader_binding_table,
349                callable_shader_binding_table,
350                indirect_device_address,
351            )
352        }
353
354        self
355    }
356}
357
358impl<'a> Deref for RayTracingCommandRef<'a> {
359    type Target = CommandRef<'a>;
360
361    fn deref(&self) -> &Self::Target {
362        &self.cmd
363    }
364}