/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::{id, RawString}; use std::{borrow::Cow, ffi, slice}; use wgc::{command::{compute_ffi, render_ffi, ComputePassDescriptor, ComputePassTimestampWrites, RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor, RenderPassTimestampWrites}, id::CommandEncoderId}; use wgt::{BufferAddress, BufferSize, Color, DynamicOffset, IndexFormat}; use serde::{Serialize, Deserialize}; use arrayvec::ArrayVec; /// A stream of commands for a render pass or compute pass. /// /// This also contains side tables referred to by certain commands, /// like dynamic offsets for [`SetBindGroup`] or string data for /// [`InsertDebugMarker`]. /// /// Render passes use `BasePass`, whereas compute /// passes use `BasePass`. /// /// [`SetBindGroup`]: RenderCommand::SetBindGroup /// [`InsertDebugMarker`]: RenderCommand::InsertDebugMarker #[doc(hidden)] #[derive(Debug)] #[derive(serde::Serialize, serde::Deserialize)] pub struct BasePass { pub label: Option, /// The stream of commands. pub commands: Vec, /// Dynamic offsets consumed by [`SetBindGroup`] commands in `commands`. /// /// Each successive `SetBindGroup` consumes the next /// [`num_dynamic_offsets`] values from this list. pub dynamic_offsets: Vec, /// Strings used by debug instructions. /// /// Each successive [`PushDebugGroup`] or [`InsertDebugMarker`] /// instruction consumes the next `len` bytes from this vector. pub string_data: Vec, } #[derive(Deserialize, Serialize)] pub struct RecordedRenderPass { base: BasePass, color_attachments: ArrayVec, { wgh::MAX_COLOR_ATTACHMENTS }>, depth_stencil_attachment: Option, timestamp_writes: Option, occlusion_query_set_id: Option, } impl RecordedRenderPass { pub fn new(desc: &RenderPassDescriptor) -> Self { Self { base: BasePass { label: desc.label.as_ref().map(|cow| cow.to_string()), commands: Vec::new(), dynamic_offsets: Vec::new(), string_data: Vec::new(), }, color_attachments: desc.color_attachments.iter().cloned().collect(), depth_stencil_attachment: desc.depth_stencil_attachment.cloned(), timestamp_writes: desc.timestamp_writes.cloned(), occlusion_query_set_id: desc.occlusion_query_set, } } } #[derive(serde::Deserialize, serde::Serialize)] pub struct RecordedComputePass { base: BasePass, timestamp_writes: Option, } impl RecordedComputePass { pub fn new(desc: &ComputePassDescriptor) -> Self { Self { base: BasePass { label: desc.label.as_ref().map(|cow| cow.to_string()), commands: Vec::new(), dynamic_offsets: Vec::new(), string_data: Vec::new(), }, timestamp_writes: desc.timestamp_writes.cloned(), } } } #[doc(hidden)] #[derive(Clone, Copy, Debug)] #[derive(serde::Serialize, serde::Deserialize)] pub enum RenderCommand { SetBindGroup { index: u32, num_dynamic_offsets: usize, bind_group_id: id::BindGroupId, }, SetPipeline(id::RenderPipelineId), SetIndexBuffer { buffer_id: id::BufferId, index_format: wgt::IndexFormat, offset: BufferAddress, size: Option, }, SetVertexBuffer { slot: u32, buffer_id: id::BufferId, offset: BufferAddress, size: Option, }, SetBlendConstant(Color), SetStencilReference(u32), SetViewport { x: f32, y: f32, w: f32, h: f32, depth_min: f32, depth_max: f32, }, SetScissor { x: u32, y: u32, w: u32, h: u32 }, Draw { vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32, }, DrawIndexed { index_count: u32, instance_count: u32, first_index: u32, base_vertex: i32, first_instance: u32, }, MultiDrawIndirect { buffer_id: id::BufferId, offset: BufferAddress, /// Count of `None` represents a non-multi call. count: Option, indexed: bool, }, MultiDrawIndirectCount { buffer_id: id::BufferId, offset: BufferAddress, count_buffer_id: id::BufferId, count_buffer_offset: BufferAddress, max_count: u32, indexed: bool, }, PushDebugGroup { color: u32, len: usize, }, PopDebugGroup, InsertDebugMarker { color: u32, len: usize, }, WriteTimestamp { query_set_id: id::QuerySetId, query_index: u32, }, BeginOcclusionQuery { query_index: u32, }, EndOcclusionQuery, BeginPipelineStatisticsQuery { query_set_id: id::QuerySetId, query_index: u32, }, EndPipelineStatisticsQuery, ExecuteBundle(id::RenderBundleId), } #[doc(hidden)] #[derive(Clone, Copy, Debug)] #[derive(serde::Serialize, serde::Deserialize)] pub enum ComputeCommand { SetBindGroup { index: u32, num_dynamic_offsets: usize, bind_group_id: id::BindGroupId, }, SetPipeline(id::ComputePipelineId), Dispatch([u32; 3]), DispatchIndirect { buffer_id: id::BufferId, offset: wgt::BufferAddress, }, PushDebugGroup { color: u32, len: usize, }, PopDebugGroup, InsertDebugMarker { color: u32, len: usize, }, WriteTimestamp { query_set_id: id::QuerySetId, query_index: u32, }, BeginPipelineStatisticsQuery { query_set_id: id::QuerySetId, query_index: u32, }, EndPipelineStatisticsQuery, } /// # Safety /// /// This function is unsafe as there is no guarantee that the given pointer is /// valid for `offset_length` elements. #[no_mangle] pub unsafe extern "C" fn wgpu_recorded_render_pass_set_bind_group( pass: &mut RecordedRenderPass, index: u32, bind_group_id: id::BindGroupId, offsets: *const DynamicOffset, offset_length: usize, ) { pass.base.dynamic_offsets .extend_from_slice(unsafe { slice::from_raw_parts(offsets, offset_length) }); pass.base.commands.push(RenderCommand::SetBindGroup { index, num_dynamic_offsets: offset_length, bind_group_id, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_set_pipeline( pass: &mut RecordedRenderPass, pipeline_id: id::RenderPipelineId, ) { pass.base .commands .push(RenderCommand::SetPipeline(pipeline_id)); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_set_vertex_buffer( pass: &mut RecordedRenderPass, slot: u32, buffer_id: id::BufferId, offset: BufferAddress, size: Option, ) { pass.base.commands.push(RenderCommand::SetVertexBuffer { slot, buffer_id, offset, size, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_set_index_buffer( pass: &mut RecordedRenderPass, buffer_id: id::BufferId, index_format: IndexFormat, offset: BufferAddress, size: Option, ) { pass.base.commands.push(RenderCommand::SetIndexBuffer { buffer_id, index_format, offset, size, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_set_blend_constant(pass: &mut RecordedRenderPass, color: &Color) { pass.base .commands .push(RenderCommand::SetBlendConstant(*color)); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_set_stencil_reference(pass: &mut RecordedRenderPass, value: u32) { pass.base .commands .push(RenderCommand::SetStencilReference(value)); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_set_viewport( pass: &mut RecordedRenderPass, x: f32, y: f32, w: f32, h: f32, depth_min: f32, depth_max: f32, ) { pass.base.commands.push(RenderCommand::SetViewport { x, y, w, h, depth_min, depth_max, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_set_scissor_rect( pass: &mut RecordedRenderPass, x: u32, y: u32, w: u32, h: u32, ) { pass.base .commands .push(RenderCommand::SetScissor { x, y, w, h }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_draw( pass: &mut RecordedRenderPass, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32, ) { pass.base.commands.push(RenderCommand::Draw { vertex_count, instance_count, first_vertex, first_instance, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_draw_indexed( pass: &mut RecordedRenderPass, index_count: u32, instance_count: u32, first_index: u32, base_vertex: i32, first_instance: u32, ) { pass.base.commands.push(RenderCommand::DrawIndexed { index_count, instance_count, first_index, base_vertex, first_instance, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_draw_indirect( pass: &mut RecordedRenderPass, buffer_id: id::BufferId, offset: BufferAddress, ) { pass.base.commands.push(RenderCommand::MultiDrawIndirect { buffer_id, offset, count: None, indexed: false, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_draw_indexed_indirect( pass: &mut RecordedRenderPass, buffer_id: id::BufferId, offset: BufferAddress, ) { pass.base.commands.push(RenderCommand::MultiDrawIndirect { buffer_id, offset, count: None, indexed: true, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_multi_draw_indirect( pass: &mut RecordedRenderPass, buffer_id: id::BufferId, offset: BufferAddress, count: u32, ) { pass.base.commands.push(RenderCommand::MultiDrawIndirect { buffer_id, offset, count: Some(count), indexed: false, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_multi_draw_indexed_indirect( pass: &mut RecordedRenderPass, buffer_id: id::BufferId, offset: BufferAddress, count: u32, ) { pass.base.commands.push(RenderCommand::MultiDrawIndirect { buffer_id, offset, count: Some(count), indexed: true, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_multi_draw_indirect_count( pass: &mut RecordedRenderPass, buffer_id: id::BufferId, offset: BufferAddress, count_buffer_id: id::BufferId, count_buffer_offset: BufferAddress, max_count: u32, ) { pass.base .commands .push(RenderCommand::MultiDrawIndirectCount { buffer_id, offset, count_buffer_id, count_buffer_offset, max_count, indexed: false, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_multi_draw_indexed_indirect_count( pass: &mut RecordedRenderPass, buffer_id: id::BufferId, offset: BufferAddress, count_buffer_id: id::BufferId, count_buffer_offset: BufferAddress, max_count: u32, ) { pass.base .commands .push(RenderCommand::MultiDrawIndirectCount { buffer_id, offset, count_buffer_id, count_buffer_offset, max_count, indexed: true, }); } /// # Safety /// /// This function is unsafe as there is no guarantee that the given `label` /// is a valid null-terminated string. #[no_mangle] pub unsafe extern "C" fn wgpu_recorded_render_pass_push_debug_group( pass: &mut RecordedRenderPass, label: RawString, color: u32, ) { let bytes = unsafe { ffi::CStr::from_ptr(label) }.to_bytes(); pass.base.string_data.extend_from_slice(bytes); pass.base.commands.push(RenderCommand::PushDebugGroup { color, len: bytes.len(), }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_pop_debug_group(pass: &mut RecordedRenderPass) { pass.base.commands.push(RenderCommand::PopDebugGroup); } /// # Safety /// /// This function is unsafe as there is no guarantee that the given `label` /// is a valid null-terminated string. #[no_mangle] pub unsafe extern "C" fn wgpu_recorded_render_pass_insert_debug_marker( pass: &mut RecordedRenderPass, label: RawString, color: u32, ) { let bytes = unsafe { ffi::CStr::from_ptr(label) }.to_bytes(); pass.base.string_data.extend_from_slice(bytes); pass.base.commands.push(RenderCommand::InsertDebugMarker { color, len: bytes.len(), }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_write_timestamp( pass: &mut RecordedRenderPass, query_set_id: id::QuerySetId, query_index: u32, ) { pass.base.commands.push(RenderCommand::WriteTimestamp { query_set_id, query_index, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_begin_occlusion_query( pass: &mut RecordedRenderPass, query_index: u32, ) { pass.base .commands .push(RenderCommand::BeginOcclusionQuery { query_index }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_end_occlusion_query(pass: &mut RecordedRenderPass) { pass.base.commands.push(RenderCommand::EndOcclusionQuery); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_begin_pipeline_statistics_query( pass: &mut RecordedRenderPass, query_set_id: id::QuerySetId, query_index: u32, ) { pass.base .commands .push(RenderCommand::BeginPipelineStatisticsQuery { query_set_id, query_index, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_render_pass_end_pipeline_statistics_query(pass: &mut RecordedRenderPass) { pass.base .commands .push(RenderCommand::EndPipelineStatisticsQuery); } /// # Safety /// /// This function is unsafe as there is no guarantee that the given pointer is /// valid for `render_bundle_ids_length` elements. #[no_mangle] pub unsafe extern "C" fn wgpu_recorded_render_pass_execute_bundles( pass: &mut RecordedRenderPass, render_bundle_ids: *const id::RenderBundleId, render_bundle_ids_length: usize, ) { for &bundle_id in unsafe { slice::from_raw_parts(render_bundle_ids, render_bundle_ids_length) } { pass.base .commands .push(RenderCommand::ExecuteBundle(bundle_id)); } } /// # Safety /// /// This function is unsafe as there is no guarantee that the given pointer is /// valid for `offset_length` elements. #[no_mangle] pub unsafe extern "C" fn wgpu_recorded_compute_pass_set_bind_group( pass: &mut RecordedComputePass, index: u32, bind_group_id: id::BindGroupId, offsets: *const DynamicOffset, offset_length: usize, ) { pass.base.dynamic_offsets .extend_from_slice(unsafe { slice::from_raw_parts(offsets, offset_length) }); pass.base.commands.push(ComputeCommand::SetBindGroup { index, num_dynamic_offsets: offset_length, bind_group_id, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_compute_pass_set_pipeline( pass: &mut RecordedComputePass, pipeline_id: id::ComputePipelineId, ) { pass.base .commands .push(ComputeCommand::SetPipeline(pipeline_id)); } #[no_mangle] pub extern "C" fn wgpu_recorded_compute_pass_dispatch_workgroups( pass: &mut RecordedComputePass, groups_x: u32, groups_y: u32, groups_z: u32, ) { pass.base .commands .push(ComputeCommand::Dispatch([groups_x, groups_y, groups_z])); } #[no_mangle] pub extern "C" fn wgpu_recorded_compute_pass_dispatch_workgroups_indirect( pass: &mut RecordedComputePass, buffer_id: id::BufferId, offset: BufferAddress, ) { pass.base .commands .push(ComputeCommand::DispatchIndirect { buffer_id, offset }); } /// # Safety /// /// This function is unsafe as there is no guarantee that the given `label` /// is a valid null-terminated string. #[no_mangle] pub unsafe extern "C" fn wgpu_recorded_compute_pass_push_debug_group( pass: &mut RecordedComputePass, label: RawString, color: u32, ) { let bytes = unsafe { ffi::CStr::from_ptr(label) }.to_bytes(); pass.base.string_data.extend_from_slice(bytes); pass.base.commands.push(ComputeCommand::PushDebugGroup { color, len: bytes.len(), }); } #[no_mangle] pub extern "C" fn wgpu_recorded_compute_pass_pop_debug_group(pass: &mut RecordedComputePass) { pass.base.commands.push(ComputeCommand::PopDebugGroup); } /// # Safety /// /// This function is unsafe as there is no guarantee that the given `label` /// is a valid null-terminated string. #[no_mangle] pub unsafe extern "C" fn wgpu_recorded_compute_pass_insert_debug_marker( pass: &mut RecordedComputePass, label: RawString, color: u32, ) { let bytes = unsafe { ffi::CStr::from_ptr(label) }.to_bytes(); pass.base.string_data.extend_from_slice(bytes); pass.base.commands.push(ComputeCommand::InsertDebugMarker { color, len: bytes.len(), }); } #[no_mangle] pub extern "C" fn wgpu_recorded_compute_pass_write_timestamp( pass: &mut RecordedComputePass, query_set_id: id::QuerySetId, query_index: u32, ) { pass.base.commands.push(ComputeCommand::WriteTimestamp { query_set_id, query_index, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_compute_pass_begin_pipeline_statistics_query( pass: &mut RecordedComputePass, query_set_id: id::QuerySetId, query_index: u32, ) { pass.base .commands .push(ComputeCommand::BeginPipelineStatisticsQuery { query_set_id, query_index, }); } #[no_mangle] pub extern "C" fn wgpu_recorded_compute_pass_end_pipeline_statistics_query(pass: &mut RecordedComputePass) { pass.base .commands .push(ComputeCommand::EndPipelineStatisticsQuery); } pub fn replay_render_pass( id: CommandEncoderId, src_pass: &RecordedRenderPass ) -> wgc::command::RenderPass { let mut dst_pass = wgc::command::RenderPass::new( id, &wgc::command::RenderPassDescriptor { label: src_pass.base.label.as_ref().map(|s| s.as_str().into()), color_attachments: Cow::Borrowed(&src_pass.color_attachments), depth_stencil_attachment: src_pass.depth_stencil_attachment.as_ref(), timestamp_writes: src_pass.timestamp_writes.as_ref(), occlusion_query_set: src_pass.occlusion_query_set_id, }, ); let mut dynamic_offsets = src_pass.base.dynamic_offsets.as_slice(); let mut dynamic_offsets = |len| { let offsets; (offsets, dynamic_offsets) = dynamic_offsets.split_at(len); offsets }; let mut strings = src_pass.base.string_data.as_slice(); let mut strings = |len| { let label; (label, strings) = strings.split_at(len); label }; for command in &src_pass.base.commands { match *command { RenderCommand::SetBindGroup { index, num_dynamic_offsets, bind_group_id, } => { let offsets = dynamic_offsets(num_dynamic_offsets); unsafe { render_ffi::wgpu_render_pass_set_bind_group( &mut dst_pass, index, bind_group_id, offsets.as_ptr(), offsets.len(), ); } } RenderCommand::SetPipeline(pipeline_id) => { render_ffi::wgpu_render_pass_set_pipeline(&mut dst_pass, pipeline_id); } RenderCommand::SetIndexBuffer { buffer_id, index_format, offset, size, } => { render_ffi::wgpu_render_pass_set_index_buffer( &mut dst_pass, buffer_id, index_format, offset, size ); } RenderCommand::SetVertexBuffer { slot, buffer_id, offset, size, } => { render_ffi::wgpu_render_pass_set_vertex_buffer( &mut dst_pass, slot, buffer_id, offset, size ) } RenderCommand::SetBlendConstant(ref color) => { render_ffi::wgpu_render_pass_set_blend_constant(&mut dst_pass, color); } RenderCommand::SetStencilReference(value) => { render_ffi::wgpu_render_pass_set_stencil_reference(&mut dst_pass, value); } RenderCommand::SetViewport { x, y, w, h, depth_min, depth_max, } => { render_ffi::wgpu_render_pass_set_viewport( &mut dst_pass, x, y, w, h, depth_min, depth_max, ); } RenderCommand::SetScissor { x, y, w, h } => { render_ffi::wgpu_render_pass_set_scissor_rect( &mut dst_pass, x, y, w, h ); } RenderCommand::Draw { vertex_count, instance_count, first_vertex, first_instance, } => { render_ffi::wgpu_render_pass_draw( &mut dst_pass, vertex_count, instance_count, first_vertex, first_instance, ); } RenderCommand::DrawIndexed { index_count, instance_count, first_index, base_vertex, first_instance, } => { render_ffi::wgpu_render_pass_draw_indexed( &mut dst_pass, index_count, instance_count, first_index, base_vertex, first_instance, ); } RenderCommand::MultiDrawIndirect { buffer_id, offset, count, indexed, } => { match (indexed, count) { (false, Some(count)) => render_ffi::wgpu_render_pass_multi_draw_indirect( &mut dst_pass, buffer_id, offset, count, ), (false, None) => render_ffi::wgpu_render_pass_draw_indirect( &mut dst_pass, buffer_id, offset, ), (true, Some(count)) => render_ffi::wgpu_render_pass_multi_draw_indexed_indirect( &mut dst_pass, buffer_id, offset, count, ), (true, None) => render_ffi::wgpu_render_pass_draw_indexed_indirect( &mut dst_pass, buffer_id, offset, ), }; } RenderCommand::MultiDrawIndirectCount { buffer_id, offset, count_buffer_id, count_buffer_offset, max_count, indexed, } => { if indexed { render_ffi::wgpu_render_pass_multi_draw_indexed_indirect_count( &mut dst_pass, buffer_id, offset, count_buffer_id, count_buffer_offset, max_count, ); } else { render_ffi::wgpu_render_pass_multi_draw_indirect_count( &mut dst_pass, buffer_id, offset, count_buffer_id, count_buffer_offset, max_count, ); } } RenderCommand::PushDebugGroup { color, len } => { let label = strings(len); let label = std::ffi::CString::new(label).unwrap(); unsafe { render_ffi::wgpu_render_pass_push_debug_group( &mut dst_pass, label.as_ptr(), color ); } } RenderCommand::PopDebugGroup => { render_ffi::wgpu_render_pass_pop_debug_group(&mut dst_pass); } RenderCommand::InsertDebugMarker { color, len } => { let label = strings(len); let label = std::ffi::CString::new(label).unwrap(); unsafe { render_ffi::wgpu_render_pass_insert_debug_marker( &mut dst_pass, label.as_ptr(), color, ); } } RenderCommand::WriteTimestamp { query_set_id, query_index, } => { render_ffi::wgpu_render_pass_write_timestamp( &mut dst_pass, query_set_id, query_index, ); } RenderCommand::BeginOcclusionQuery { query_index } => { render_ffi::wgpu_render_pass_begin_occlusion_query( &mut dst_pass, query_index ); } RenderCommand::EndOcclusionQuery => { render_ffi::wgpu_render_pass_end_occlusion_query(&mut dst_pass); } RenderCommand::BeginPipelineStatisticsQuery { query_set_id, query_index, } => { render_ffi::wgpu_render_pass_begin_pipeline_statistics_query( &mut dst_pass, query_set_id, query_index, ); } RenderCommand::EndPipelineStatisticsQuery => { render_ffi::wgpu_render_pass_end_pipeline_statistics_query(&mut dst_pass); } RenderCommand::ExecuteBundle(bundle_id) => { unsafe { render_ffi::wgpu_render_pass_execute_bundles( &mut dst_pass, &bundle_id, 1, ); } } } } dst_pass } pub fn replay_compute_pass( id: CommandEncoderId, src_pass: &RecordedComputePass ) -> wgc::command::ComputePass { let mut dst_pass = wgc::command::ComputePass::new( id, &wgc::command::ComputePassDescriptor { label: src_pass.base.label.as_ref().map(|s| s.as_str().into()), timestamp_writes: src_pass.timestamp_writes.as_ref(), }, ); let mut dynamic_offsets = src_pass.base.dynamic_offsets.as_slice(); let mut dynamic_offsets = |len| { let offsets; (offsets, dynamic_offsets) = dynamic_offsets.split_at(len); offsets }; let mut strings = src_pass.base.string_data.as_slice(); let mut strings = |len| { let label; (label, strings) = strings.split_at(len); label }; for command in &src_pass.base.commands { match *command { ComputeCommand::SetBindGroup { index, num_dynamic_offsets, bind_group_id, } => { let offsets = dynamic_offsets(num_dynamic_offsets); unsafe { compute_ffi::wgpu_compute_pass_set_bind_group( &mut dst_pass, index, bind_group_id, offsets.as_ptr(), offsets.len() ); } } ComputeCommand::SetPipeline(pipeline_id) => { compute_ffi::wgpu_compute_pass_set_pipeline(&mut dst_pass, pipeline_id) } ComputeCommand::Dispatch([x, y, z]) => { compute_ffi::wgpu_compute_pass_dispatch_workgroups(&mut dst_pass, x, y, z); } ComputeCommand::DispatchIndirect { buffer_id, offset } => { compute_ffi::wgpu_compute_pass_dispatch_workgroups_indirect( &mut dst_pass, buffer_id, offset, ); } ComputeCommand::PushDebugGroup { color, len } => { let label = strings(len); let label = std::ffi::CString::new(label).unwrap(); unsafe { compute_ffi::wgpu_compute_pass_push_debug_group( &mut dst_pass, label.as_ptr(), color ); } } ComputeCommand::PopDebugGroup => { compute_ffi::wgpu_compute_pass_pop_debug_group(&mut dst_pass); } ComputeCommand::InsertDebugMarker { color, len } => { let label = strings(len); let label = std::ffi::CString::new(label).unwrap(); unsafe { compute_ffi::wgpu_compute_pass_insert_debug_marker( &mut dst_pass, label.as_ptr(), color, ); } } ComputeCommand::WriteTimestamp { query_set_id, query_index, } => { compute_ffi::wgpu_compute_pass_write_timestamp( &mut dst_pass, query_set_id, query_index, ); } ComputeCommand::BeginPipelineStatisticsQuery { query_set_id, query_index, } => { compute_ffi::wgpu_compute_pass_begin_pipeline_statistics_query( &mut dst_pass, query_set_id, query_index, ); } ComputeCommand::EndPipelineStatisticsQuery => { compute_ffi::wgpu_compute_pass_end_pipeline_statistics_query(&mut dst_pass); } } } dst_pass }