1use alloc::{string::String, sync::Arc, vec::Vec};
2use core::ops::Range;
3
4use crate::{
5 api_log,
6 command::{encoder::EncodingState, ArcCommand, EncoderStateError},
7 device::{DeviceError, MissingFeatures},
8 get_lowest_common_denom,
9 global::Global,
10 hal_label,
11 id::{BufferId, CommandEncoderId, TextureId},
12 init_tracker::{MemoryInitKind, TextureInitRange},
13 resource::{
14 Buffer, DestroyedResourceError, InvalidOrDestroyedResourceError, InvalidResourceError,
15 Labeled, MissingBufferUsageError, ParentDevice, RawResourceAccess, ResourceErrorIdent,
16 Texture, TextureClearMode,
17 },
18 snatch::SnatchGuard,
19 track::TextureTrackerSetSingle,
20};
21
22use thiserror::Error;
23use wgt::{
24 error::{ErrorType, WebGpuError},
25 math::align_to,
26 BufferAddress, BufferUsages, ImageSubresourceRange, TextureAspect, TextureSelector,
27};
28
29#[derive(Clone, Debug, Error)]
31#[non_exhaustive]
32pub enum ClearError {
33 #[error(transparent)]
34 DestroyedResource(#[from] DestroyedResourceError),
35 #[error(transparent)]
36 MissingFeatures(#[from] MissingFeatures),
37 #[error("{0} can not be cleared")]
38 NoValidTextureClearMode(ResourceErrorIdent),
39 #[error("Buffer clear size {0:?} is not a multiple of `COPY_BUFFER_ALIGNMENT`")]
40 UnalignedFillSize(BufferAddress),
41 #[error("Buffer offset {0:?} is not a multiple of `COPY_BUFFER_ALIGNMENT`")]
42 UnalignedBufferOffset(BufferAddress),
43 #[error("Clear starts at offset {start_offset} with size of {requested_size}, but these added together exceed `u64::MAX`")]
44 OffsetPlusSizeExceeds64BitBounds {
45 start_offset: BufferAddress,
46 requested_size: BufferAddress,
47 },
48 #[error("Clear of {start_offset}..{end_offset} would end up overrunning the bounds of the buffer of size {buffer_size}")]
49 BufferOverrun {
50 start_offset: BufferAddress,
51 end_offset: BufferAddress,
52 buffer_size: BufferAddress,
53 },
54 #[error(transparent)]
55 MissingBufferUsage(#[from] MissingBufferUsageError),
56 #[error("Texture lacks the aspects that were specified in the image subresource range. Texture with format {texture_format:?}, specified was {subresource_range_aspects:?}")]
57 MissingTextureAspect {
58 texture_format: wgt::TextureFormat,
59 subresource_range_aspects: TextureAspect,
60 },
61 #[error("Image subresource level range is outside of the texture's level range. texture range is {texture_level_range:?}, \
62whereas subesource range specified start {subresource_base_mip_level} and count {subresource_mip_level_count:?}")]
63 InvalidTextureLevelRange {
64 texture_level_range: Range<u32>,
65 subresource_base_mip_level: u32,
66 subresource_mip_level_count: Option<u32>,
67 },
68 #[error("Image subresource layer range is outside of the texture's layer range. texture range is {texture_layer_range:?}, \
69whereas subesource range specified start {subresource_base_array_layer} and count {subresource_array_layer_count:?}")]
70 InvalidTextureLayerRange {
71 texture_layer_range: Range<u32>,
72 subresource_base_array_layer: u32,
73 subresource_array_layer_count: Option<u32>,
74 },
75 #[error(transparent)]
76 Device(#[from] DeviceError),
77 #[error(transparent)]
78 EncoderState(#[from] EncoderStateError),
79 #[error(transparent)]
80 InvalidResource(#[from] InvalidResourceError),
81}
82
83impl From<InvalidOrDestroyedResourceError> for ClearError {
84 fn from(value: InvalidOrDestroyedResourceError) -> Self {
85 match value {
86 InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
87 InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
88 }
89 }
90}
91
92impl WebGpuError for ClearError {
93 fn webgpu_error_type(&self) -> ErrorType {
94 match self {
95 Self::DestroyedResource(e) => e.webgpu_error_type(),
96 Self::MissingFeatures(e) => e.webgpu_error_type(),
97 Self::MissingBufferUsage(e) => e.webgpu_error_type(),
98 Self::Device(e) => e.webgpu_error_type(),
99 Self::EncoderState(e) => e.webgpu_error_type(),
100 Self::InvalidResource(e) => e.webgpu_error_type(),
101 Self::NoValidTextureClearMode(..)
102 | Self::UnalignedFillSize(..)
103 | Self::UnalignedBufferOffset(..)
104 | Self::OffsetPlusSizeExceeds64BitBounds { .. }
105 | Self::BufferOverrun { .. }
106 | Self::MissingTextureAspect { .. }
107 | Self::InvalidTextureLevelRange { .. }
108 | Self::InvalidTextureLayerRange { .. } => ErrorType::Validation,
109 }
110 }
111}
112
113impl super::CommandEncoder {
114 pub fn clear_buffer(
115 self: &Arc<Self>,
116 dst: Arc<Buffer>,
117 offset: BufferAddress,
118 size: Option<BufferAddress>,
119 ) -> Result<(), EncoderStateError> {
120 profiling::scope!("CommandEncoder::clear_buffer");
121 api_log!("CommandEncoder::clear_buffer {:?}", Arc::as_ptr(&dst));
122
123 let mut cmd_buf_data = self.data.lock();
124
125 cmd_buf_data.push_with(|| -> Result<_, ClearError> {
126 dst.check_is_valid()?;
127 Ok(ArcCommand::ClearBuffer { dst, offset, size })
128 })
129 }
130
131 pub fn clear_texture(
132 self: &Arc<Self>,
133 dst: Arc<Texture>,
134 subresource_range: &ImageSubresourceRange,
135 ) -> Result<(), EncoderStateError> {
136 profiling::scope!("CommandEncoder::clear_texture");
137 api_log!("CommandEncoder::clear_texture {:?}", Arc::as_ptr(&dst));
138
139 let mut cmd_buf_data = self.data.lock();
140
141 cmd_buf_data.push_with(|| -> Result<_, ClearError> {
142 dst.check_valid()?;
143 Ok(ArcCommand::ClearTexture {
144 dst,
145 subresource_range: *subresource_range,
146 })
147 })
148 }
149}
150
151impl Global {
152 pub fn command_encoder_clear_buffer(
153 &self,
154 command_encoder_id: CommandEncoderId,
155 dst: BufferId,
156 offset: BufferAddress,
157 size: Option<BufferAddress>,
158 ) -> Result<(), EncoderStateError> {
159 let hub = &self.hub;
160
161 let cmd_enc = hub.command_encoders.get(command_encoder_id);
162 cmd_enc.clear_buffer(hub.buffers.get(dst), offset, size)
163 }
164
165 pub fn command_encoder_clear_texture(
166 &self,
167 command_encoder_id: CommandEncoderId,
168 dst: TextureId,
169 subresource_range: &ImageSubresourceRange,
170 ) -> Result<(), EncoderStateError> {
171 let hub = &self.hub;
172
173 let cmd_enc = hub.command_encoders.get(command_encoder_id);
174
175 cmd_enc.clear_texture(hub.textures.get(dst), subresource_range)
176 }
177}
178
179pub(super) fn clear_buffer(
180 state: &mut EncodingState,
181 dst_buffer: Arc<Buffer>,
182 offset: BufferAddress,
183 size: Option<BufferAddress>,
184) -> Result<(), ClearError> {
185 dst_buffer.same_device(state.device)?;
186
187 let dst_pending = state
188 .tracker
189 .buffers
190 .set_single(&dst_buffer, wgt::BufferUses::COPY_DST);
191
192 let dst_raw = dst_buffer.try_raw(state.snatch_guard)?;
193 dst_buffer.check_usage(BufferUsages::COPY_DST)?;
194
195 if !offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
197 return Err(ClearError::UnalignedBufferOffset(offset));
198 }
199
200 let size = size.unwrap_or(dst_buffer.size.saturating_sub(offset));
201 if !size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
202 return Err(ClearError::UnalignedFillSize(size));
203 }
204 let end_offset =
205 offset
206 .checked_add(size)
207 .ok_or(ClearError::OffsetPlusSizeExceeds64BitBounds {
208 start_offset: offset,
209 requested_size: size,
210 })?;
211 if end_offset > dst_buffer.size {
212 return Err(ClearError::BufferOverrun {
213 start_offset: offset,
214 end_offset,
215 buffer_size: dst_buffer.size,
216 });
217 }
218
219 if offset == end_offset {
222 log::trace!("Ignoring fill_buffer of size 0");
223 return Ok(());
224 }
225
226 state
228 .buffer_memory_init_actions
229 .extend(dst_buffer.initialization_status.read().create_action(
230 &dst_buffer,
231 offset..end_offset,
232 MemoryInitKind::ImplicitlyInitialized,
233 ));
234
235 let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, state.snatch_guard));
237 unsafe {
238 state.raw_encoder.transition_buffers(dst_barrier.as_slice());
239 state.raw_encoder.clear_buffer(dst_raw, offset..end_offset);
240 }
241
242 Ok(())
243}
244
245pub(super) fn clear_texture_cmd(
253 state: &mut EncodingState,
254 dst_texture: Arc<Texture>,
255 subresource_range: &ImageSubresourceRange,
256) -> Result<(), ClearError> {
257 dst_texture.same_device(state.device)?;
258 state
259 .device
260 .require_features(wgt::Features::CLEAR_TEXTURE)?;
261
262 let clear_aspects = hal::FormatAspects::new(dst_texture.desc.format, subresource_range.aspect);
264 if clear_aspects.is_empty() {
265 return Err(ClearError::MissingTextureAspect {
266 texture_format: dst_texture.desc.format,
267 subresource_range_aspects: subresource_range.aspect,
268 });
269 };
270
271 let subresource_mip_range = subresource_range.mip_range(dst_texture.full_range.mips.end);
273 if dst_texture.full_range.mips.start > subresource_mip_range.start
274 || dst_texture.full_range.mips.end < subresource_mip_range.end
275 {
276 return Err(ClearError::InvalidTextureLevelRange {
277 texture_level_range: dst_texture.full_range.mips.clone(),
278 subresource_base_mip_level: subresource_range.base_mip_level,
279 subresource_mip_level_count: subresource_range.mip_level_count,
280 });
281 }
282 let subresource_layer_range = subresource_range.layer_range(dst_texture.full_range.layers.end);
284 if dst_texture.full_range.layers.start > subresource_layer_range.start
285 || dst_texture.full_range.layers.end < subresource_layer_range.end
286 {
287 return Err(ClearError::InvalidTextureLayerRange {
288 texture_layer_range: dst_texture.full_range.layers.clone(),
289 subresource_base_array_layer: subresource_range.base_array_layer,
290 subresource_array_layer_count: subresource_range.array_layer_count,
291 });
292 }
293
294 clear_texture(
295 &dst_texture,
296 TextureInitRange {
297 mip_range: subresource_mip_range,
298 layer_range: subresource_layer_range,
299 },
300 state.raw_encoder,
301 &mut state.tracker.textures,
302 &state.device.alignments,
303 state.device.zero_buffer.as_ref(),
304 state.snatch_guard,
305 state.device.instance_flags,
306 )?;
307
308 Ok(())
309}
310
311pub(crate) fn clear_texture<T: TextureTrackerSetSingle>(
319 dst_texture: &Arc<Texture>,
320 range: TextureInitRange,
321 encoder: &mut dyn hal::DynCommandEncoder,
322 texture_tracker: &mut T,
323 alignments: &hal::Alignments,
324 zero_buffer: &dyn hal::DynBuffer,
325 snatch_guard: &SnatchGuard<'_>,
326 instance_flags: wgt::InstanceFlags,
327) -> Result<(), ClearError> {
328 let dst_raw = dst_texture.try_inner(snatch_guard)?.raw();
329
330 let clear_usage = match *dst_texture.clear_mode.read() {
332 TextureClearMode::BufferCopy => wgt::TextureUses::COPY_DST,
333 TextureClearMode::RenderPass {
334 is_color: false, ..
335 } => wgt::TextureUses::DEPTH_STENCIL_WRITE,
336 TextureClearMode::Surface { .. } | TextureClearMode::RenderPass { is_color: true, .. } => {
337 wgt::TextureUses::COLOR_TARGET
338 }
339 TextureClearMode::None => {
340 return Err(ClearError::NoValidTextureClearMode(
341 dst_texture.error_ident(),
342 ));
343 }
344 };
345
346 let selector = TextureSelector {
347 mips: range.mip_range.clone(),
348 layers: range.layer_range.clone(),
349 };
350
351 let dst_barrier = texture_tracker
365 .set_single(dst_texture, selector, clear_usage)
366 .map(|pending| pending.into_hal(dst_raw))
367 .collect::<Vec<_>>();
368 unsafe {
369 encoder.transition_textures(&dst_barrier);
370 }
371
372 let clear_mode = dst_texture.clear_mode.read();
374 match *clear_mode {
375 TextureClearMode::BufferCopy => clear_texture_via_buffer_copies(
376 &dst_texture.desc,
377 alignments,
378 zero_buffer,
379 range,
380 encoder,
381 dst_raw,
382 ),
383 TextureClearMode::Surface { .. } => {
384 drop(clear_mode);
385 clear_texture_via_render_passes(dst_texture, range, true, encoder, instance_flags)?
386 }
387 TextureClearMode::RenderPass { is_color, .. } => {
388 drop(clear_mode);
389 clear_texture_via_render_passes(dst_texture, range, is_color, encoder, instance_flags)?
390 }
391 TextureClearMode::None => {
392 return Err(ClearError::NoValidTextureClearMode(
393 dst_texture.error_ident(),
394 ));
395 }
396 }
397 Ok(())
398}
399
400fn clear_texture_via_buffer_copies(
401 texture_desc: &wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>>,
402 alignments: &hal::Alignments,
403 zero_buffer: &dyn hal::DynBuffer, range: TextureInitRange,
405 encoder: &mut dyn hal::DynCommandEncoder,
406 dst_raw: &dyn hal::DynTexture,
407) {
408 assert!(!texture_desc.format.is_depth_stencil_format());
409
410 if texture_desc.format == wgt::TextureFormat::NV12
411 || texture_desc.format == wgt::TextureFormat::P010
412 {
413 return;
415 }
416
417 let mut zero_buffer_copy_regions = Vec::new();
419 let buffer_copy_pitch = alignments.buffer_copy_pitch.get() as u32;
420 let (block_width, block_height) = texture_desc.format.block_dimensions();
421 let block_size = texture_desc.format.block_copy_size(None).unwrap();
422
423 let bytes_per_row_alignment = get_lowest_common_denom(buffer_copy_pitch, block_size);
424
425 for mip_level in range.mip_range {
426 let mut mip_size = texture_desc.mip_level_size(mip_level).unwrap();
427 mip_size.width = align_to(mip_size.width, block_width);
429 mip_size.height = align_to(mip_size.height, block_height);
430
431 let bytes_per_row = align_to(
432 mip_size.width / block_width * block_size,
433 bytes_per_row_alignment,
434 );
435
436 let max_rows_per_copy = crate::device::ZERO_BUFFER_SIZE as u32 / bytes_per_row;
437 let max_rows_per_copy = max_rows_per_copy / block_height * block_height;
439 assert!(
440 max_rows_per_copy > 0,
441 "Zero buffer size is too small to fill a single row \
442 of a texture with format {:?} and desc {:?}",
443 texture_desc.format,
444 texture_desc.size
445 );
446
447 let z_range = 0..(if texture_desc.dimension == wgt::TextureDimension::D3 {
448 mip_size.depth_or_array_layers
449 } else {
450 1
451 });
452
453 for array_layer in range.layer_range.clone() {
454 for z in z_range.clone() {
456 let mut num_rows_left = mip_size.height;
459 while num_rows_left > 0 {
460 let num_rows = num_rows_left.min(max_rows_per_copy);
461
462 zero_buffer_copy_regions.push(hal::BufferTextureCopy {
463 buffer_layout: wgt::TexelCopyBufferLayout {
464 offset: 0,
465 bytes_per_row: Some(bytes_per_row),
466 rows_per_image: None,
467 },
468 texture_base: hal::TextureCopyBase {
469 mip_level,
470 array_layer,
471 origin: wgt::Origin3d {
472 x: 0, y: mip_size.height - num_rows_left,
474 z,
475 },
476 aspect: hal::FormatAspects::COLOR,
477 },
478 size: hal::CopyExtent {
479 width: mip_size.width, height: num_rows,
481 depth: 1, },
483 });
484
485 num_rows_left -= num_rows;
486 }
487 }
488 }
489 }
490
491 unsafe {
492 encoder.copy_buffer_to_texture(zero_buffer, dst_raw, &zero_buffer_copy_regions);
493 }
494}
495
496fn clear_texture_via_render_passes(
497 dst_texture: &Texture,
498 range: TextureInitRange,
499 is_color: bool,
500 encoder: &mut dyn hal::DynCommandEncoder,
501 instance_flags: wgt::InstanceFlags,
502) -> Result<(), ClearError> {
503 assert_eq!(dst_texture.desc.dimension, wgt::TextureDimension::D2);
504
505 let extent_base = wgt::Extent3d {
506 width: dst_texture.desc.size.width,
507 height: dst_texture.desc.size.height,
508 depth_or_array_layers: 1, };
510
511 let clear_mode = dst_texture.clear_mode.read();
512
513 for mip_level in range.mip_range {
514 let extent = extent_base.mip_level_size(mip_level, dst_texture.desc.dimension);
515 for depth_or_layer in range.layer_range.clone() {
516 let color_attachments_tmp;
517 let (color_attachments, depth_stencil_attachment) = if is_color {
518 color_attachments_tmp = [Some(hal::ColorAttachment {
519 target: hal::Attachment {
520 view: Texture::get_clear_view(
521 &clear_mode,
522 &dst_texture.desc,
523 mip_level,
524 depth_or_layer,
525 ),
526 usage: wgt::TextureUses::COLOR_TARGET,
527 },
528 depth_slice: None,
529 resolve_target: None,
530 ops: hal::AttachmentOps::STORE | hal::AttachmentOps::LOAD_CLEAR,
531 clear_value: wgt::Color::TRANSPARENT,
532 })];
533 (&color_attachments_tmp[..], None)
534 } else {
535 (
536 &[][..],
537 Some(hal::DepthStencilAttachment {
538 target: hal::Attachment {
539 view: Texture::get_clear_view(
540 &clear_mode,
541 &dst_texture.desc,
542 mip_level,
543 depth_or_layer,
544 ),
545 usage: wgt::TextureUses::DEPTH_STENCIL_WRITE,
546 },
547 depth_ops: hal::AttachmentOps::STORE | hal::AttachmentOps::LOAD_CLEAR,
548 stencil_ops: hal::AttachmentOps::STORE | hal::AttachmentOps::LOAD_CLEAR,
549 clear_value: (0.0, 0),
550 }),
551 )
552 };
553 unsafe {
554 encoder
555 .begin_render_pass(&hal::RenderPassDescriptor {
556 label: hal_label(
557 Some("(wgpu internal) clear_texture clear pass"),
558 instance_flags,
559 ),
560 extent,
561 sample_count: dst_texture.desc.sample_count,
562 color_attachments,
563 depth_stencil_attachment,
564 multiview_mask: None,
565 timestamp_writes: None,
566 occlusion_query_set: None,
567 })
568 .map_err(|e| dst_texture.device.handle_hal_error(e))?;
569 encoder.end_render_pass();
570 }
571 }
572 }
573
574 Ok(())
575}