command.rs (30330B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 use crate::{id, server::Global, FfiSlice, RawString}; 6 use std::{borrow::Cow, ffi}; 7 use wgc::{ 8 command::{ 9 ComputePassDescriptor, PassTimestampWrites, RenderPassColorAttachment, 10 RenderPassDepthStencilAttachment, 11 }, 12 id::{CommandEncoderId, TextureViewId}, 13 }; 14 use wgt::{BufferAddress, BufferSize, Color, DynamicOffset, IndexFormat}; 15 16 use serde::{Deserialize, Serialize}; 17 18 /// A stream of commands for a render pass or compute pass. 19 /// 20 /// This also contains side tables referred to by certain commands, 21 /// like dynamic offsets for [`SetBindGroup`] or string data for 22 /// [`InsertDebugMarker`]. 23 /// 24 /// Render passes use `Pass<RenderCommand>`, whereas compute 25 /// passes use `Pass<ComputeCommand>`. 26 /// 27 /// [`SetBindGroup`]: RenderCommand::SetBindGroup 28 /// [`InsertDebugMarker`]: RenderCommand::InsertDebugMarker 29 #[doc(hidden)] 30 #[derive(Debug, serde::Serialize, serde::Deserialize)] 31 pub struct Pass<C> { 32 pub label: Option<String>, 33 34 /// The stream of commands. 35 pub commands: Vec<C>, 36 37 /// Dynamic offsets consumed by [`SetBindGroup`] commands in `commands`. 38 /// 39 /// Each successive `SetBindGroup` consumes the next 40 /// [`num_dynamic_offsets`] values from this list. 41 pub dynamic_offsets: Vec<wgt::DynamicOffset>, 42 43 /// Strings used by debug instructions. 44 /// 45 /// Each successive [`PushDebugGroup`] or [`InsertDebugMarker`] 46 /// instruction consumes the next `len` bytes from this vector. 47 pub string_data: Vec<u8>, 48 } 49 50 #[derive(Deserialize, Serialize)] 51 pub struct RecordedRenderPass { 52 base: Pass<RenderCommand>, 53 color_attachments: Vec<Option<RenderPassColorAttachment>>, 54 depth_stencil_attachment: Option<RenderPassDepthStencilAttachment<TextureViewId>>, 55 timestamp_writes: Option<PassTimestampWrites>, 56 occlusion_query_set_id: Option<id::QuerySetId>, 57 } 58 59 impl RecordedRenderPass { 60 pub fn new( 61 label: Option<String>, 62 color_attachments: Vec<Option<RenderPassColorAttachment>>, 63 depth_stencil_attachment: Option<RenderPassDepthStencilAttachment<TextureViewId>>, 64 timestamp_writes: Option<PassTimestampWrites>, 65 occlusion_query_set_id: Option<id::QuerySetId>, 66 ) -> Self { 67 Self { 68 base: Pass { 69 label, 70 commands: Vec::new(), 71 dynamic_offsets: Vec::new(), 72 string_data: Vec::new(), 73 }, 74 color_attachments, 75 depth_stencil_attachment, 76 timestamp_writes, 77 occlusion_query_set_id, 78 } 79 } 80 } 81 82 #[derive(serde::Deserialize, serde::Serialize)] 83 pub struct RecordedComputePass { 84 base: Pass<ComputeCommand>, 85 timestamp_writes: Option<PassTimestampWrites>, 86 } 87 88 impl RecordedComputePass { 89 pub fn new(desc: &ComputePassDescriptor) -> Self { 90 Self { 91 base: Pass { 92 label: desc.label.as_ref().map(|cow| cow.to_string()), 93 commands: Vec::new(), 94 dynamic_offsets: Vec::new(), 95 string_data: Vec::new(), 96 }, 97 timestamp_writes: desc.timestamp_writes.clone(), 98 } 99 } 100 } 101 102 #[doc(hidden)] 103 #[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)] 104 pub enum RenderCommand { 105 SetBindGroup { 106 index: u32, 107 num_dynamic_offsets: usize, 108 bind_group_id: Option<id::BindGroupId>, 109 }, 110 SetPipeline(id::RenderPipelineId), 111 SetIndexBuffer { 112 buffer_id: id::BufferId, 113 index_format: wgt::IndexFormat, 114 offset: BufferAddress, 115 size: Option<BufferSize>, 116 }, 117 SetVertexBuffer { 118 slot: u32, 119 buffer_id: id::BufferId, 120 offset: BufferAddress, 121 size: Option<BufferSize>, 122 }, 123 SetBlendConstant(Color), 124 SetStencilReference(u32), 125 SetViewport { 126 x: f32, 127 y: f32, 128 w: f32, 129 h: f32, 130 depth_min: f32, 131 depth_max: f32, 132 }, 133 SetScissor { 134 x: u32, 135 y: u32, 136 w: u32, 137 h: u32, 138 }, 139 Draw { 140 vertex_count: u32, 141 instance_count: u32, 142 first_vertex: u32, 143 first_instance: u32, 144 }, 145 DrawIndexed { 146 index_count: u32, 147 instance_count: u32, 148 first_index: u32, 149 base_vertex: i32, 150 first_instance: u32, 151 }, 152 MultiDrawIndirect { 153 buffer_id: id::BufferId, 154 offset: BufferAddress, 155 /// Count of `None` represents a non-multi call. 156 count: Option<u32>, 157 indexed: bool, 158 }, 159 MultiDrawIndirectCount { 160 buffer_id: id::BufferId, 161 offset: BufferAddress, 162 count_buffer_id: id::BufferId, 163 count_buffer_offset: BufferAddress, 164 max_count: u32, 165 indexed: bool, 166 }, 167 PushDebugGroup { 168 color: u32, 169 len: usize, 170 }, 171 PopDebugGroup, 172 InsertDebugMarker { 173 color: u32, 174 len: usize, 175 }, 176 WriteTimestamp { 177 query_set_id: id::QuerySetId, 178 query_index: u32, 179 }, 180 BeginOcclusionQuery { 181 query_index: u32, 182 }, 183 EndOcclusionQuery, 184 BeginPipelineStatisticsQuery { 185 query_set_id: id::QuerySetId, 186 query_index: u32, 187 }, 188 EndPipelineStatisticsQuery, 189 ExecuteBundle(id::RenderBundleId), 190 } 191 192 #[doc(hidden)] 193 #[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)] 194 pub enum ComputeCommand { 195 SetBindGroup { 196 index: u32, 197 num_dynamic_offsets: usize, 198 bind_group_id: Option<id::BindGroupId>, 199 }, 200 SetPipeline(id::ComputePipelineId), 201 Dispatch([u32; 3]), 202 DispatchIndirect { 203 buffer_id: id::BufferId, 204 offset: wgt::BufferAddress, 205 }, 206 PushDebugGroup { 207 color: u32, 208 len: usize, 209 }, 210 PopDebugGroup, 211 InsertDebugMarker { 212 color: u32, 213 len: usize, 214 }, 215 WriteTimestamp { 216 query_set_id: id::QuerySetId, 217 query_index: u32, 218 }, 219 BeginPipelineStatisticsQuery { 220 query_set_id: id::QuerySetId, 221 query_index: u32, 222 }, 223 EndPipelineStatisticsQuery, 224 } 225 226 #[no_mangle] 227 pub unsafe extern "C" fn wgpu_recorded_render_pass_set_bind_group( 228 pass: &mut RecordedRenderPass, 229 index: u32, 230 bind_group_id: Option<id::BindGroupId>, 231 offsets: FfiSlice<'_, DynamicOffset>, 232 ) { 233 let offsets = offsets.as_slice(); 234 pass.base.dynamic_offsets.extend_from_slice(offsets); 235 236 pass.base.commands.push(RenderCommand::SetBindGroup { 237 index, 238 num_dynamic_offsets: offsets.len(), 239 bind_group_id, 240 }); 241 } 242 243 #[no_mangle] 244 pub extern "C" fn wgpu_recorded_render_pass_set_pipeline( 245 pass: &mut RecordedRenderPass, 246 pipeline_id: id::RenderPipelineId, 247 ) { 248 pass.base 249 .commands 250 .push(RenderCommand::SetPipeline(pipeline_id)); 251 } 252 253 #[no_mangle] 254 pub extern "C" fn wgpu_recorded_render_pass_set_vertex_buffer( 255 pass: &mut RecordedRenderPass, 256 slot: u32, 257 buffer_id: id::BufferId, 258 offset: BufferAddress, 259 size: Option<&BufferSize>, 260 ) { 261 pass.base.commands.push(RenderCommand::SetVertexBuffer { 262 slot, 263 buffer_id, 264 offset, 265 size: size.copied(), 266 }); 267 } 268 269 #[no_mangle] 270 pub extern "C" fn wgpu_recorded_render_pass_set_index_buffer( 271 pass: &mut RecordedRenderPass, 272 buffer_id: id::BufferId, 273 index_format: IndexFormat, 274 offset: BufferAddress, 275 size: Option<&BufferSize>, 276 ) { 277 pass.base.commands.push(RenderCommand::SetIndexBuffer { 278 buffer_id, 279 index_format, 280 offset, 281 size: size.copied(), 282 }); 283 } 284 285 #[no_mangle] 286 pub extern "C" fn wgpu_recorded_render_pass_set_blend_constant( 287 pass: &mut RecordedRenderPass, 288 color: &Color, 289 ) { 290 pass.base 291 .commands 292 .push(RenderCommand::SetBlendConstant(*color)); 293 } 294 295 #[no_mangle] 296 pub extern "C" fn wgpu_recorded_render_pass_set_stencil_reference( 297 pass: &mut RecordedRenderPass, 298 value: u32, 299 ) { 300 pass.base 301 .commands 302 .push(RenderCommand::SetStencilReference(value)); 303 } 304 305 #[no_mangle] 306 pub extern "C" fn wgpu_recorded_render_pass_set_viewport( 307 pass: &mut RecordedRenderPass, 308 x: f32, 309 y: f32, 310 w: f32, 311 h: f32, 312 depth_min: f32, 313 depth_max: f32, 314 ) { 315 pass.base.commands.push(RenderCommand::SetViewport { 316 x, 317 y, 318 w, 319 h, 320 depth_min, 321 depth_max, 322 }); 323 } 324 325 #[no_mangle] 326 pub extern "C" fn wgpu_recorded_render_pass_set_scissor_rect( 327 pass: &mut RecordedRenderPass, 328 x: u32, 329 y: u32, 330 w: u32, 331 h: u32, 332 ) { 333 pass.base 334 .commands 335 .push(RenderCommand::SetScissor { x, y, w, h }); 336 } 337 338 #[no_mangle] 339 pub extern "C" fn wgpu_recorded_render_pass_draw( 340 pass: &mut RecordedRenderPass, 341 vertex_count: u32, 342 instance_count: u32, 343 first_vertex: u32, 344 first_instance: u32, 345 ) { 346 pass.base.commands.push(RenderCommand::Draw { 347 vertex_count, 348 instance_count, 349 first_vertex, 350 first_instance, 351 }); 352 } 353 354 #[no_mangle] 355 pub extern "C" fn wgpu_recorded_render_pass_draw_indexed( 356 pass: &mut RecordedRenderPass, 357 index_count: u32, 358 instance_count: u32, 359 first_index: u32, 360 base_vertex: i32, 361 first_instance: u32, 362 ) { 363 pass.base.commands.push(RenderCommand::DrawIndexed { 364 index_count, 365 instance_count, 366 first_index, 367 base_vertex, 368 first_instance, 369 }); 370 } 371 372 #[no_mangle] 373 pub extern "C" fn wgpu_recorded_render_pass_draw_indirect( 374 pass: &mut RecordedRenderPass, 375 buffer_id: id::BufferId, 376 offset: BufferAddress, 377 ) { 378 pass.base.commands.push(RenderCommand::MultiDrawIndirect { 379 buffer_id, 380 offset, 381 count: None, 382 indexed: false, 383 }); 384 } 385 386 #[no_mangle] 387 pub extern "C" fn wgpu_recorded_render_pass_draw_indexed_indirect( 388 pass: &mut RecordedRenderPass, 389 buffer_id: id::BufferId, 390 offset: BufferAddress, 391 ) { 392 pass.base.commands.push(RenderCommand::MultiDrawIndirect { 393 buffer_id, 394 offset, 395 count: None, 396 indexed: true, 397 }); 398 } 399 400 #[no_mangle] 401 pub extern "C" fn wgpu_recorded_render_pass_multi_draw_indirect( 402 pass: &mut RecordedRenderPass, 403 buffer_id: id::BufferId, 404 offset: BufferAddress, 405 count: u32, 406 ) { 407 pass.base.commands.push(RenderCommand::MultiDrawIndirect { 408 buffer_id, 409 offset, 410 count: Some(count), 411 indexed: false, 412 }); 413 } 414 415 #[no_mangle] 416 pub extern "C" fn wgpu_recorded_render_pass_multi_draw_indexed_indirect( 417 pass: &mut RecordedRenderPass, 418 buffer_id: id::BufferId, 419 offset: BufferAddress, 420 count: u32, 421 ) { 422 pass.base.commands.push(RenderCommand::MultiDrawIndirect { 423 buffer_id, 424 offset, 425 count: Some(count), 426 indexed: true, 427 }); 428 } 429 430 #[no_mangle] 431 pub extern "C" fn wgpu_recorded_render_pass_multi_draw_indirect_count( 432 pass: &mut RecordedRenderPass, 433 buffer_id: id::BufferId, 434 offset: BufferAddress, 435 count_buffer_id: id::BufferId, 436 count_buffer_offset: BufferAddress, 437 max_count: u32, 438 ) { 439 pass.base 440 .commands 441 .push(RenderCommand::MultiDrawIndirectCount { 442 buffer_id, 443 offset, 444 count_buffer_id, 445 count_buffer_offset, 446 max_count, 447 indexed: false, 448 }); 449 } 450 451 #[no_mangle] 452 pub extern "C" fn wgpu_recorded_render_pass_multi_draw_indexed_indirect_count( 453 pass: &mut RecordedRenderPass, 454 buffer_id: id::BufferId, 455 offset: BufferAddress, 456 count_buffer_id: id::BufferId, 457 count_buffer_offset: BufferAddress, 458 max_count: u32, 459 ) { 460 pass.base 461 .commands 462 .push(RenderCommand::MultiDrawIndirectCount { 463 buffer_id, 464 offset, 465 count_buffer_id, 466 count_buffer_offset, 467 max_count, 468 indexed: true, 469 }); 470 } 471 472 /// # Safety 473 /// 474 /// This function is unsafe as there is no guarantee that the given `label` 475 /// is a valid null-terminated string. 476 #[no_mangle] 477 pub unsafe extern "C" fn wgpu_recorded_render_pass_push_debug_group( 478 pass: &mut RecordedRenderPass, 479 label: RawString, 480 color: u32, 481 ) { 482 let bytes = unsafe { ffi::CStr::from_ptr(label) }.to_bytes(); 483 pass.base.string_data.extend_from_slice(bytes); 484 485 pass.base.commands.push(RenderCommand::PushDebugGroup { 486 color, 487 len: bytes.len(), 488 }); 489 } 490 491 #[no_mangle] 492 pub extern "C" fn wgpu_recorded_render_pass_pop_debug_group(pass: &mut RecordedRenderPass) { 493 pass.base.commands.push(RenderCommand::PopDebugGroup); 494 } 495 496 /// # Safety 497 /// 498 /// This function is unsafe as there is no guarantee that the given `label` 499 /// is a valid null-terminated string. 500 #[no_mangle] 501 pub unsafe extern "C" fn wgpu_recorded_render_pass_insert_debug_marker( 502 pass: &mut RecordedRenderPass, 503 label: RawString, 504 color: u32, 505 ) { 506 let bytes = unsafe { ffi::CStr::from_ptr(label) }.to_bytes(); 507 pass.base.string_data.extend_from_slice(bytes); 508 509 pass.base.commands.push(RenderCommand::InsertDebugMarker { 510 color, 511 len: bytes.len(), 512 }); 513 } 514 515 #[no_mangle] 516 pub extern "C" fn wgpu_recorded_render_pass_write_timestamp( 517 pass: &mut RecordedRenderPass, 518 query_set_id: id::QuerySetId, 519 query_index: u32, 520 ) { 521 pass.base.commands.push(RenderCommand::WriteTimestamp { 522 query_set_id, 523 query_index, 524 }); 525 } 526 527 #[no_mangle] 528 pub extern "C" fn wgpu_recorded_render_pass_begin_occlusion_query( 529 pass: &mut RecordedRenderPass, 530 query_index: u32, 531 ) { 532 pass.base 533 .commands 534 .push(RenderCommand::BeginOcclusionQuery { query_index }); 535 } 536 537 #[no_mangle] 538 pub extern "C" fn wgpu_recorded_render_pass_end_occlusion_query(pass: &mut RecordedRenderPass) { 539 pass.base.commands.push(RenderCommand::EndOcclusionQuery); 540 } 541 542 #[no_mangle] 543 pub extern "C" fn wgpu_recorded_render_pass_begin_pipeline_statistics_query( 544 pass: &mut RecordedRenderPass, 545 query_set_id: id::QuerySetId, 546 query_index: u32, 547 ) { 548 pass.base 549 .commands 550 .push(RenderCommand::BeginPipelineStatisticsQuery { 551 query_set_id, 552 query_index, 553 }); 554 } 555 556 #[no_mangle] 557 pub extern "C" fn wgpu_recorded_render_pass_end_pipeline_statistics_query( 558 pass: &mut RecordedRenderPass, 559 ) { 560 pass.base 561 .commands 562 .push(RenderCommand::EndPipelineStatisticsQuery); 563 } 564 565 #[no_mangle] 566 pub unsafe extern "C" fn wgpu_recorded_render_pass_execute_bundles( 567 pass: &mut RecordedRenderPass, 568 render_bundles: FfiSlice<'_, id::RenderBundleId>, 569 ) { 570 for &bundle_id in render_bundles.as_slice() { 571 pass.base 572 .commands 573 .push(RenderCommand::ExecuteBundle(bundle_id)); 574 } 575 } 576 577 #[no_mangle] 578 pub unsafe extern "C" fn wgpu_recorded_compute_pass_set_bind_group( 579 pass: &mut RecordedComputePass, 580 index: u32, 581 bind_group_id: Option<id::BindGroupId>, 582 offsets: FfiSlice<'_, DynamicOffset>, 583 ) { 584 let offsets = offsets.as_slice(); 585 pass.base.dynamic_offsets.extend_from_slice(offsets); 586 587 pass.base.commands.push(ComputeCommand::SetBindGroup { 588 index, 589 num_dynamic_offsets: offsets.len(), 590 bind_group_id, 591 }); 592 } 593 594 #[no_mangle] 595 pub extern "C" fn wgpu_recorded_compute_pass_set_pipeline( 596 pass: &mut RecordedComputePass, 597 pipeline_id: id::ComputePipelineId, 598 ) { 599 pass.base 600 .commands 601 .push(ComputeCommand::SetPipeline(pipeline_id)); 602 } 603 604 #[no_mangle] 605 pub extern "C" fn wgpu_recorded_compute_pass_dispatch_workgroups( 606 pass: &mut RecordedComputePass, 607 groups_x: u32, 608 groups_y: u32, 609 groups_z: u32, 610 ) { 611 pass.base 612 .commands 613 .push(ComputeCommand::Dispatch([groups_x, groups_y, groups_z])); 614 } 615 616 #[no_mangle] 617 pub extern "C" fn wgpu_recorded_compute_pass_dispatch_workgroups_indirect( 618 pass: &mut RecordedComputePass, 619 buffer_id: id::BufferId, 620 offset: BufferAddress, 621 ) { 622 pass.base 623 .commands 624 .push(ComputeCommand::DispatchIndirect { buffer_id, offset }); 625 } 626 627 /// # Safety 628 /// 629 /// This function is unsafe as there is no guarantee that the given `label` 630 /// is a valid null-terminated string. 631 #[no_mangle] 632 pub unsafe extern "C" fn wgpu_recorded_compute_pass_push_debug_group( 633 pass: &mut RecordedComputePass, 634 label: RawString, 635 color: u32, 636 ) { 637 let bytes = unsafe { ffi::CStr::from_ptr(label) }.to_bytes(); 638 pass.base.string_data.extend_from_slice(bytes); 639 640 pass.base.commands.push(ComputeCommand::PushDebugGroup { 641 color, 642 len: bytes.len(), 643 }); 644 } 645 646 #[no_mangle] 647 pub extern "C" fn wgpu_recorded_compute_pass_pop_debug_group(pass: &mut RecordedComputePass) { 648 pass.base.commands.push(ComputeCommand::PopDebugGroup); 649 } 650 651 /// # Safety 652 /// 653 /// This function is unsafe as there is no guarantee that the given `label` 654 /// is a valid null-terminated string. 655 #[no_mangle] 656 pub unsafe extern "C" fn wgpu_recorded_compute_pass_insert_debug_marker( 657 pass: &mut RecordedComputePass, 658 label: RawString, 659 color: u32, 660 ) { 661 let bytes = unsafe { ffi::CStr::from_ptr(label) }.to_bytes(); 662 pass.base.string_data.extend_from_slice(bytes); 663 664 pass.base.commands.push(ComputeCommand::InsertDebugMarker { 665 color, 666 len: bytes.len(), 667 }); 668 } 669 670 #[no_mangle] 671 pub extern "C" fn wgpu_recorded_compute_pass_write_timestamp( 672 pass: &mut RecordedComputePass, 673 query_set_id: id::QuerySetId, 674 query_index: u32, 675 ) { 676 pass.base.commands.push(ComputeCommand::WriteTimestamp { 677 query_set_id, 678 query_index, 679 }); 680 } 681 682 #[no_mangle] 683 pub extern "C" fn wgpu_recorded_compute_pass_begin_pipeline_statistics_query( 684 pass: &mut RecordedComputePass, 685 query_set_id: id::QuerySetId, 686 query_index: u32, 687 ) { 688 pass.base 689 .commands 690 .push(ComputeCommand::BeginPipelineStatisticsQuery { 691 query_set_id, 692 query_index, 693 }); 694 } 695 696 #[no_mangle] 697 pub extern "C" fn wgpu_recorded_compute_pass_end_pipeline_statistics_query( 698 pass: &mut RecordedComputePass, 699 ) { 700 pass.base 701 .commands 702 .push(ComputeCommand::EndPipelineStatisticsQuery); 703 } 704 705 pub fn replay_render_pass( 706 global: &Global, 707 device_id: id::DeviceId, 708 id: CommandEncoderId, 709 src_pass: &RecordedRenderPass, 710 error_buf: &mut crate::error::OwnedErrorBuffer, 711 ) { 712 // Explicitly forbid `LoadOp::DontCare`, until wgpu#8780 is resolved. 713 // 714 // Since `DontCare` is not part of WebGPU (and is unlikely to become so), 715 // only a corrupted content process could ever produce such a render pass, 716 // so it suffices for us to just crash here if we see it. 717 for attachment in &src_pass.color_attachments { 718 if let Some(attachment) = attachment { 719 assert!(!matches!(attachment.load_op, wgt::LoadOp::DontCare(_))); 720 } 721 } 722 if let Some(ref attachment) = src_pass.depth_stencil_attachment { 723 assert!(!matches!( 724 attachment.depth.load_op, 725 Some(wgt::LoadOp::DontCare(_)) 726 )); 727 assert!(!matches!( 728 attachment.stencil.load_op, 729 Some(wgt::LoadOp::DontCare(_)) 730 )); 731 } 732 733 let (mut dst_pass, err) = global.command_encoder_begin_render_pass( 734 id, 735 &wgc::command::RenderPassDescriptor { 736 label: src_pass.base.label.as_ref().map(|s| s.as_str().into()), 737 color_attachments: Cow::Borrowed(&src_pass.color_attachments), 738 depth_stencil_attachment: src_pass.depth_stencil_attachment.as_ref(), 739 timestamp_writes: src_pass.timestamp_writes.as_ref(), 740 occlusion_query_set: src_pass.occlusion_query_set_id, 741 multiview_mask: None, 742 }, 743 ); 744 if let Some(err) = err { 745 error_buf.init(err, device_id); 746 return; 747 } 748 match replay_render_pass_impl(global, src_pass, &mut dst_pass) { 749 Ok(()) => (), 750 Err(err) => { 751 error_buf.init(err, device_id); 752 return; 753 } 754 }; 755 756 match global.render_pass_end(&mut dst_pass) { 757 Ok(()) => (), 758 Err(err) => error_buf.init(err, device_id), 759 } 760 } 761 762 pub fn replay_render_pass_impl( 763 global: &Global, 764 src_pass: &RecordedRenderPass, 765 dst_pass: &mut wgc::command::RenderPass, 766 ) -> Result<(), wgc::command::PassStateError> { 767 let mut dynamic_offsets = src_pass.base.dynamic_offsets.as_slice(); 768 let mut dynamic_offsets = |len| { 769 let offsets; 770 (offsets, dynamic_offsets) = dynamic_offsets.split_at(len); 771 offsets 772 }; 773 let mut strings = src_pass.base.string_data.as_slice(); 774 let mut strings = |len| { 775 let label; 776 (label, strings) = strings.split_at(len); 777 label 778 }; 779 for command in &src_pass.base.commands { 780 match *command { 781 RenderCommand::SetBindGroup { 782 index, 783 num_dynamic_offsets, 784 bind_group_id, 785 } => { 786 let offsets = dynamic_offsets(num_dynamic_offsets); 787 global.render_pass_set_bind_group(dst_pass, index, bind_group_id, offsets) 788 } 789 RenderCommand::SetPipeline(pipeline_id) => { 790 global.render_pass_set_pipeline(dst_pass, pipeline_id) 791 } 792 RenderCommand::SetIndexBuffer { 793 buffer_id, 794 index_format, 795 offset, 796 size, 797 } => { 798 global.render_pass_set_index_buffer(dst_pass, buffer_id, index_format, offset, size) 799 } 800 RenderCommand::SetVertexBuffer { 801 slot, 802 buffer_id, 803 offset, 804 size, 805 } => global.render_pass_set_vertex_buffer(dst_pass, slot, buffer_id, offset, size), 806 RenderCommand::SetBlendConstant(color) => { 807 global.render_pass_set_blend_constant(dst_pass, color) 808 } 809 RenderCommand::SetStencilReference(value) => { 810 global.render_pass_set_stencil_reference(dst_pass, value) 811 } 812 RenderCommand::SetViewport { 813 x, 814 y, 815 w, 816 h, 817 depth_min, 818 depth_max, 819 } => global.render_pass_set_viewport(dst_pass, x, y, w, h, depth_min, depth_max), 820 RenderCommand::SetScissor { x, y, w, h } => { 821 global.render_pass_set_scissor_rect(dst_pass, x, y, w, h) 822 } 823 RenderCommand::Draw { 824 vertex_count, 825 instance_count, 826 first_vertex, 827 first_instance, 828 } => global.render_pass_draw( 829 dst_pass, 830 vertex_count, 831 instance_count, 832 first_vertex, 833 first_instance, 834 ), 835 RenderCommand::DrawIndexed { 836 index_count, 837 instance_count, 838 first_index, 839 base_vertex, 840 first_instance, 841 } => global.render_pass_draw_indexed( 842 dst_pass, 843 index_count, 844 instance_count, 845 first_index, 846 base_vertex, 847 first_instance, 848 ), 849 RenderCommand::MultiDrawIndirect { 850 buffer_id, 851 offset, 852 count, 853 indexed, 854 } => match (indexed, count) { 855 (false, Some(count)) => { 856 global.render_pass_multi_draw_indirect(dst_pass, buffer_id, offset, count) 857 } 858 (false, None) => global.render_pass_draw_indirect(dst_pass, buffer_id, offset), 859 (true, Some(count)) => global 860 .render_pass_multi_draw_indexed_indirect(dst_pass, buffer_id, offset, count), 861 (true, None) => { 862 global.render_pass_draw_indexed_indirect(dst_pass, buffer_id, offset) 863 } 864 }, 865 RenderCommand::MultiDrawIndirectCount { 866 buffer_id, 867 offset, 868 count_buffer_id, 869 count_buffer_offset, 870 max_count, 871 indexed, 872 } => { 873 if indexed { 874 global.render_pass_multi_draw_indexed_indirect_count( 875 dst_pass, 876 buffer_id, 877 offset, 878 count_buffer_id, 879 count_buffer_offset, 880 max_count, 881 ) 882 } else { 883 global.render_pass_multi_draw_indirect_count( 884 dst_pass, 885 buffer_id, 886 offset, 887 count_buffer_id, 888 count_buffer_offset, 889 max_count, 890 ) 891 } 892 } 893 RenderCommand::PushDebugGroup { color, len } => { 894 let label = strings(len); 895 let label = std::str::from_utf8(label).unwrap(); 896 global.render_pass_push_debug_group(dst_pass, label, color) 897 } 898 RenderCommand::PopDebugGroup => global.render_pass_pop_debug_group(dst_pass), 899 RenderCommand::InsertDebugMarker { color, len } => { 900 let label = strings(len); 901 let label = std::str::from_utf8(label).unwrap(); 902 global.render_pass_insert_debug_marker(dst_pass, label, color) 903 } 904 RenderCommand::WriteTimestamp { 905 query_set_id, 906 query_index, 907 } => global.render_pass_write_timestamp(dst_pass, query_set_id, query_index), 908 RenderCommand::BeginOcclusionQuery { query_index } => { 909 global.render_pass_begin_occlusion_query(dst_pass, query_index) 910 } 911 RenderCommand::EndOcclusionQuery => global.render_pass_end_occlusion_query(dst_pass), 912 RenderCommand::BeginPipelineStatisticsQuery { 913 query_set_id, 914 query_index, 915 } => global.render_pass_begin_pipeline_statistics_query( 916 dst_pass, 917 query_set_id, 918 query_index, 919 ), 920 RenderCommand::EndPipelineStatisticsQuery => { 921 global.render_pass_end_pipeline_statistics_query(dst_pass) 922 } 923 RenderCommand::ExecuteBundle(bundle_id) => { 924 global.render_pass_execute_bundles(dst_pass, &[bundle_id]) 925 } 926 }? 927 } 928 929 Ok(()) 930 } 931 932 pub fn replay_compute_pass( 933 global: &Global, 934 device_id: id::DeviceId, 935 id: CommandEncoderId, 936 src_pass: &RecordedComputePass, 937 error_buf: &mut crate::error::OwnedErrorBuffer, 938 ) { 939 let (mut dst_pass, err) = global.command_encoder_begin_compute_pass( 940 id, 941 &wgc::command::ComputePassDescriptor { 942 label: src_pass.base.label.as_ref().map(|s| s.as_str().into()), 943 timestamp_writes: src_pass.timestamp_writes.clone(), 944 }, 945 ); 946 if let Some(err) = err { 947 error_buf.init(err, device_id); 948 return; 949 } 950 if let Err(err) = replay_compute_pass_impl(global, src_pass, &mut dst_pass) { 951 error_buf.init(err, device_id); 952 return; 953 } 954 955 match global.compute_pass_end(&mut dst_pass) { 956 Ok(()) => (), 957 Err(err) => error_buf.init(err, device_id), 958 } 959 } 960 961 fn replay_compute_pass_impl( 962 global: &Global, 963 src_pass: &RecordedComputePass, 964 dst_pass: &mut wgc::command::ComputePass, 965 ) -> Result<(), wgc::command::PassStateError> { 966 let mut dynamic_offsets = src_pass.base.dynamic_offsets.as_slice(); 967 let mut dynamic_offsets = |len| { 968 let offsets; 969 (offsets, dynamic_offsets) = dynamic_offsets.split_at(len); 970 offsets 971 }; 972 let mut strings = src_pass.base.string_data.as_slice(); 973 let mut strings = |len| { 974 let label; 975 (label, strings) = strings.split_at(len); 976 label 977 }; 978 for command in &src_pass.base.commands { 979 match *command { 980 ComputeCommand::SetBindGroup { 981 index, 982 num_dynamic_offsets, 983 bind_group_id, 984 } => { 985 let offsets = dynamic_offsets(num_dynamic_offsets); 986 global.compute_pass_set_bind_group(dst_pass, index, bind_group_id, offsets)?; 987 } 988 ComputeCommand::SetPipeline(pipeline_id) => { 989 global.compute_pass_set_pipeline(dst_pass, pipeline_id)?; 990 } 991 ComputeCommand::Dispatch([x, y, z]) => { 992 global.compute_pass_dispatch_workgroups(dst_pass, x, y, z)?; 993 } 994 ComputeCommand::DispatchIndirect { buffer_id, offset } => { 995 global.compute_pass_dispatch_workgroups_indirect(dst_pass, buffer_id, offset)?; 996 } 997 ComputeCommand::PushDebugGroup { color, len } => { 998 let label = strings(len); 999 let label = std::str::from_utf8(label).unwrap(); 1000 global.compute_pass_push_debug_group(dst_pass, label, color)?; 1001 } 1002 ComputeCommand::PopDebugGroup => { 1003 global.compute_pass_pop_debug_group(dst_pass)?; 1004 } 1005 ComputeCommand::InsertDebugMarker { color, len } => { 1006 let label = strings(len); 1007 let label = std::str::from_utf8(label).unwrap(); 1008 global.compute_pass_insert_debug_marker(dst_pass, label, color)?; 1009 } 1010 ComputeCommand::WriteTimestamp { 1011 query_set_id, 1012 query_index, 1013 } => { 1014 global.compute_pass_write_timestamp(dst_pass, query_set_id, query_index)?; 1015 } 1016 ComputeCommand::BeginPipelineStatisticsQuery { 1017 query_set_id, 1018 query_index, 1019 } => { 1020 global.compute_pass_begin_pipeline_statistics_query( 1021 dst_pass, 1022 query_set_id, 1023 query_index, 1024 )?; 1025 } 1026 ComputeCommand::EndPipelineStatisticsQuery => { 1027 global.compute_pass_end_pipeline_statistics_query(dst_pass)?; 1028 } 1029 } 1030 } 1031 1032 Ok(()) 1033 }