1use crate::resource::Resource;
2use crate::snatch::SnatchGuard;
3use crate::{
4 api_log,
5 binding_model::BindError,
6 command::{
7 self,
8 bind::Binder,
9 end_occlusion_query, end_pipeline_statistics_query,
10 memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState},
11 BasePass, BasePassRef, BindGroupStateChange, CommandBuffer, CommandEncoderError,
12 CommandEncoderStatus, DrawError, ExecutionError, MapPassErr, PassErrorScope, QueryUseError,
13 RenderCommand, RenderCommandError, StateChange,
14 },
15 device::{
16 AttachmentData, Device, DeviceError, MissingDownlevelFlags, MissingFeatures,
17 RenderPassCompatibilityCheckType, RenderPassCompatibilityError, RenderPassContext,
18 },
19 error::{ErrorFormatter, PrettyError},
20 global::Global,
21 hal_api::HalApi,
22 hal_label, id,
23 init_tracker::{MemoryInitKind, TextureInitRange, TextureInitTrackerAction},
24 pipeline::{self, PipelineFlags},
25 resource::{QuerySet, Texture, TextureView, TextureViewNotRenderableReason},
26 storage::Storage,
27 track::{TextureSelector, Tracker, UsageConflict, UsageScope},
28 validation::{
29 check_buffer_usage, check_texture_usage, MissingBufferUsageError, MissingTextureUsageError,
30 },
31 Label,
32};
33
34use arrayvec::ArrayVec;
35use hal::CommandEncoder as _;
36use thiserror::Error;
37use wgt::{
38 BufferAddress, BufferSize, BufferUsages, Color, IndexFormat, TextureUsages,
39 TextureViewDimension, VertexStepMode,
40};
41
42#[cfg(feature = "serde")]
43use serde::Deserialize;
44#[cfg(feature = "serde")]
45use serde::Serialize;
46
47use std::sync::Arc;
48use std::{borrow::Cow, fmt, iter, marker::PhantomData, mem, num::NonZeroU32, ops::Range, str};
49
50use super::{
51 memory_init::TextureSurfaceDiscard, CommandBufferTextureMemoryActions, CommandEncoder,
52 QueryResetMap,
53};
54
55#[repr(C)]
57#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
58#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
59#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
60pub enum LoadOp {
61 Clear = 0,
63 Load = 1,
65}
66
67#[repr(C)]
69#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
70#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
71#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
72pub enum StoreOp {
73 Discard = 0,
77 Store = 1,
79}
80
81#[repr(C)]
83#[derive(Clone, Debug, Eq, PartialEq)]
84#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
85pub struct PassChannel<V> {
86 pub load_op: LoadOp,
92 pub store_op: StoreOp,
94 pub clear_value: V,
97 pub read_only: bool,
101}
102
103impl<V> PassChannel<V> {
104 fn hal_ops(&self) -> hal::AttachmentOps {
105 let mut ops = hal::AttachmentOps::empty();
106 match self.load_op {
107 LoadOp::Load => ops |= hal::AttachmentOps::LOAD,
108 LoadOp::Clear => (),
109 };
110 match self.store_op {
111 StoreOp::Store => ops |= hal::AttachmentOps::STORE,
112 StoreOp::Discard => (),
113 };
114 ops
115 }
116}
117
118#[repr(C)]
120#[derive(Clone, Debug, PartialEq)]
121#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
122pub struct RenderPassColorAttachment {
123 pub view: id::TextureViewId,
125 pub resolve_target: Option<id::TextureViewId>,
127 pub channel: PassChannel<Color>,
129}
130
131#[repr(C)]
133#[derive(Clone, Debug, PartialEq)]
134#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
135pub struct RenderPassDepthStencilAttachment {
136 pub view: id::TextureViewId,
138 pub depth: PassChannel<f32>,
140 pub stencil: PassChannel<u32>,
142}
143
144impl RenderPassDepthStencilAttachment {
145 fn depth_stencil_read_only(
154 &self,
155 aspects: hal::FormatAspects,
156 ) -> Result<(bool, bool), RenderPassErrorInner> {
157 let mut depth_read_only = true;
158 let mut stencil_read_only = true;
159
160 if aspects.contains(hal::FormatAspects::DEPTH) {
161 if self.depth.read_only
162 && (self.depth.load_op, self.depth.store_op) != (LoadOp::Load, StoreOp::Store)
163 {
164 return Err(RenderPassErrorInner::InvalidDepthOps);
165 }
166 depth_read_only = self.depth.read_only;
167 }
168
169 if aspects.contains(hal::FormatAspects::STENCIL) {
170 if self.stencil.read_only
171 && (self.stencil.load_op, self.stencil.store_op) != (LoadOp::Load, StoreOp::Store)
172 {
173 return Err(RenderPassErrorInner::InvalidStencilOps);
174 }
175 stencil_read_only = self.stencil.read_only;
176 }
177
178 Ok((depth_read_only, stencil_read_only))
179 }
180}
181
182#[repr(C)]
184#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
185#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
186#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
187pub enum RenderPassTimestampLocation {
188 Beginning = 0,
189 End = 1,
190}
191
192#[repr(C)]
194#[derive(Clone, Debug, PartialEq, Eq)]
195#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
196pub struct RenderPassTimestampWrites {
197 pub query_set: id::QuerySetId,
199 pub beginning_of_pass_write_index: Option<u32>,
201 pub end_of_pass_write_index: Option<u32>,
203}
204
205#[derive(Clone, Debug, Default, PartialEq)]
207pub struct RenderPassDescriptor<'a> {
208 pub label: Label<'a>,
209 pub color_attachments: Cow<'a, [Option<RenderPassColorAttachment>]>,
211 pub depth_stencil_attachment: Option<&'a RenderPassDepthStencilAttachment>,
213 pub timestamp_writes: Option<&'a RenderPassTimestampWrites>,
215 pub occlusion_query_set: Option<id::QuerySetId>,
217}
218
219#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
220pub struct RenderPass {
221 base: BasePass<RenderCommand>,
222 parent_id: id::CommandEncoderId,
223 color_targets: ArrayVec<Option<RenderPassColorAttachment>, { hal::MAX_COLOR_ATTACHMENTS }>,
224 depth_stencil_target: Option<RenderPassDepthStencilAttachment>,
225 timestamp_writes: Option<RenderPassTimestampWrites>,
226 occlusion_query_set_id: Option<id::QuerySetId>,
227
228 #[cfg_attr(feature = "serde", serde(skip))]
230 current_bind_groups: BindGroupStateChange,
231 #[cfg_attr(feature = "serde", serde(skip))]
232 current_pipeline: StateChange<id::RenderPipelineId>,
233}
234
235impl RenderPass {
236 pub fn new(parent_id: id::CommandEncoderId, desc: &RenderPassDescriptor) -> Self {
237 Self {
238 base: BasePass::new(&desc.label),
239 parent_id,
240 color_targets: desc.color_attachments.iter().cloned().collect(),
241 depth_stencil_target: desc.depth_stencil_attachment.cloned(),
242 timestamp_writes: desc.timestamp_writes.cloned(),
243 occlusion_query_set_id: desc.occlusion_query_set,
244
245 current_bind_groups: BindGroupStateChange::new(),
246 current_pipeline: StateChange::new(),
247 }
248 }
249
250 pub fn parent_id(&self) -> id::CommandEncoderId {
251 self.parent_id
252 }
253
254 #[cfg(feature = "trace")]
255 pub fn into_command(self) -> crate::device::trace::Command {
256 crate::device::trace::Command::RunRenderPass {
257 base: self.base,
258 target_colors: self.color_targets.into_iter().collect(),
259 target_depth_stencil: self.depth_stencil_target,
260 timestamp_writes: self.timestamp_writes,
261 occlusion_query_set_id: self.occlusion_query_set_id,
262 }
263 }
264
265 pub fn set_index_buffer(
266 &mut self,
267 buffer_id: id::BufferId,
268 index_format: IndexFormat,
269 offset: BufferAddress,
270 size: Option<BufferSize>,
271 ) {
272 self.base.commands.push(RenderCommand::SetIndexBuffer {
273 buffer_id,
274 index_format,
275 offset,
276 size,
277 });
278 }
279}
280
281impl fmt::Debug for RenderPass {
282 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283 f.debug_struct("RenderPass")
284 .field("encoder_id", &self.parent_id)
285 .field("color_targets", &self.color_targets)
286 .field("depth_stencil_target", &self.depth_stencil_target)
287 .field("command count", &self.base.commands.len())
288 .field("dynamic offset count", &self.base.dynamic_offsets.len())
289 .field(
290 "push constant u32 count",
291 &self.base.push_constant_data.len(),
292 )
293 .finish()
294 }
295}
296
297#[derive(Debug, PartialEq)]
298enum OptionalState {
299 Unused,
300 Required,
301 Set,
302}
303
304impl OptionalState {
305 fn require(&mut self, require: bool) {
306 if require && *self == Self::Unused {
307 *self = Self::Required;
308 }
309 }
310}
311
312#[derive(Debug, Default)]
313struct IndexState {
314 bound_buffer_view: Option<(id::BufferId, Range<BufferAddress>)>,
315 format: Option<IndexFormat>,
316 pipeline_format: Option<IndexFormat>,
317 limit: u64,
318}
319
320impl IndexState {
321 fn update_limit(&mut self) {
322 self.limit = match self.bound_buffer_view {
323 Some((_, ref range)) => {
324 let format = self
325 .format
326 .expect("IndexState::update_limit must be called after a index buffer is set");
327 let shift = match format {
328 IndexFormat::Uint16 => 1,
329 IndexFormat::Uint32 => 2,
330 };
331
332 (range.end - range.start) >> shift
333 }
334 None => 0,
335 }
336 }
337
338 fn reset(&mut self) {
339 self.bound_buffer_view = None;
340 self.limit = 0;
341 }
342}
343
344#[derive(Clone, Copy, Debug)]
345struct VertexBufferState {
346 total_size: BufferAddress,
347 step: pipeline::VertexStep,
348 bound: bool,
349}
350
351impl VertexBufferState {
352 const EMPTY: Self = Self {
353 total_size: 0,
354 step: pipeline::VertexStep {
355 stride: 0,
356 last_stride: 0,
357 mode: VertexStepMode::Vertex,
358 },
359 bound: false,
360 };
361}
362
363#[derive(Debug, Default)]
364struct VertexState {
365 inputs: ArrayVec<VertexBufferState, { hal::MAX_VERTEX_BUFFERS }>,
366 vertex_limit: u64,
368 vertex_limit_slot: u32,
370 instance_limit: u64,
372 instance_limit_slot: u32,
374 buffers_required: u32,
376}
377
378impl VertexState {
379 fn update_limits(&mut self) {
380 self.vertex_limit = u32::MAX as u64;
386 self.instance_limit = u32::MAX as u64;
387 for (idx, vbs) in self.inputs.iter().enumerate() {
388 if !vbs.bound {
389 continue;
390 }
391
392 let limit = if vbs.total_size < vbs.step.last_stride {
393 0
395 } else {
396 if vbs.step.stride == 0 {
397 continue;
401 }
402
403 (vbs.total_size - vbs.step.last_stride) / vbs.step.stride + 1
405 };
406
407 match vbs.step.mode {
408 VertexStepMode::Vertex => {
409 if limit < self.vertex_limit {
410 self.vertex_limit = limit;
411 self.vertex_limit_slot = idx as _;
412 }
413 }
414 VertexStepMode::Instance => {
415 if limit < self.instance_limit {
416 self.instance_limit = limit;
417 self.instance_limit_slot = idx as _;
418 }
419 }
420 }
421 }
422 }
423
424 fn reset(&mut self) {
425 self.inputs.clear();
426 self.vertex_limit = 0;
427 self.instance_limit = 0;
428 }
429}
430
431#[derive(Debug)]
432struct State<A: HalApi> {
433 pipeline_flags: PipelineFlags,
434 binder: Binder<A>,
435 blend_constant: OptionalState,
436 stencil_reference: u32,
437 pipeline: Option<id::RenderPipelineId>,
438 index: IndexState,
439 vertex: VertexState,
440 debug_scope_depth: u32,
441}
442
443impl<A: HalApi> State<A> {
444 fn is_ready(&self, indexed: bool) -> Result<(), DrawError> {
445 let vertex_buffer_count = self.vertex.inputs.iter().take_while(|v| v.bound).count() as u32;
447 if vertex_buffer_count < self.vertex.buffers_required {
449 return Err(DrawError::MissingVertexBuffer {
450 index: vertex_buffer_count,
451 });
452 }
453
454 let bind_mask = self.binder.invalid_mask();
455 if bind_mask != 0 {
456 return Err(DrawError::IncompatibleBindGroup {
458 index: bind_mask.trailing_zeros(),
459 diff: self.binder.bgl_diff(),
460 });
461 }
462 if self.pipeline.is_none() {
463 return Err(DrawError::MissingPipeline);
464 }
465 if self.blend_constant == OptionalState::Required {
466 return Err(DrawError::MissingBlendConstant);
467 }
468
469 if indexed {
470 if let Some(pipeline_index_format) = self.index.pipeline_format {
472 let buffer_index_format = self.index.format.ok_or(DrawError::MissingIndexBuffer)?;
474
475 if pipeline_index_format != buffer_index_format {
477 return Err(DrawError::UnmatchedIndexFormats {
478 pipeline: pipeline_index_format,
479 buffer: buffer_index_format,
480 });
481 }
482 }
483 }
484
485 self.binder.check_late_buffer_bindings()?;
486
487 Ok(())
488 }
489
490 fn reset_bundle(&mut self) {
492 self.binder.reset();
493 self.pipeline = None;
494 self.index.reset();
495 self.vertex.reset();
496 }
497}
498
499#[derive(Debug, Copy, Clone)]
503pub enum AttachmentErrorLocation {
504 Color { index: usize, resolve: bool },
505 Depth,
506}
507
508impl fmt::Display for AttachmentErrorLocation {
509 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510 match *self {
511 AttachmentErrorLocation::Color {
512 index,
513 resolve: false,
514 } => write!(f, "color attachment at index {index}'s texture view"),
515 AttachmentErrorLocation::Color {
516 index,
517 resolve: true,
518 } => write!(
519 f,
520 "color attachment at index {index}'s resolve texture view"
521 ),
522 AttachmentErrorLocation::Depth => write!(f, "depth attachment's texture view"),
523 }
524 }
525}
526
527#[derive(Clone, Debug, Error)]
528#[non_exhaustive]
529pub enum ColorAttachmentError {
530 #[error("Attachment format {0:?} is not a color format")]
531 InvalidFormat(wgt::TextureFormat),
532 #[error("The number of color attachments {given} exceeds the limit {limit}")]
533 TooMany { given: usize, limit: usize },
534 #[error("The total number of bytes per sample in color attachments {total} exceeds the limit {limit}")]
535 TooManyBytesPerSample { total: u32, limit: u32 },
536}
537
538#[derive(Clone, Debug, Error)]
540pub enum RenderPassErrorInner {
541 #[error(transparent)]
542 Device(DeviceError),
543 #[error(transparent)]
544 ColorAttachment(#[from] ColorAttachmentError),
545 #[error(transparent)]
546 Encoder(#[from] CommandEncoderError),
547 #[error("Attachment texture view {0:?} is invalid")]
548 InvalidAttachment(id::TextureViewId),
549 #[error("Attachment texture view {0:?} is invalid")]
550 InvalidResolveTarget(id::TextureViewId),
551 #[error("The format of the depth-stencil attachment ({0:?}) is not a depth-stencil format")]
552 InvalidDepthStencilAttachmentFormat(wgt::TextureFormat),
553 #[error("The format of the {location} ({format:?}) is not resolvable")]
554 UnsupportedResolveTargetFormat {
555 location: AttachmentErrorLocation,
556 format: wgt::TextureFormat,
557 },
558 #[error("No color attachments or depth attachments were provided, at least one attachment of any kind must be provided")]
559 MissingAttachments,
560 #[error("The {location} is not renderable:")]
561 TextureViewIsNotRenderable {
562 location: AttachmentErrorLocation,
563 #[source]
564 reason: TextureViewNotRenderableReason,
565 },
566 #[error("Attachments have differing sizes: the {expected_location} has extent {expected_extent:?} but is followed by the {actual_location} which has {actual_extent:?}")]
567 AttachmentsDimensionMismatch {
568 expected_location: AttachmentErrorLocation,
569 expected_extent: wgt::Extent3d,
570 actual_location: AttachmentErrorLocation,
571 actual_extent: wgt::Extent3d,
572 },
573 #[error("Attachments have differing sample counts: the {expected_location} has count {expected_samples:?} but is followed by the {actual_location} which has count {actual_samples:?}")]
574 AttachmentSampleCountMismatch {
575 expected_location: AttachmentErrorLocation,
576 expected_samples: u32,
577 actual_location: AttachmentErrorLocation,
578 actual_samples: u32,
579 },
580 #[error("The resolve source, {location}, must be multi-sampled (has {src} samples) while the resolve destination must not be multisampled (has {dst} samples)")]
581 InvalidResolveSampleCounts {
582 location: AttachmentErrorLocation,
583 src: u32,
584 dst: u32,
585 },
586 #[error(
587 "Resource source, {location}, format ({src:?}) must match the resolve destination format ({dst:?})"
588 )]
589 MismatchedResolveTextureFormat {
590 location: AttachmentErrorLocation,
591 src: wgt::TextureFormat,
592 dst: wgt::TextureFormat,
593 },
594 #[error("Surface texture is dropped before the render pass is finished")]
595 SurfaceTextureDropped,
596 #[error("Not enough memory left for render pass")]
597 OutOfMemory,
598 #[error("The bind group at index {0:?} is invalid")]
599 InvalidBindGroup(usize),
600 #[error("Unable to clear non-present/read-only depth")]
601 InvalidDepthOps,
602 #[error("Unable to clear non-present/read-only stencil")]
603 InvalidStencilOps,
604 #[error("Setting `values_offset` to be `None` is only for internal use in render bundles")]
605 InvalidValuesOffset,
606 #[error(transparent)]
607 MissingFeatures(#[from] MissingFeatures),
608 #[error(transparent)]
609 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
610 #[error("Indirect draw uses bytes {offset}..{end_offset} {} which overruns indirect buffer of size {buffer_size}",
611 count.map_or_else(String::new, |v| format!("(using count {v})")))]
612 IndirectBufferOverrun {
613 count: Option<NonZeroU32>,
614 offset: u64,
615 end_offset: u64,
616 buffer_size: u64,
617 },
618 #[error("Indirect draw uses bytes {begin_count_offset}..{end_count_offset} which overruns indirect buffer of size {count_buffer_size}")]
619 IndirectCountBufferOverrun {
620 begin_count_offset: u64,
621 end_count_offset: u64,
622 count_buffer_size: u64,
623 },
624 #[error("Cannot pop debug group, because number of pushed debug groups is zero")]
625 InvalidPopDebugGroup,
626 #[error(transparent)]
627 ResourceUsageConflict(#[from] UsageConflict),
628 #[error("Render bundle has incompatible targets, {0}")]
629 IncompatibleBundleTargets(#[from] RenderPassCompatibilityError),
630 #[error(
631 "Render bundle has incompatible read-only flags: \
632 bundle has flags depth = {bundle_depth} and stencil = {bundle_stencil}, \
633 while the pass has flags depth = {pass_depth} and stencil = {pass_stencil}. \
634 Read-only renderpasses are only compatible with read-only bundles for that aspect."
635 )]
636 IncompatibleBundleReadOnlyDepthStencil {
637 pass_depth: bool,
638 pass_stencil: bool,
639 bundle_depth: bool,
640 bundle_stencil: bool,
641 },
642 #[error(transparent)]
643 RenderCommand(#[from] RenderCommandError),
644 #[error(transparent)]
645 Draw(#[from] DrawError),
646 #[error(transparent)]
647 Bind(#[from] BindError),
648 #[error(transparent)]
649 QueryUse(#[from] QueryUseError),
650 #[error("Multiview layer count must match")]
651 MultiViewMismatch,
652 #[error(
653 "Multiview pass texture views with more than one array layer must have D2Array dimension"
654 )]
655 MultiViewDimensionMismatch,
656 #[error("QuerySet {0:?} is invalid")]
657 InvalidQuerySet(id::QuerySetId),
658 #[error("missing occlusion query set")]
659 MissingOcclusionQuerySet,
660}
661
662impl PrettyError for RenderPassErrorInner {
663 fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
664 fmt.error(self);
665 if let Self::InvalidAttachment(id) = *self {
666 fmt.texture_view_label_with_key(&id, "attachment");
667 };
668 if let Self::Draw(DrawError::IncompatibleBindGroup { diff, .. }) = self {
669 for d in diff {
670 fmt.note(&d);
671 }
672 };
673 }
674}
675
676impl From<MissingBufferUsageError> for RenderPassErrorInner {
677 fn from(error: MissingBufferUsageError) -> Self {
678 Self::RenderCommand(error.into())
679 }
680}
681
682impl From<MissingTextureUsageError> for RenderPassErrorInner {
683 fn from(error: MissingTextureUsageError) -> Self {
684 Self::RenderCommand(error.into())
685 }
686}
687
688impl From<DeviceError> for RenderPassErrorInner {
689 fn from(error: DeviceError) -> Self {
690 Self::Device(error)
691 }
692}
693
694#[derive(Clone, Debug, Error)]
696#[error("{scope}")]
697pub struct RenderPassError {
698 pub scope: PassErrorScope,
699 #[source]
700 inner: RenderPassErrorInner,
701}
702impl PrettyError for RenderPassError {
703 fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
704 fmt.error(self);
707 self.scope.fmt_pretty(fmt);
708 }
709}
710
711impl<T, E> MapPassErr<T, RenderPassError> for Result<T, E>
712where
713 E: Into<RenderPassErrorInner>,
714{
715 fn map_pass_err(self, scope: PassErrorScope) -> Result<T, RenderPassError> {
716 self.map_err(|inner| RenderPassError {
717 scope,
718 inner: inner.into(),
719 })
720 }
721}
722
723struct RenderAttachment<'a, A: HalApi> {
724 texture: Arc<Texture<A>>,
725 selector: &'a TextureSelector,
726 usage: hal::TextureUses,
727}
728
729impl<A: HalApi> TextureView<A> {
730 fn to_render_attachment(&self, usage: hal::TextureUses) -> RenderAttachment<A> {
731 RenderAttachment {
732 texture: self.parent.clone(),
733 selector: &self.selector,
734 usage,
735 }
736 }
737}
738
739const MAX_TOTAL_ATTACHMENTS: usize = hal::MAX_COLOR_ATTACHMENTS + hal::MAX_COLOR_ATTACHMENTS + 1;
740type AttachmentDataVec<T> = ArrayVec<T, MAX_TOTAL_ATTACHMENTS>;
741
742struct RenderPassInfo<'a, 'd, A: HalApi> {
743 context: RenderPassContext,
744 usage_scope: UsageScope<'d, A>,
745 render_attachments: AttachmentDataVec<RenderAttachment<'a, A>>,
747 is_depth_read_only: bool,
748 is_stencil_read_only: bool,
749 extent: wgt::Extent3d,
750 _phantom: PhantomData<A>,
751
752 pending_discard_init_fixups: SurfacesInDiscardState<A>,
753 divergent_discarded_depth_stencil_aspect: Option<(wgt::TextureAspect, &'a TextureView<A>)>,
754 multiview: Option<NonZeroU32>,
755}
756
757impl<'a, 'd, A: HalApi> RenderPassInfo<'a, 'd, A> {
758 fn add_pass_texture_init_actions<V>(
759 channel: &PassChannel<V>,
760 texture_memory_actions: &mut CommandBufferTextureMemoryActions<A>,
761 view: &TextureView<A>,
762 pending_discard_init_fixups: &mut SurfacesInDiscardState<A>,
763 ) {
764 if channel.load_op == LoadOp::Load {
765 pending_discard_init_fixups.extend(texture_memory_actions.register_init_action(
766 &TextureInitTrackerAction {
767 texture: view.parent.clone(),
768 range: TextureInitRange::from(view.selector.clone()),
769 kind: MemoryInitKind::NeedsInitializedMemory,
771 },
772 ));
773 } else if channel.store_op == StoreOp::Store {
774 texture_memory_actions.register_implicit_init(
776 &view.parent,
777 TextureInitRange::from(view.selector.clone()),
778 );
779 }
780 if channel.store_op == StoreOp::Discard {
781 texture_memory_actions.discard(TextureSurfaceDiscard {
785 texture: view.parent.clone(),
786 mip_level: view.selector.mips.start,
787 layer: view.selector.layers.start,
788 });
789 }
790 }
791
792 fn start(
793 device: &'d Device<A>,
794 label: Option<&str>,
795 color_attachments: &[Option<RenderPassColorAttachment>],
796 depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>,
797 timestamp_writes: Option<&RenderPassTimestampWrites>,
798 occlusion_query_set: Option<id::QuerySetId>,
799 encoder: &mut CommandEncoder<A>,
800 trackers: &mut Tracker<A>,
801 texture_memory_actions: &mut CommandBufferTextureMemoryActions<A>,
802 pending_query_resets: &mut QueryResetMap<A>,
803 view_guard: &'a Storage<TextureView<A>>,
804 query_set_guard: &'a Storage<QuerySet<A>>,
805 snatch_guard: &SnatchGuard<'a>,
806 ) -> Result<Self, RenderPassErrorInner> {
807 profiling::scope!("RenderPassInfo::start");
808
809 let mut is_depth_read_only = false;
813 let mut is_stencil_read_only = false;
814
815 let mut render_attachments = AttachmentDataVec::<RenderAttachment<A>>::new();
816 let mut discarded_surfaces = AttachmentDataVec::new();
817 let mut pending_discard_init_fixups = SurfacesInDiscardState::new();
818 let mut divergent_discarded_depth_stencil_aspect = None;
819
820 let mut attachment_location = AttachmentErrorLocation::Color {
821 index: usize::MAX,
822 resolve: false,
823 };
824 let mut extent = None;
825 let mut sample_count = 0;
826
827 let mut detected_multiview: Option<Option<NonZeroU32>> = None;
828
829 let mut check_multiview = |view: &TextureView<A>| {
830 let layers = view.selector.layers.end - view.selector.layers.start;
832 let this_multiview = if layers >= 2 {
833 Some(unsafe { NonZeroU32::new_unchecked(layers) })
835 } else {
836 None
837 };
838
839 if this_multiview.is_some() && view.desc.dimension != TextureViewDimension::D2Array {
841 return Err(RenderPassErrorInner::MultiViewDimensionMismatch);
842 }
843
844 if let Some(multiview) = detected_multiview {
846 if multiview != this_multiview {
847 return Err(RenderPassErrorInner::MultiViewMismatch);
848 }
849 } else {
850 if this_multiview.is_some() {
852 device.require_features(wgt::Features::MULTIVIEW)?;
853 }
854
855 detected_multiview = Some(this_multiview);
856 }
857
858 Ok(())
859 };
860 let mut add_view = |view: &TextureView<A>, location| {
861 let render_extent = view.render_extent.map_err(|reason| {
862 RenderPassErrorInner::TextureViewIsNotRenderable { location, reason }
863 })?;
864 if let Some(ex) = extent {
865 if ex != render_extent {
866 return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
867 expected_location: attachment_location,
868 expected_extent: ex,
869 actual_location: location,
870 actual_extent: render_extent,
871 });
872 }
873 } else {
874 extent = Some(render_extent);
875 }
876 if sample_count == 0 {
877 sample_count = view.samples;
878 } else if sample_count != view.samples {
879 return Err(RenderPassErrorInner::AttachmentSampleCountMismatch {
880 expected_location: attachment_location,
881 expected_samples: sample_count,
882 actual_location: location,
883 actual_samples: view.samples,
884 });
885 }
886 attachment_location = location;
887 Ok(())
888 };
889
890 let mut colors =
891 ArrayVec::<Option<hal::ColorAttachment<A>>, { hal::MAX_COLOR_ATTACHMENTS }>::new();
892 let mut depth_stencil = None;
893
894 if let Some(at) = depth_stencil_attachment {
895 let view: &TextureView<A> = trackers
896 .views
897 .add_single(view_guard, at.view)
898 .ok_or(RenderPassErrorInner::InvalidAttachment(at.view))?;
899 check_multiview(view)?;
900 add_view(view, AttachmentErrorLocation::Depth)?;
901
902 let ds_aspects = view.desc.aspects();
903 if ds_aspects.contains(hal::FormatAspects::COLOR) {
904 return Err(RenderPassErrorInner::InvalidDepthStencilAttachmentFormat(
905 view.desc.format,
906 ));
907 }
908
909 if !ds_aspects.contains(hal::FormatAspects::STENCIL)
910 || (at.stencil.load_op == at.depth.load_op
911 && at.stencil.store_op == at.depth.store_op)
912 {
913 Self::add_pass_texture_init_actions(
914 &at.depth,
915 texture_memory_actions,
916 view,
917 &mut pending_discard_init_fixups,
918 );
919 } else if !ds_aspects.contains(hal::FormatAspects::DEPTH) {
920 Self::add_pass_texture_init_actions(
921 &at.stencil,
922 texture_memory_actions,
923 view,
924 &mut pending_discard_init_fixups,
925 );
926 } else {
927 let need_init_beforehand =
949 at.depth.load_op == LoadOp::Load || at.stencil.load_op == LoadOp::Load;
950 if need_init_beforehand {
951 pending_discard_init_fixups.extend(
952 texture_memory_actions.register_init_action(&TextureInitTrackerAction {
953 texture: view.parent.clone(),
954 range: TextureInitRange::from(view.selector.clone()),
955 kind: MemoryInitKind::NeedsInitializedMemory,
956 }),
957 );
958 }
959
960 if at.depth.store_op != at.stencil.store_op {
969 if !need_init_beforehand {
970 texture_memory_actions.register_implicit_init(
971 &view.parent,
972 TextureInitRange::from(view.selector.clone()),
973 );
974 }
975 divergent_discarded_depth_stencil_aspect = Some((
976 if at.depth.store_op == StoreOp::Discard {
977 wgt::TextureAspect::DepthOnly
978 } else {
979 wgt::TextureAspect::StencilOnly
980 },
981 view,
982 ));
983 } else if at.depth.store_op == StoreOp::Discard {
984 discarded_surfaces.push(TextureSurfaceDiscard {
986 texture: view.parent.clone(),
987 mip_level: view.selector.mips.start,
988 layer: view.selector.layers.start,
989 });
990 }
991 }
992
993 (is_depth_read_only, is_stencil_read_only) = at.depth_stencil_read_only(ds_aspects)?;
994
995 let usage = if is_depth_read_only
996 && is_stencil_read_only
997 && device
998 .downlevel
999 .flags
1000 .contains(wgt::DownlevelFlags::READ_ONLY_DEPTH_STENCIL)
1001 {
1002 hal::TextureUses::DEPTH_STENCIL_READ | hal::TextureUses::RESOURCE
1003 } else {
1004 hal::TextureUses::DEPTH_STENCIL_WRITE
1005 };
1006 render_attachments.push(view.to_render_attachment(usage));
1007
1008 depth_stencil = Some(hal::DepthStencilAttachment {
1009 target: hal::Attachment {
1010 view: view
1011 .raw(snatch_guard)
1012 .ok_or_else(|| RenderPassErrorInner::InvalidAttachment(view.info.id()))?,
1013 usage,
1014 },
1015 depth_ops: at.depth.hal_ops(),
1016 stencil_ops: at.stencil.hal_ops(),
1017 clear_value: (at.depth.clear_value, at.stencil.clear_value),
1018 });
1019 }
1020
1021 for (index, attachment) in color_attachments.iter().enumerate() {
1022 let at = if let Some(attachment) = attachment.as_ref() {
1023 attachment
1024 } else {
1025 colors.push(None);
1026 continue;
1027 };
1028 let color_view: &TextureView<A> = trackers
1029 .views
1030 .add_single(view_guard, at.view)
1031 .ok_or(RenderPassErrorInner::InvalidAttachment(at.view))?;
1032 check_multiview(color_view)?;
1033 add_view(
1034 color_view,
1035 AttachmentErrorLocation::Color {
1036 index,
1037 resolve: false,
1038 },
1039 )?;
1040
1041 if !color_view
1042 .desc
1043 .aspects()
1044 .contains(hal::FormatAspects::COLOR)
1045 {
1046 return Err(RenderPassErrorInner::ColorAttachment(
1047 ColorAttachmentError::InvalidFormat(color_view.desc.format),
1048 ));
1049 }
1050
1051 Self::add_pass_texture_init_actions(
1052 &at.channel,
1053 texture_memory_actions,
1054 color_view,
1055 &mut pending_discard_init_fixups,
1056 );
1057 render_attachments
1058 .push(color_view.to_render_attachment(hal::TextureUses::COLOR_TARGET));
1059
1060 let mut hal_resolve_target = None;
1061 if let Some(resolve_target) = at.resolve_target {
1062 let resolve_view: &TextureView<A> = trackers
1063 .views
1064 .add_single(view_guard, resolve_target)
1065 .ok_or(RenderPassErrorInner::InvalidAttachment(resolve_target))?;
1066
1067 check_multiview(resolve_view)?;
1068
1069 let resolve_location = AttachmentErrorLocation::Color {
1070 index,
1071 resolve: true,
1072 };
1073
1074 let render_extent = resolve_view.render_extent.map_err(|reason| {
1075 RenderPassErrorInner::TextureViewIsNotRenderable {
1076 location: resolve_location,
1077 reason,
1078 }
1079 })?;
1080 if color_view.render_extent.unwrap() != render_extent {
1081 return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
1082 expected_location: attachment_location,
1083 expected_extent: extent.unwrap_or_default(),
1084 actual_location: resolve_location,
1085 actual_extent: render_extent,
1086 });
1087 }
1088 if color_view.samples == 1 || resolve_view.samples != 1 {
1089 return Err(RenderPassErrorInner::InvalidResolveSampleCounts {
1090 location: resolve_location,
1091 src: color_view.samples,
1092 dst: resolve_view.samples,
1093 });
1094 }
1095 if color_view.desc.format != resolve_view.desc.format {
1096 return Err(RenderPassErrorInner::MismatchedResolveTextureFormat {
1097 location: resolve_location,
1098 src: color_view.desc.format,
1099 dst: resolve_view.desc.format,
1100 });
1101 }
1102 if !resolve_view
1103 .format_features
1104 .flags
1105 .contains(wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE)
1106 {
1107 return Err(RenderPassErrorInner::UnsupportedResolveTargetFormat {
1108 location: resolve_location,
1109 format: resolve_view.desc.format,
1110 });
1111 }
1112
1113 texture_memory_actions.register_implicit_init(
1114 &resolve_view.parent,
1115 TextureInitRange::from(resolve_view.selector.clone()),
1116 );
1117 render_attachments
1118 .push(resolve_view.to_render_attachment(hal::TextureUses::COLOR_TARGET));
1119
1120 hal_resolve_target = Some(hal::Attachment {
1121 view: resolve_view.raw(snatch_guard).ok_or_else(|| {
1122 RenderPassErrorInner::InvalidResolveTarget(resolve_view.info.id())
1123 })?,
1124 usage: hal::TextureUses::COLOR_TARGET,
1125 });
1126 }
1127
1128 colors.push(Some(hal::ColorAttachment {
1129 target: hal::Attachment {
1130 view: color_view.raw(snatch_guard).ok_or_else(|| {
1131 RenderPassErrorInner::InvalidAttachment(color_view.info.id())
1132 })?,
1133 usage: hal::TextureUses::COLOR_TARGET,
1134 },
1135 resolve_target: hal_resolve_target,
1136 ops: at.channel.hal_ops(),
1137 clear_value: at.channel.clear_value,
1138 }));
1139 }
1140
1141 let extent = extent.ok_or(RenderPassErrorInner::MissingAttachments)?;
1142 let multiview = detected_multiview.expect("Multiview was not detected, no attachments");
1143
1144 let view_data = AttachmentData {
1145 colors: color_attachments
1146 .iter()
1147 .map(|at| at.as_ref().map(|at| view_guard.get(at.view).unwrap()))
1148 .collect(),
1149 resolves: color_attachments
1150 .iter()
1151 .filter_map(|at| match *at {
1152 Some(RenderPassColorAttachment {
1153 resolve_target: Some(resolve),
1154 ..
1155 }) => Some(view_guard.get(resolve).unwrap()),
1156 _ => None,
1157 })
1158 .collect(),
1159 depth_stencil: depth_stencil_attachment.map(|at| view_guard.get(at.view).unwrap()),
1160 };
1161
1162 let context = RenderPassContext {
1163 attachments: view_data.map(|view| view.desc.format),
1164 sample_count,
1165 multiview,
1166 };
1167
1168 let timestamp_writes = if let Some(tw) = timestamp_writes {
1169 let query_set = trackers
1170 .query_sets
1171 .add_single(query_set_guard, tw.query_set)
1172 .ok_or(RenderPassErrorInner::InvalidQuerySet(tw.query_set))?;
1173
1174 if let Some(index) = tw.beginning_of_pass_write_index {
1175 pending_query_resets.use_query_set(tw.query_set, query_set, index);
1176 }
1177 if let Some(index) = tw.end_of_pass_write_index {
1178 pending_query_resets.use_query_set(tw.query_set, query_set, index);
1179 }
1180
1181 Some(hal::RenderPassTimestampWrites {
1182 query_set: query_set.raw.as_ref().unwrap(),
1183 beginning_of_pass_write_index: tw.beginning_of_pass_write_index,
1184 end_of_pass_write_index: tw.end_of_pass_write_index,
1185 })
1186 } else {
1187 None
1188 };
1189
1190 let occlusion_query_set = if let Some(occlusion_query_set) = occlusion_query_set {
1191 let query_set = trackers
1192 .query_sets
1193 .add_single(query_set_guard, occlusion_query_set)
1194 .ok_or(RenderPassErrorInner::InvalidQuerySet(occlusion_query_set))?;
1195
1196 Some(query_set.raw.as_ref().unwrap())
1197 } else {
1198 None
1199 };
1200
1201 let hal_desc = hal::RenderPassDescriptor {
1202 label: hal_label(label, device.instance_flags),
1203 extent,
1204 sample_count,
1205 color_attachments: &colors,
1206 depth_stencil_attachment: depth_stencil,
1207 multiview,
1208 timestamp_writes,
1209 occlusion_query_set,
1210 };
1211 unsafe {
1212 encoder.raw.begin_render_pass(&hal_desc);
1213 };
1214
1215 Ok(Self {
1216 context,
1217 usage_scope: device.new_usage_scope(),
1218 render_attachments,
1219 is_depth_read_only,
1220 is_stencil_read_only,
1221 extent,
1222 _phantom: PhantomData,
1223 pending_discard_init_fixups,
1224 divergent_discarded_depth_stencil_aspect,
1225 multiview,
1226 })
1227 }
1228
1229 fn finish(
1230 mut self,
1231 raw: &mut A::CommandEncoder,
1232 snatch_guard: &SnatchGuard,
1233 ) -> Result<(UsageScope<'d, A>, SurfacesInDiscardState<A>), RenderPassErrorInner> {
1234 profiling::scope!("RenderPassInfo::finish");
1235 unsafe {
1236 raw.end_render_pass();
1237 }
1238
1239 for ra in self.render_attachments {
1240 let texture = &ra.texture;
1241 check_texture_usage(texture.desc.usage, TextureUsages::RENDER_ATTACHMENT)?;
1242
1243 unsafe {
1245 self.usage_scope
1246 .textures
1247 .merge_single(texture, Some(ra.selector.clone()), ra.usage)
1248 .map_err(UsageConflict::from)?
1249 };
1250 }
1251
1252 if let Some((aspect, view)) = self.divergent_discarded_depth_stencil_aspect {
1262 let (depth_ops, stencil_ops) = if aspect == wgt::TextureAspect::DepthOnly {
1263 (
1264 hal::AttachmentOps::STORE, hal::AttachmentOps::LOAD | hal::AttachmentOps::STORE, )
1267 } else {
1268 (
1269 hal::AttachmentOps::LOAD | hal::AttachmentOps::STORE, hal::AttachmentOps::STORE, )
1272 };
1273 let desc = hal::RenderPassDescriptor {
1274 label: Some("(wgpu internal) Zero init discarded depth/stencil aspect"),
1275 extent: view.render_extent.unwrap(),
1276 sample_count: view.samples,
1277 color_attachments: &[],
1278 depth_stencil_attachment: Some(hal::DepthStencilAttachment {
1279 target: hal::Attachment {
1280 view: view.raw(snatch_guard).ok_or_else(|| {
1281 RenderPassErrorInner::InvalidAttachment(view.info.id())
1282 })?,
1283 usage: hal::TextureUses::DEPTH_STENCIL_WRITE,
1284 },
1285 depth_ops,
1286 stencil_ops,
1287 clear_value: (0.0, 0),
1288 }),
1289 multiview: self.multiview,
1290 timestamp_writes: None,
1291 occlusion_query_set: None,
1292 };
1293 unsafe {
1294 raw.begin_render_pass(&desc);
1295 raw.end_render_pass();
1296 }
1297 }
1298
1299 Ok((self.usage_scope, self.pending_discard_init_fixups))
1300 }
1301}
1302
1303impl Global {
1306 pub fn command_encoder_run_render_pass<A: HalApi>(
1307 &self,
1308 encoder_id: id::CommandEncoderId,
1309 pass: &RenderPass,
1310 ) -> Result<(), RenderPassError> {
1311 self.command_encoder_run_render_pass_impl::<A>(
1312 encoder_id,
1313 pass.base.as_ref(),
1314 &pass.color_targets,
1315 pass.depth_stencil_target.as_ref(),
1316 pass.timestamp_writes.as_ref(),
1317 pass.occlusion_query_set_id,
1318 )
1319 }
1320
1321 #[doc(hidden)]
1322 pub fn command_encoder_run_render_pass_impl<A: HalApi>(
1323 &self,
1324 encoder_id: id::CommandEncoderId,
1325 base: BasePassRef<RenderCommand>,
1326 color_attachments: &[Option<RenderPassColorAttachment>],
1327 depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>,
1328 timestamp_writes: Option<&RenderPassTimestampWrites>,
1329 occlusion_query_set_id: Option<id::QuerySetId>,
1330 ) -> Result<(), RenderPassError> {
1331 profiling::scope!(
1332 "CommandEncoder::run_render_pass {}",
1333 base.label.unwrap_or("")
1334 );
1335
1336 let discard_hal_labels = self
1337 .instance
1338 .flags
1339 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS);
1340 let label = hal_label(base.label, self.instance.flags);
1341
1342 let pass_scope = PassErrorScope::Pass(encoder_id);
1343
1344 let hub = A::hub(self);
1345
1346 let cmd_buf: Arc<CommandBuffer<A>> =
1347 CommandBuffer::get_encoder(hub, encoder_id).map_pass_err(pass_scope)?;
1348 let device = &cmd_buf.device;
1349 let snatch_guard = device.snatchable_lock.read();
1350
1351 let (scope, pending_discard_init_fixups) = {
1352 let mut cmd_buf_data = cmd_buf.data.lock();
1353 let cmd_buf_data = cmd_buf_data.as_mut().unwrap();
1354
1355 #[cfg(feature = "trace")]
1356 if let Some(ref mut list) = cmd_buf_data.commands {
1357 list.push(crate::device::trace::Command::RunRenderPass {
1358 base: BasePass::from_ref(base),
1359 target_colors: color_attachments.to_vec(),
1360 target_depth_stencil: depth_stencil_attachment.cloned(),
1361 timestamp_writes: timestamp_writes.cloned(),
1362 occlusion_query_set_id,
1363 });
1364 }
1365
1366 if !device.is_valid() {
1367 return Err(DeviceError::Lost).map_pass_err(pass_scope);
1368 }
1369
1370 let encoder = &mut cmd_buf_data.encoder;
1371 let status = &mut cmd_buf_data.status;
1372 let tracker = &mut cmd_buf_data.trackers;
1373 let buffer_memory_init_actions = &mut cmd_buf_data.buffer_memory_init_actions;
1374 let texture_memory_actions = &mut cmd_buf_data.texture_memory_actions;
1375 let pending_query_resets = &mut cmd_buf_data.pending_query_resets;
1376
1377 encoder.close().map_pass_err(pass_scope)?;
1381 *status = CommandEncoderStatus::Error;
1383 encoder.open_pass(label).map_pass_err(pass_scope)?;
1384
1385 let bundle_guard = hub.render_bundles.read();
1386 let bind_group_guard = hub.bind_groups.read();
1387 let render_pipeline_guard = hub.render_pipelines.read();
1388 let query_set_guard = hub.query_sets.read();
1389 let buffer_guard = hub.buffers.read();
1390 let view_guard = hub.texture_views.read();
1391
1392 log::trace!(
1393 "Encoding render pass begin in command buffer {:?}",
1394 encoder_id
1395 );
1396
1397 let mut info = RenderPassInfo::start(
1398 device,
1399 label,
1400 color_attachments,
1401 depth_stencil_attachment,
1402 timestamp_writes,
1403 occlusion_query_set_id,
1404 encoder,
1405 tracker,
1406 texture_memory_actions,
1407 pending_query_resets,
1408 &*view_guard,
1409 &*query_set_guard,
1410 &snatch_guard,
1411 )
1412 .map_pass_err(pass_scope)?;
1413
1414 let indices = &device.tracker_indices;
1415 tracker.buffers.set_size(indices.buffers.size());
1416 tracker.textures.set_size(indices.textures.size());
1417 tracker.views.set_size(indices.texture_views.size());
1418 tracker.bind_groups.set_size(indices.bind_groups.size());
1419 tracker
1420 .render_pipelines
1421 .set_size(indices.render_pipelines.size());
1422 tracker.bundles.set_size(indices.bundles.size());
1423 tracker.query_sets.set_size(indices.query_sets.size());
1424
1425 let raw = &mut encoder.raw;
1426
1427 let mut state = State {
1428 pipeline_flags: PipelineFlags::empty(),
1429 binder: Binder::new(),
1430 blend_constant: OptionalState::Unused,
1431 stencil_reference: 0,
1432 pipeline: None,
1433 index: IndexState::default(),
1434 vertex: VertexState::default(),
1435 debug_scope_depth: 0,
1436 };
1437 let mut temp_offsets = Vec::new();
1438 let mut dynamic_offset_count = 0;
1439 let mut string_offset = 0;
1440 let mut active_query = None;
1441
1442 for command in base.commands {
1443 match *command {
1444 RenderCommand::SetBindGroup {
1445 index,
1446 num_dynamic_offsets,
1447 bind_group_id,
1448 } => {
1449 api_log!("RenderPass::set_bind_group {index} {bind_group_id:?}");
1450
1451 let scope = PassErrorScope::SetBindGroup(bind_group_id);
1452 let max_bind_groups = device.limits.max_bind_groups;
1453 if index >= max_bind_groups {
1454 return Err(RenderCommandError::BindGroupIndexOutOfRange {
1455 index,
1456 max: max_bind_groups,
1457 })
1458 .map_pass_err(scope);
1459 }
1460
1461 temp_offsets.clear();
1462 temp_offsets.extend_from_slice(
1463 &base.dynamic_offsets
1464 [dynamic_offset_count..dynamic_offset_count + num_dynamic_offsets],
1465 );
1466 dynamic_offset_count += num_dynamic_offsets;
1467
1468 let bind_group = tracker
1469 .bind_groups
1470 .add_single(&*bind_group_guard, bind_group_id)
1471 .ok_or(RenderCommandError::InvalidBindGroup(bind_group_id))
1472 .map_pass_err(scope)?;
1473
1474 if bind_group.device.as_info().id() != device.as_info().id() {
1475 return Err(DeviceError::WrongDevice).map_pass_err(scope);
1476 }
1477
1478 bind_group
1479 .validate_dynamic_bindings(index, &temp_offsets, &cmd_buf.limits)
1480 .map_pass_err(scope)?;
1481
1482 unsafe {
1484 info.usage_scope
1485 .merge_bind_group(&bind_group.used)
1486 .map_pass_err(scope)?;
1487 }
1488 buffer_memory_init_actions.extend(
1492 bind_group.used_buffer_ranges.iter().filter_map(|action| {
1493 action
1494 .buffer
1495 .initialization_status
1496 .read()
1497 .check_action(action)
1498 }),
1499 );
1500 for action in bind_group.used_texture_ranges.iter() {
1501 info.pending_discard_init_fixups
1502 .extend(texture_memory_actions.register_init_action(action));
1503 }
1504
1505 let pipeline_layout = state.binder.pipeline_layout.clone();
1506 let entries =
1507 state
1508 .binder
1509 .assign_group(index as usize, bind_group, &temp_offsets);
1510 if !entries.is_empty() && pipeline_layout.is_some() {
1511 let pipeline_layout = pipeline_layout.as_ref().unwrap().raw();
1512 for (i, e) in entries.iter().enumerate() {
1513 if let Some(group) = e.group.as_ref() {
1514 let raw_bg = group
1515 .raw(&snatch_guard)
1516 .ok_or(RenderPassErrorInner::InvalidBindGroup(i))
1517 .map_pass_err(scope)?;
1518 unsafe {
1519 raw.set_bind_group(
1520 pipeline_layout,
1521 index + i as u32,
1522 raw_bg,
1523 &e.dynamic_offsets,
1524 );
1525 }
1526 }
1527 }
1528 }
1529 }
1530 RenderCommand::SetPipeline(pipeline_id) => {
1531 api_log!("RenderPass::set_pipeline {pipeline_id:?}");
1532
1533 let scope = PassErrorScope::SetPipelineRender(pipeline_id);
1534 state.pipeline = Some(pipeline_id);
1535
1536 let pipeline: &pipeline::RenderPipeline<A> = tracker
1537 .render_pipelines
1538 .add_single(&*render_pipeline_guard, pipeline_id)
1539 .ok_or(RenderCommandError::InvalidPipeline(pipeline_id))
1540 .map_pass_err(scope)?;
1541
1542 if pipeline.device.as_info().id() != device.as_info().id() {
1543 return Err(DeviceError::WrongDevice).map_pass_err(scope);
1544 }
1545
1546 info.context
1547 .check_compatible(
1548 &pipeline.pass_context,
1549 RenderPassCompatibilityCheckType::RenderPipeline,
1550 )
1551 .map_err(RenderCommandError::IncompatiblePipelineTargets)
1552 .map_pass_err(scope)?;
1553
1554 state.pipeline_flags = pipeline.flags;
1555
1556 if (pipeline.flags.contains(PipelineFlags::WRITES_DEPTH)
1557 && info.is_depth_read_only)
1558 || (pipeline.flags.contains(PipelineFlags::WRITES_STENCIL)
1559 && info.is_stencil_read_only)
1560 {
1561 return Err(RenderCommandError::IncompatiblePipelineRods)
1562 .map_pass_err(scope);
1563 }
1564
1565 state
1566 .blend_constant
1567 .require(pipeline.flags.contains(PipelineFlags::BLEND_CONSTANT));
1568
1569 unsafe {
1570 raw.set_render_pipeline(pipeline.raw());
1571 }
1572
1573 if pipeline.flags.contains(PipelineFlags::STENCIL_REFERENCE) {
1574 unsafe {
1575 raw.set_stencil_reference(state.stencil_reference);
1576 }
1577 }
1578
1579 if state.binder.pipeline_layout.is_none()
1581 || !state
1582 .binder
1583 .pipeline_layout
1584 .as_ref()
1585 .unwrap()
1586 .is_equal(&pipeline.layout)
1587 {
1588 let (start_index, entries) = state.binder.change_pipeline_layout(
1589 &pipeline.layout,
1590 &pipeline.late_sized_buffer_groups,
1591 );
1592 if !entries.is_empty() {
1593 for (i, e) in entries.iter().enumerate() {
1594 if let Some(group) = e.group.as_ref() {
1595 let raw_bg = group
1596 .raw(&snatch_guard)
1597 .ok_or(RenderPassErrorInner::InvalidBindGroup(i))
1598 .map_pass_err(scope)?;
1599 unsafe {
1600 raw.set_bind_group(
1601 pipeline.layout.raw(),
1602 start_index as u32 + i as u32,
1603 raw_bg,
1604 &e.dynamic_offsets,
1605 );
1606 }
1607 }
1608 }
1609 }
1610
1611 let non_overlapping = super::bind::compute_nonoverlapping_ranges(
1613 &pipeline.layout.push_constant_ranges,
1614 );
1615 for range in non_overlapping {
1616 let offset = range.range.start;
1617 let size_bytes = range.range.end - offset;
1618 super::push_constant_clear(
1619 offset,
1620 size_bytes,
1621 |clear_offset, clear_data| unsafe {
1622 raw.set_push_constants(
1623 pipeline.layout.raw(),
1624 range.stages,
1625 clear_offset,
1626 clear_data,
1627 );
1628 },
1629 );
1630 }
1631 }
1632
1633 state.index.pipeline_format = pipeline.strip_index_format;
1634
1635 let vertex_steps_len = pipeline.vertex_steps.len();
1636 state.vertex.buffers_required = vertex_steps_len as u32;
1637
1638 while state.vertex.inputs.len() < vertex_steps_len {
1644 state.vertex.inputs.push(VertexBufferState::EMPTY);
1645 }
1646
1647 let mut steps = pipeline.vertex_steps.iter();
1649 for input in state.vertex.inputs.iter_mut() {
1650 input.step = steps.next().cloned().unwrap_or_default();
1651 }
1652
1653 state.vertex.update_limits();
1655 }
1656 RenderCommand::SetIndexBuffer {
1657 buffer_id,
1658 index_format,
1659 offset,
1660 size,
1661 } => {
1662 api_log!("RenderPass::set_index_buffer {buffer_id:?}");
1663
1664 let scope = PassErrorScope::SetIndexBuffer(buffer_id);
1665 let buffer = info
1666 .usage_scope
1667 .buffers
1668 .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDEX)
1669 .map_pass_err(scope)?;
1670
1671 if buffer.device.as_info().id() != device.as_info().id() {
1672 return Err(DeviceError::WrongDevice).map_pass_err(scope);
1673 }
1674
1675 check_buffer_usage(buffer_id, buffer.usage, BufferUsages::INDEX)
1676 .map_pass_err(scope)?;
1677 let buf_raw = buffer
1678 .raw
1679 .get(&snatch_guard)
1680 .ok_or(RenderCommandError::DestroyedBuffer(buffer_id))
1681 .map_pass_err(scope)?;
1682
1683 let end = match size {
1684 Some(s) => offset + s.get(),
1685 None => buffer.size,
1686 };
1687 state.index.bound_buffer_view = Some((buffer_id, offset..end));
1688
1689 state.index.format = Some(index_format);
1690 state.index.update_limit();
1691
1692 buffer_memory_init_actions.extend(
1693 buffer.initialization_status.read().create_action(
1694 buffer,
1695 offset..end,
1696 MemoryInitKind::NeedsInitializedMemory,
1697 ),
1698 );
1699
1700 let bb = hal::BufferBinding {
1701 buffer: buf_raw,
1702 offset,
1703 size,
1704 };
1705 unsafe {
1706 raw.set_index_buffer(bb, index_format);
1707 }
1708 }
1709 RenderCommand::SetVertexBuffer {
1710 slot,
1711 buffer_id,
1712 offset,
1713 size,
1714 } => {
1715 api_log!("RenderPass::set_vertex_buffer {slot} {buffer_id:?}");
1716
1717 let scope = PassErrorScope::SetVertexBuffer(buffer_id);
1718 let buffer = info
1719 .usage_scope
1720 .buffers
1721 .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::VERTEX)
1722 .map_pass_err(scope)?;
1723
1724 if buffer.device.as_info().id() != device.as_info().id() {
1725 return Err(DeviceError::WrongDevice).map_pass_err(scope);
1726 }
1727
1728 let max_vertex_buffers = device.limits.max_vertex_buffers;
1729 if slot >= max_vertex_buffers {
1730 return Err(RenderCommandError::VertexBufferIndexOutOfRange {
1731 index: slot,
1732 max: max_vertex_buffers,
1733 })
1734 .map_pass_err(scope);
1735 }
1736
1737 check_buffer_usage(buffer_id, buffer.usage, BufferUsages::VERTEX)
1738 .map_pass_err(scope)?;
1739 let buf_raw = buffer
1740 .raw
1741 .get(&snatch_guard)
1742 .ok_or(RenderCommandError::DestroyedBuffer(buffer_id))
1743 .map_pass_err(scope)?;
1744
1745 let empty_slots =
1746 (1 + slot as usize).saturating_sub(state.vertex.inputs.len());
1747 state
1748 .vertex
1749 .inputs
1750 .extend(iter::repeat(VertexBufferState::EMPTY).take(empty_slots));
1751 let vertex_state = &mut state.vertex.inputs[slot as usize];
1752 vertex_state.total_size = match size {
1754 Some(s) => s.get(),
1755 None => buffer.size - offset,
1756 };
1757 vertex_state.bound = true;
1758
1759 buffer_memory_init_actions.extend(
1760 buffer.initialization_status.read().create_action(
1761 buffer,
1762 offset..(offset + vertex_state.total_size),
1763 MemoryInitKind::NeedsInitializedMemory,
1764 ),
1765 );
1766
1767 let bb = hal::BufferBinding {
1768 buffer: buf_raw,
1769 offset,
1770 size,
1771 };
1772 unsafe {
1773 raw.set_vertex_buffer(slot, bb);
1774 }
1775 state.vertex.update_limits();
1776 }
1777 RenderCommand::SetBlendConstant(ref color) => {
1778 api_log!("RenderPass::set_blend_constant");
1779
1780 state.blend_constant = OptionalState::Set;
1781 let array = [
1782 color.r as f32,
1783 color.g as f32,
1784 color.b as f32,
1785 color.a as f32,
1786 ];
1787 unsafe {
1788 raw.set_blend_constants(&array);
1789 }
1790 }
1791 RenderCommand::SetStencilReference(value) => {
1792 api_log!("RenderPass::set_stencil_reference {value}");
1793
1794 state.stencil_reference = value;
1795 if state
1796 .pipeline_flags
1797 .contains(PipelineFlags::STENCIL_REFERENCE)
1798 {
1799 unsafe {
1800 raw.set_stencil_reference(value);
1801 }
1802 }
1803 }
1804 RenderCommand::SetViewport {
1805 ref rect,
1806 depth_min,
1807 depth_max,
1808 } => {
1809 api_log!("RenderPass::set_viewport {rect:?}");
1810
1811 let scope = PassErrorScope::SetViewport;
1812 if rect.x < 0.0
1813 || rect.y < 0.0
1814 || rect.w <= 0.0
1815 || rect.h <= 0.0
1816 || rect.x + rect.w > info.extent.width as f32
1817 || rect.y + rect.h > info.extent.height as f32
1818 {
1819 return Err(RenderCommandError::InvalidViewportRect(
1820 *rect,
1821 info.extent,
1822 ))
1823 .map_pass_err(scope);
1824 }
1825 if !(0.0..=1.0).contains(&depth_min) || !(0.0..=1.0).contains(&depth_max) {
1826 return Err(RenderCommandError::InvalidViewportDepth(
1827 depth_min, depth_max,
1828 ))
1829 .map_pass_err(scope);
1830 }
1831 let r = hal::Rect {
1832 x: rect.x,
1833 y: rect.y,
1834 w: rect.w,
1835 h: rect.h,
1836 };
1837 unsafe {
1838 raw.set_viewport(&r, depth_min..depth_max);
1839 }
1840 }
1841 RenderCommand::SetPushConstant {
1842 stages,
1843 offset,
1844 size_bytes,
1845 values_offset,
1846 } => {
1847 api_log!("RenderPass::set_push_constants");
1848
1849 let scope = PassErrorScope::SetPushConstant;
1850 let values_offset = values_offset
1851 .ok_or(RenderPassErrorInner::InvalidValuesOffset)
1852 .map_pass_err(scope)?;
1853
1854 let end_offset_bytes = offset + size_bytes;
1855 let values_end_offset =
1856 (values_offset + size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT) as usize;
1857 let data_slice =
1858 &base.push_constant_data[(values_offset as usize)..values_end_offset];
1859
1860 let pipeline_layout = state
1861 .binder
1862 .pipeline_layout
1863 .as_ref()
1864 .ok_or(DrawError::MissingPipeline)
1865 .map_pass_err(scope)?;
1866
1867 pipeline_layout
1868 .validate_push_constant_ranges(stages, offset, end_offset_bytes)
1869 .map_err(RenderCommandError::from)
1870 .map_pass_err(scope)?;
1871
1872 unsafe {
1873 raw.set_push_constants(
1874 pipeline_layout.raw(),
1875 stages,
1876 offset,
1877 data_slice,
1878 )
1879 }
1880 }
1881 RenderCommand::SetScissor(ref rect) => {
1882 api_log!("RenderPass::set_scissor_rect {rect:?}");
1883
1884 let scope = PassErrorScope::SetScissorRect;
1885 if rect.x + rect.w > info.extent.width
1886 || rect.y + rect.h > info.extent.height
1887 {
1888 return Err(RenderCommandError::InvalidScissorRect(*rect, info.extent))
1889 .map_pass_err(scope);
1890 }
1891 let r = hal::Rect {
1892 x: rect.x,
1893 y: rect.y,
1894 w: rect.w,
1895 h: rect.h,
1896 };
1897 unsafe {
1898 raw.set_scissor_rect(&r);
1899 }
1900 }
1901 RenderCommand::Draw {
1902 vertex_count,
1903 instance_count,
1904 first_vertex,
1905 first_instance,
1906 } => {
1907 api_log!(
1908 "RenderPass::draw {vertex_count} {instance_count} {first_vertex} {first_instance}"
1909 );
1910
1911 let indexed = false;
1912 let scope = PassErrorScope::Draw {
1913 indexed,
1914 indirect: false,
1915 pipeline: state.pipeline,
1916 };
1917 state.is_ready(indexed).map_pass_err(scope)?;
1918
1919 let last_vertex = first_vertex as u64 + vertex_count as u64;
1920 let vertex_limit = state.vertex.vertex_limit;
1921 if last_vertex > vertex_limit {
1922 return Err(DrawError::VertexBeyondLimit {
1923 last_vertex,
1924 vertex_limit,
1925 slot: state.vertex.vertex_limit_slot,
1926 })
1927 .map_pass_err(scope);
1928 }
1929 let last_instance = first_instance as u64 + instance_count as u64;
1930 let instance_limit = state.vertex.instance_limit;
1931 if last_instance > instance_limit {
1932 return Err(DrawError::InstanceBeyondLimit {
1933 last_instance,
1934 instance_limit,
1935 slot: state.vertex.instance_limit_slot,
1936 })
1937 .map_pass_err(scope);
1938 }
1939
1940 unsafe {
1941 if instance_count > 0 && vertex_count > 0 {
1942 raw.draw(
1943 first_vertex,
1944 vertex_count,
1945 first_instance,
1946 instance_count,
1947 );
1948 }
1949 }
1950 }
1951 RenderCommand::DrawIndexed {
1952 index_count,
1953 instance_count,
1954 first_index,
1955 base_vertex,
1956 first_instance,
1957 } => {
1958 api_log!("RenderPass::draw_indexed {index_count} {instance_count} {first_index} {base_vertex} {first_instance}");
1959
1960 let indexed = true;
1961 let scope = PassErrorScope::Draw {
1962 indexed,
1963 indirect: false,
1964 pipeline: state.pipeline,
1965 };
1966 state.is_ready(indexed).map_pass_err(scope)?;
1967
1968 let last_index = first_index as u64 + index_count as u64;
1969 let index_limit = state.index.limit;
1970 if last_index > index_limit {
1971 return Err(DrawError::IndexBeyondLimit {
1972 last_index,
1973 index_limit,
1974 })
1975 .map_pass_err(scope);
1976 }
1977 let last_instance = first_instance as u64 + instance_count as u64;
1978 let instance_limit = state.vertex.instance_limit;
1979 if last_instance > instance_limit {
1980 return Err(DrawError::InstanceBeyondLimit {
1981 last_instance,
1982 instance_limit,
1983 slot: state.vertex.instance_limit_slot,
1984 })
1985 .map_pass_err(scope);
1986 }
1987
1988 unsafe {
1989 if instance_count > 0 && index_count > 0 {
1990 raw.draw_indexed(
1991 first_index,
1992 index_count,
1993 base_vertex,
1994 first_instance,
1995 instance_count,
1996 );
1997 }
1998 }
1999 }
2000 RenderCommand::MultiDrawIndirect {
2001 buffer_id,
2002 offset,
2003 count,
2004 indexed,
2005 } => {
2006 api_log!("RenderPass::draw_indirect (indexed:{indexed}) {buffer_id:?} {offset} {count:?}");
2007
2008 let scope = PassErrorScope::Draw {
2009 indexed,
2010 indirect: true,
2011 pipeline: state.pipeline,
2012 };
2013 state.is_ready(indexed).map_pass_err(scope)?;
2014
2015 let stride = match indexed {
2016 false => mem::size_of::<wgt::DrawIndirectArgs>(),
2017 true => mem::size_of::<wgt::DrawIndexedIndirectArgs>(),
2018 };
2019
2020 if count.is_some() {
2021 device
2022 .require_features(wgt::Features::MULTI_DRAW_INDIRECT)
2023 .map_pass_err(scope)?;
2024 }
2025 device
2026 .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
2027 .map_pass_err(scope)?;
2028
2029 let indirect_buffer = info
2030 .usage_scope
2031 .buffers
2032 .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
2033 .map_pass_err(scope)?;
2034 check_buffer_usage(
2035 buffer_id,
2036 indirect_buffer.usage,
2037 BufferUsages::INDIRECT,
2038 )
2039 .map_pass_err(scope)?;
2040 let indirect_raw = indirect_buffer
2041 .raw
2042 .get(&snatch_guard)
2043 .ok_or(RenderCommandError::DestroyedBuffer(buffer_id))
2044 .map_pass_err(scope)?;
2045
2046 let actual_count = count.map_or(1, |c| c.get());
2047
2048 let end_offset = offset + stride as u64 * actual_count as u64;
2049 if end_offset > indirect_buffer.size {
2050 return Err(RenderPassErrorInner::IndirectBufferOverrun {
2051 count,
2052 offset,
2053 end_offset,
2054 buffer_size: indirect_buffer.size,
2055 })
2056 .map_pass_err(scope);
2057 }
2058
2059 buffer_memory_init_actions.extend(
2060 indirect_buffer.initialization_status.read().create_action(
2061 indirect_buffer,
2062 offset..end_offset,
2063 MemoryInitKind::NeedsInitializedMemory,
2064 ),
2065 );
2066
2067 match indexed {
2068 false => unsafe {
2069 raw.draw_indirect(indirect_raw, offset, actual_count);
2070 },
2071 true => unsafe {
2072 raw.draw_indexed_indirect(indirect_raw, offset, actual_count);
2073 },
2074 }
2075 }
2076 RenderCommand::MultiDrawIndirectCount {
2077 buffer_id,
2078 offset,
2079 count_buffer_id,
2080 count_buffer_offset,
2081 max_count,
2082 indexed,
2083 } => {
2084 api_log!("RenderPass::multi_draw_indirect_count (indexed:{indexed}) {buffer_id:?} {offset} {count_buffer_id:?} {count_buffer_offset:?} {max_count:?}");
2085
2086 let scope = PassErrorScope::Draw {
2087 indexed,
2088 indirect: true,
2089 pipeline: state.pipeline,
2090 };
2091 state.is_ready(indexed).map_pass_err(scope)?;
2092
2093 let stride = match indexed {
2094 false => mem::size_of::<wgt::DrawIndirectArgs>(),
2095 true => mem::size_of::<wgt::DrawIndexedIndirectArgs>(),
2096 } as u64;
2097
2098 device
2099 .require_features(wgt::Features::MULTI_DRAW_INDIRECT_COUNT)
2100 .map_pass_err(scope)?;
2101 device
2102 .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
2103 .map_pass_err(scope)?;
2104
2105 let indirect_buffer = info
2106 .usage_scope
2107 .buffers
2108 .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
2109 .map_pass_err(scope)?;
2110 check_buffer_usage(
2111 buffer_id,
2112 indirect_buffer.usage,
2113 BufferUsages::INDIRECT,
2114 )
2115 .map_pass_err(scope)?;
2116 let indirect_raw = indirect_buffer
2117 .raw
2118 .get(&snatch_guard)
2119 .ok_or(RenderCommandError::DestroyedBuffer(buffer_id))
2120 .map_pass_err(scope)?;
2121
2122 let count_buffer = info
2123 .usage_scope
2124 .buffers
2125 .merge_single(
2126 &*buffer_guard,
2127 count_buffer_id,
2128 hal::BufferUses::INDIRECT,
2129 )
2130 .map_pass_err(scope)?;
2131 check_buffer_usage(buffer_id, count_buffer.usage, BufferUsages::INDIRECT)
2132 .map_pass_err(scope)?;
2133 let count_raw = count_buffer
2134 .raw
2135 .get(&snatch_guard)
2136 .ok_or(RenderCommandError::DestroyedBuffer(count_buffer_id))
2137 .map_pass_err(scope)?;
2138
2139 let end_offset = offset + stride * max_count as u64;
2140 if end_offset > indirect_buffer.size {
2141 return Err(RenderPassErrorInner::IndirectBufferOverrun {
2142 count: None,
2143 offset,
2144 end_offset,
2145 buffer_size: indirect_buffer.size,
2146 })
2147 .map_pass_err(scope);
2148 }
2149 buffer_memory_init_actions.extend(
2150 indirect_buffer.initialization_status.read().create_action(
2151 indirect_buffer,
2152 offset..end_offset,
2153 MemoryInitKind::NeedsInitializedMemory,
2154 ),
2155 );
2156
2157 let begin_count_offset = count_buffer_offset;
2158 let end_count_offset = count_buffer_offset + 4;
2159 if end_count_offset > count_buffer.size {
2160 return Err(RenderPassErrorInner::IndirectCountBufferOverrun {
2161 begin_count_offset,
2162 end_count_offset,
2163 count_buffer_size: count_buffer.size,
2164 })
2165 .map_pass_err(scope);
2166 }
2167 buffer_memory_init_actions.extend(
2168 count_buffer.initialization_status.read().create_action(
2169 count_buffer,
2170 count_buffer_offset..end_count_offset,
2171 MemoryInitKind::NeedsInitializedMemory,
2172 ),
2173 );
2174
2175 match indexed {
2176 false => unsafe {
2177 raw.draw_indirect_count(
2178 indirect_raw,
2179 offset,
2180 count_raw,
2181 count_buffer_offset,
2182 max_count,
2183 );
2184 },
2185 true => unsafe {
2186 raw.draw_indexed_indirect_count(
2187 indirect_raw,
2188 offset,
2189 count_raw,
2190 count_buffer_offset,
2191 max_count,
2192 );
2193 },
2194 }
2195 }
2196 RenderCommand::PushDebugGroup { color: _, len } => {
2197 state.debug_scope_depth += 1;
2198 if !discard_hal_labels {
2199 let label = str::from_utf8(
2200 &base.string_data[string_offset..string_offset + len],
2201 )
2202 .unwrap();
2203
2204 api_log!("RenderPass::push_debug_group {label:?}");
2205 unsafe {
2206 raw.begin_debug_marker(label);
2207 }
2208 }
2209 string_offset += len;
2210 }
2211 RenderCommand::PopDebugGroup => {
2212 api_log!("RenderPass::pop_debug_group");
2213
2214 let scope = PassErrorScope::PopDebugGroup;
2215 if state.debug_scope_depth == 0 {
2216 return Err(RenderPassErrorInner::InvalidPopDebugGroup)
2217 .map_pass_err(scope);
2218 }
2219 state.debug_scope_depth -= 1;
2220 if !discard_hal_labels {
2221 unsafe {
2222 raw.end_debug_marker();
2223 }
2224 }
2225 }
2226 RenderCommand::InsertDebugMarker { color: _, len } => {
2227 if !discard_hal_labels {
2228 let label = str::from_utf8(
2229 &base.string_data[string_offset..string_offset + len],
2230 )
2231 .unwrap();
2232 api_log!("RenderPass::insert_debug_marker {label:?}");
2233 unsafe {
2234 raw.insert_debug_marker(label);
2235 }
2236 }
2237 string_offset += len;
2238 }
2239 RenderCommand::WriteTimestamp {
2240 query_set_id,
2241 query_index,
2242 } => {
2243 api_log!("RenderPass::write_timestamps {query_set_id:?} {query_index}");
2244 let scope = PassErrorScope::WriteTimestamp;
2245
2246 device
2247 .require_features(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES)
2248 .map_pass_err(scope)?;
2249
2250 let query_set = tracker
2251 .query_sets
2252 .add_single(&*query_set_guard, query_set_id)
2253 .ok_or(RenderCommandError::InvalidQuerySet(query_set_id))
2254 .map_pass_err(scope)?;
2255
2256 query_set
2257 .validate_and_write_timestamp(
2258 raw,
2259 query_set_id,
2260 query_index,
2261 Some(&mut cmd_buf_data.pending_query_resets),
2262 )
2263 .map_pass_err(scope)?;
2264 }
2265 RenderCommand::BeginOcclusionQuery { query_index } => {
2266 api_log!("RenderPass::begin_occlusion_query {query_index}");
2267 let scope = PassErrorScope::BeginOcclusionQuery;
2268
2269 let query_set_id = occlusion_query_set_id
2270 .ok_or(RenderPassErrorInner::MissingOcclusionQuerySet)
2271 .map_pass_err(scope)?;
2272
2273 let query_set = tracker
2274 .query_sets
2275 .add_single(&*query_set_guard, query_set_id)
2276 .ok_or(RenderCommandError::InvalidQuerySet(query_set_id))
2277 .map_pass_err(scope)?;
2278
2279 query_set
2280 .validate_and_begin_occlusion_query(
2281 raw,
2282 query_set_id,
2283 query_index,
2284 Some(&mut cmd_buf_data.pending_query_resets),
2285 &mut active_query,
2286 )
2287 .map_pass_err(scope)?;
2288 }
2289 RenderCommand::EndOcclusionQuery => {
2290 api_log!("RenderPass::end_occlusion_query");
2291 let scope = PassErrorScope::EndOcclusionQuery;
2292
2293 end_occlusion_query(raw, &*query_set_guard, &mut active_query)
2294 .map_pass_err(scope)?;
2295 }
2296 RenderCommand::BeginPipelineStatisticsQuery {
2297 query_set_id,
2298 query_index,
2299 } => {
2300 api_log!("RenderPass::begin_pipeline_statistics_query {query_set_id:?} {query_index}");
2301 let scope = PassErrorScope::BeginPipelineStatisticsQuery;
2302
2303 let query_set = tracker
2304 .query_sets
2305 .add_single(&*query_set_guard, query_set_id)
2306 .ok_or(RenderCommandError::InvalidQuerySet(query_set_id))
2307 .map_pass_err(scope)?;
2308
2309 query_set
2310 .validate_and_begin_pipeline_statistics_query(
2311 raw,
2312 query_set_id,
2313 query_index,
2314 Some(&mut cmd_buf_data.pending_query_resets),
2315 &mut active_query,
2316 )
2317 .map_pass_err(scope)?;
2318 }
2319 RenderCommand::EndPipelineStatisticsQuery => {
2320 api_log!("RenderPass::end_pipeline_statistics_query");
2321 let scope = PassErrorScope::EndPipelineStatisticsQuery;
2322
2323 end_pipeline_statistics_query(raw, &*query_set_guard, &mut active_query)
2324 .map_pass_err(scope)?;
2325 }
2326 RenderCommand::ExecuteBundle(bundle_id) => {
2327 api_log!("RenderPass::execute_bundle {bundle_id:?}");
2328 let scope = PassErrorScope::ExecuteBundle;
2329 let bundle: &command::RenderBundle<A> = tracker
2330 .bundles
2331 .add_single(&*bundle_guard, bundle_id)
2332 .ok_or(RenderCommandError::InvalidRenderBundle(bundle_id))
2333 .map_pass_err(scope)?;
2334
2335 if bundle.device.as_info().id() != device.as_info().id() {
2336 return Err(DeviceError::WrongDevice).map_pass_err(scope);
2337 }
2338
2339 info.context
2340 .check_compatible(
2341 &bundle.context,
2342 RenderPassCompatibilityCheckType::RenderBundle,
2343 )
2344 .map_err(RenderPassErrorInner::IncompatibleBundleTargets)
2345 .map_pass_err(scope)?;
2346
2347 if (info.is_depth_read_only && !bundle.is_depth_read_only)
2348 || (info.is_stencil_read_only && !bundle.is_stencil_read_only)
2349 {
2350 return Err(
2351 RenderPassErrorInner::IncompatibleBundleReadOnlyDepthStencil {
2352 pass_depth: info.is_depth_read_only,
2353 pass_stencil: info.is_stencil_read_only,
2354 bundle_depth: bundle.is_depth_read_only,
2355 bundle_stencil: bundle.is_stencil_read_only,
2356 },
2357 )
2358 .map_pass_err(scope);
2359 }
2360
2361 buffer_memory_init_actions.extend(
2362 bundle
2363 .buffer_memory_init_actions
2364 .iter()
2365 .filter_map(|action| {
2366 action
2367 .buffer
2368 .initialization_status
2369 .read()
2370 .check_action(action)
2371 }),
2372 );
2373 for action in bundle.texture_memory_init_actions.iter() {
2374 info.pending_discard_init_fixups
2375 .extend(texture_memory_actions.register_init_action(action));
2376 }
2377
2378 unsafe { bundle.execute(raw, &snatch_guard) }
2379 .map_err(|e| match e {
2380 ExecutionError::DestroyedBuffer(id) => {
2381 RenderCommandError::DestroyedBuffer(id)
2382 }
2383 ExecutionError::InvalidBindGroup(id) => {
2384 RenderCommandError::InvalidBindGroup(id)
2385 }
2386 ExecutionError::Unimplemented(what) => {
2387 RenderCommandError::Unimplemented(what)
2388 }
2389 })
2390 .map_pass_err(scope)?;
2391
2392 unsafe {
2393 info.usage_scope
2394 .merge_render_bundle(&bundle.used)
2395 .map_pass_err(scope)?;
2396 tracker
2397 .add_from_render_bundle(&bundle.used)
2398 .map_pass_err(scope)?;
2399 };
2400 state.reset_bundle();
2401 }
2402 }
2403 }
2404
2405 log::trace!("Merging renderpass into cmd_buf {:?}", encoder_id);
2406 let (trackers, pending_discard_init_fixups) =
2407 info.finish(raw, &snatch_guard).map_pass_err(pass_scope)?;
2408
2409 encoder.close().map_pass_err(pass_scope)?;
2410 (trackers, pending_discard_init_fixups)
2411 };
2412
2413 let cmd_buf = hub
2414 .command_buffers
2415 .get(encoder_id.into_command_buffer_id())
2416 .unwrap();
2417 let mut cmd_buf_data = cmd_buf.data.lock();
2418 let cmd_buf_data = cmd_buf_data.as_mut().unwrap();
2419
2420 let query_set_guard = hub.query_sets.read();
2421
2422 let encoder = &mut cmd_buf_data.encoder;
2423 let status = &mut cmd_buf_data.status;
2424 let tracker = &mut cmd_buf_data.trackers;
2425
2426 {
2427 let transit = encoder.open().map_pass_err(pass_scope)?;
2428
2429 fixup_discarded_surfaces(
2430 pending_discard_init_fixups.into_iter(),
2431 transit,
2432 &mut tracker.textures,
2433 &cmd_buf.device,
2434 &snatch_guard,
2435 );
2436
2437 cmd_buf_data
2438 .pending_query_resets
2439 .reset_queries(
2440 transit,
2441 &query_set_guard,
2442 cmd_buf.device.info.id().backend(),
2443 )
2444 .map_err(RenderCommandError::InvalidQuerySet)
2445 .map_pass_err(PassErrorScope::QueryReset)?;
2446
2447 super::CommandBuffer::insert_barriers_from_scope(
2448 transit,
2449 tracker,
2450 &scope,
2451 &snatch_guard,
2452 );
2453 }
2454
2455 *status = CommandEncoderStatus::Recording;
2456 encoder.close_and_swap().map_pass_err(pass_scope)?;
2457
2458 Ok(())
2459 }
2460}
2461
2462pub mod render_commands {
2463 use super::{
2464 super::{Rect, RenderCommand},
2465 RenderPass,
2466 };
2467 use crate::id;
2468 use std::{convert::TryInto, num::NonZeroU32};
2469 use wgt::{BufferAddress, BufferSize, Color, DynamicOffset, IndexFormat};
2470
2471 pub fn wgpu_render_pass_set_bind_group(
2476 pass: &mut RenderPass,
2477 index: u32,
2478 bind_group_id: id::BindGroupId,
2479 offsets: &[DynamicOffset],
2480 ) {
2481 let redundant = pass.current_bind_groups.set_and_check_redundant(
2482 bind_group_id,
2483 index,
2484 &mut pass.base.dynamic_offsets,
2485 offsets,
2486 );
2487
2488 if redundant {
2489 return;
2490 }
2491
2492 pass.base.commands.push(RenderCommand::SetBindGroup {
2493 index,
2494 num_dynamic_offsets: offsets.len(),
2495 bind_group_id,
2496 });
2497 }
2498
2499 pub fn wgpu_render_pass_set_pipeline(pass: &mut RenderPass, pipeline_id: id::RenderPipelineId) {
2500 if pass.current_pipeline.set_and_check_redundant(pipeline_id) {
2501 return;
2502 }
2503
2504 pass.base
2505 .commands
2506 .push(RenderCommand::SetPipeline(pipeline_id));
2507 }
2508
2509 pub fn wgpu_render_pass_set_vertex_buffer(
2510 pass: &mut RenderPass,
2511 slot: u32,
2512 buffer_id: id::BufferId,
2513 offset: BufferAddress,
2514 size: Option<BufferSize>,
2515 ) {
2516 pass.base.commands.push(RenderCommand::SetVertexBuffer {
2517 slot,
2518 buffer_id,
2519 offset,
2520 size,
2521 });
2522 }
2523
2524 pub fn wgpu_render_pass_set_index_buffer(
2525 pass: &mut RenderPass,
2526 buffer: id::BufferId,
2527 index_format: IndexFormat,
2528 offset: BufferAddress,
2529 size: Option<BufferSize>,
2530 ) {
2531 pass.set_index_buffer(buffer, index_format, offset, size);
2532 }
2533
2534 pub fn wgpu_render_pass_set_blend_constant(pass: &mut RenderPass, color: &Color) {
2535 pass.base
2536 .commands
2537 .push(RenderCommand::SetBlendConstant(*color));
2538 }
2539
2540 pub fn wgpu_render_pass_set_stencil_reference(pass: &mut RenderPass, value: u32) {
2541 pass.base
2542 .commands
2543 .push(RenderCommand::SetStencilReference(value));
2544 }
2545
2546 pub fn wgpu_render_pass_set_viewport(
2547 pass: &mut RenderPass,
2548 x: f32,
2549 y: f32,
2550 w: f32,
2551 h: f32,
2552 depth_min: f32,
2553 depth_max: f32,
2554 ) {
2555 pass.base.commands.push(RenderCommand::SetViewport {
2556 rect: Rect { x, y, w, h },
2557 depth_min,
2558 depth_max,
2559 });
2560 }
2561
2562 pub fn wgpu_render_pass_set_scissor_rect(
2563 pass: &mut RenderPass,
2564 x: u32,
2565 y: u32,
2566 w: u32,
2567 h: u32,
2568 ) {
2569 pass.base
2570 .commands
2571 .push(RenderCommand::SetScissor(Rect { x, y, w, h }));
2572 }
2573
2574 pub fn wgpu_render_pass_set_push_constants(
2579 pass: &mut RenderPass,
2580 stages: wgt::ShaderStages,
2581 offset: u32,
2582 data: &[u8],
2583 ) {
2584 assert_eq!(
2585 offset & (wgt::PUSH_CONSTANT_ALIGNMENT - 1),
2586 0,
2587 "Push constant offset must be aligned to 4 bytes."
2588 );
2589 assert_eq!(
2590 data.len() as u32 & (wgt::PUSH_CONSTANT_ALIGNMENT - 1),
2591 0,
2592 "Push constant size must be aligned to 4 bytes."
2593 );
2594 let value_offset = pass.base.push_constant_data.len().try_into().expect(
2595 "Ran out of push constant space. Don't set 4gb of push constants per RenderPass.",
2596 );
2597
2598 pass.base.push_constant_data.extend(
2599 data.chunks_exact(wgt::PUSH_CONSTANT_ALIGNMENT as usize)
2600 .map(|arr| u32::from_ne_bytes([arr[0], arr[1], arr[2], arr[3]])),
2601 );
2602
2603 pass.base.commands.push(RenderCommand::SetPushConstant {
2604 stages,
2605 offset,
2606 size_bytes: data.len() as u32,
2607 values_offset: Some(value_offset),
2608 });
2609 }
2610
2611 pub fn wgpu_render_pass_draw(
2612 pass: &mut RenderPass,
2613 vertex_count: u32,
2614 instance_count: u32,
2615 first_vertex: u32,
2616 first_instance: u32,
2617 ) {
2618 pass.base.commands.push(RenderCommand::Draw {
2619 vertex_count,
2620 instance_count,
2621 first_vertex,
2622 first_instance,
2623 });
2624 }
2625
2626 pub fn wgpu_render_pass_draw_indexed(
2627 pass: &mut RenderPass,
2628 index_count: u32,
2629 instance_count: u32,
2630 first_index: u32,
2631 base_vertex: i32,
2632 first_instance: u32,
2633 ) {
2634 pass.base.commands.push(RenderCommand::DrawIndexed {
2635 index_count,
2636 instance_count,
2637 first_index,
2638 base_vertex,
2639 first_instance,
2640 });
2641 }
2642
2643 pub fn wgpu_render_pass_draw_indirect(
2644 pass: &mut RenderPass,
2645 buffer_id: id::BufferId,
2646 offset: BufferAddress,
2647 ) {
2648 pass.base.commands.push(RenderCommand::MultiDrawIndirect {
2649 buffer_id,
2650 offset,
2651 count: None,
2652 indexed: false,
2653 });
2654 }
2655
2656 pub fn wgpu_render_pass_draw_indexed_indirect(
2657 pass: &mut RenderPass,
2658 buffer_id: id::BufferId,
2659 offset: BufferAddress,
2660 ) {
2661 pass.base.commands.push(RenderCommand::MultiDrawIndirect {
2662 buffer_id,
2663 offset,
2664 count: None,
2665 indexed: true,
2666 });
2667 }
2668
2669 pub fn wgpu_render_pass_multi_draw_indirect(
2670 pass: &mut RenderPass,
2671 buffer_id: id::BufferId,
2672 offset: BufferAddress,
2673 count: u32,
2674 ) {
2675 pass.base.commands.push(RenderCommand::MultiDrawIndirect {
2676 buffer_id,
2677 offset,
2678 count: NonZeroU32::new(count),
2679 indexed: false,
2680 });
2681 }
2682
2683 pub fn wgpu_render_pass_multi_draw_indexed_indirect(
2684 pass: &mut RenderPass,
2685 buffer_id: id::BufferId,
2686 offset: BufferAddress,
2687 count: u32,
2688 ) {
2689 pass.base.commands.push(RenderCommand::MultiDrawIndirect {
2690 buffer_id,
2691 offset,
2692 count: NonZeroU32::new(count),
2693 indexed: true,
2694 });
2695 }
2696
2697 pub fn wgpu_render_pass_multi_draw_indirect_count(
2698 pass: &mut RenderPass,
2699 buffer_id: id::BufferId,
2700 offset: BufferAddress,
2701 count_buffer_id: id::BufferId,
2702 count_buffer_offset: BufferAddress,
2703 max_count: u32,
2704 ) {
2705 pass.base
2706 .commands
2707 .push(RenderCommand::MultiDrawIndirectCount {
2708 buffer_id,
2709 offset,
2710 count_buffer_id,
2711 count_buffer_offset,
2712 max_count,
2713 indexed: false,
2714 });
2715 }
2716
2717 pub fn wgpu_render_pass_multi_draw_indexed_indirect_count(
2718 pass: &mut RenderPass,
2719 buffer_id: id::BufferId,
2720 offset: BufferAddress,
2721 count_buffer_id: id::BufferId,
2722 count_buffer_offset: BufferAddress,
2723 max_count: u32,
2724 ) {
2725 pass.base
2726 .commands
2727 .push(RenderCommand::MultiDrawIndirectCount {
2728 buffer_id,
2729 offset,
2730 count_buffer_id,
2731 count_buffer_offset,
2732 max_count,
2733 indexed: true,
2734 });
2735 }
2736
2737 pub fn wgpu_render_pass_push_debug_group(pass: &mut RenderPass, label: &str, color: u32) {
2738 let bytes = label.as_bytes();
2739 pass.base.string_data.extend_from_slice(bytes);
2740
2741 pass.base.commands.push(RenderCommand::PushDebugGroup {
2742 color,
2743 len: bytes.len(),
2744 });
2745 }
2746
2747 pub fn wgpu_render_pass_pop_debug_group(pass: &mut RenderPass) {
2748 pass.base.commands.push(RenderCommand::PopDebugGroup);
2749 }
2750
2751 pub fn wgpu_render_pass_insert_debug_marker(pass: &mut RenderPass, label: &str, color: u32) {
2752 let bytes = label.as_bytes();
2753 pass.base.string_data.extend_from_slice(bytes);
2754
2755 pass.base.commands.push(RenderCommand::InsertDebugMarker {
2756 color,
2757 len: bytes.len(),
2758 });
2759 }
2760
2761 pub fn wgpu_render_pass_write_timestamp(
2762 pass: &mut RenderPass,
2763 query_set_id: id::QuerySetId,
2764 query_index: u32,
2765 ) {
2766 pass.base.commands.push(RenderCommand::WriteTimestamp {
2767 query_set_id,
2768 query_index,
2769 });
2770 }
2771
2772 pub fn wgpu_render_pass_begin_occlusion_query(pass: &mut RenderPass, query_index: u32) {
2773 pass.base
2774 .commands
2775 .push(RenderCommand::BeginOcclusionQuery { query_index });
2776 }
2777
2778 pub fn wgpu_render_pass_end_occlusion_query(pass: &mut RenderPass) {
2779 pass.base.commands.push(RenderCommand::EndOcclusionQuery);
2780 }
2781
2782 pub fn wgpu_render_pass_begin_pipeline_statistics_query(
2783 pass: &mut RenderPass,
2784 query_set_id: id::QuerySetId,
2785 query_index: u32,
2786 ) {
2787 pass.base
2788 .commands
2789 .push(RenderCommand::BeginPipelineStatisticsQuery {
2790 query_set_id,
2791 query_index,
2792 });
2793 }
2794
2795 pub fn wgpu_render_pass_end_pipeline_statistics_query(pass: &mut RenderPass) {
2796 pass.base
2797 .commands
2798 .push(RenderCommand::EndPipelineStatisticsQuery);
2799 }
2800
2801 pub fn wgpu_render_pass_execute_bundles(
2802 pass: &mut RenderPass,
2803 render_bundle_ids: &[id::RenderBundleId],
2804 ) {
2805 for &bundle_id in render_bundle_ids {
2806 pass.base
2807 .commands
2808 .push(RenderCommand::ExecuteBundle(bundle_id));
2809 }
2810 pass.current_pipeline.reset();
2811 pass.current_bind_groups.reset();
2812 }
2813}