1use alloc::{sync::Arc, vec::Vec};
2use core::ops::Range;
3
4#[cfg(feature = "trace")]
5use crate::device::trace::Command as TraceCommand;
6use crate::{
7 api_log,
8 command::CommandEncoderError,
9 device::DeviceError,
10 get_lowest_common_denom,
11 global::Global,
12 id::{BufferId, CommandEncoderId, TextureId},
13 init_tracker::{MemoryInitKind, TextureInitRange},
14 resource::{
15 DestroyedResourceError, InvalidResourceError, Labeled, MissingBufferUsageError,
16 ParentDevice, ResourceErrorIdent, Texture, TextureClearMode,
17 },
18 snatch::SnatchGuard,
19 track::TextureTrackerSetSingle,
20};
21
22use thiserror::Error;
23use wgt::{
24 math::align_to, BufferAddress, BufferUsages, ImageSubresourceRange, TextureAspect,
25 TextureSelector,
26};
27
28#[derive(Clone, Debug, Error)]
30#[non_exhaustive]
31pub enum ClearError {
32 #[error("To use clear_texture the CLEAR_TEXTURE feature needs to be enabled")]
33 MissingClearTextureFeature,
34 #[error(transparent)]
35 DestroyedResource(#[from] DestroyedResourceError),
36 #[error("{0} can not be cleared")]
37 NoValidTextureClearMode(ResourceErrorIdent),
38 #[error("Buffer clear size {0:?} is not a multiple of `COPY_BUFFER_ALIGNMENT`")]
39 UnalignedFillSize(BufferAddress),
40 #[error("Buffer offset {0:?} is not a multiple of `COPY_BUFFER_ALIGNMENT`")]
41 UnalignedBufferOffset(BufferAddress),
42 #[error("Clear starts at offset {start_offset} with size of {requested_size}, but these added together exceed `u64::MAX`")]
43 OffsetPlusSizeExceeds64BitBounds {
44 start_offset: BufferAddress,
45 requested_size: BufferAddress,
46 },
47 #[error("Clear of {start_offset}..{end_offset} would end up overrunning the bounds of the buffer of size {buffer_size}")]
48 BufferOverrun {
49 start_offset: BufferAddress,
50 end_offset: BufferAddress,
51 buffer_size: BufferAddress,
52 },
53 #[error(transparent)]
54 MissingBufferUsage(#[from] MissingBufferUsageError),
55 #[error("Texture lacks the aspects that were specified in the image subresource range. Texture with format {texture_format:?}, specified was {subresource_range_aspects:?}")]
56 MissingTextureAspect {
57 texture_format: wgt::TextureFormat,
58 subresource_range_aspects: TextureAspect,
59 },
60 #[error("Image subresource level range is outside of the texture's level range. texture range is {texture_level_range:?}, \
61whereas subesource range specified start {subresource_base_mip_level} and count {subresource_mip_level_count:?}")]
62 InvalidTextureLevelRange {
63 texture_level_range: Range<u32>,
64 subresource_base_mip_level: u32,
65 subresource_mip_level_count: Option<u32>,
66 },
67 #[error("Image subresource layer range is outside of the texture's layer range. texture range is {texture_layer_range:?}, \
68whereas subesource range specified start {subresource_base_array_layer} and count {subresource_array_layer_count:?}")]
69 InvalidTextureLayerRange {
70 texture_layer_range: Range<u32>,
71 subresource_base_array_layer: u32,
72 subresource_array_layer_count: Option<u32>,
73 },
74 #[error(transparent)]
75 Device(#[from] DeviceError),
76 #[error(transparent)]
77 CommandEncoderError(#[from] CommandEncoderError),
78 #[error(transparent)]
79 InvalidResource(#[from] InvalidResourceError),
80}
81
82impl Global {
83 pub fn command_encoder_clear_buffer(
84 &self,
85 command_encoder_id: CommandEncoderId,
86 dst: BufferId,
87 offset: BufferAddress,
88 size: Option<BufferAddress>,
89 ) -> Result<(), ClearError> {
90 profiling::scope!("CommandEncoder::clear_buffer");
91 api_log!("CommandEncoder::clear_buffer {dst:?}");
92
93 let hub = &self.hub;
94
95 let cmd_buf = hub
96 .command_buffers
97 .get(command_encoder_id.into_command_buffer_id());
98 let mut cmd_buf_data = cmd_buf.data.lock();
99 let mut cmd_buf_data_guard = cmd_buf_data.record()?;
100 let cmd_buf_data = &mut *cmd_buf_data_guard;
101
102 #[cfg(feature = "trace")]
103 if let Some(ref mut list) = cmd_buf_data.commands {
104 list.push(TraceCommand::ClearBuffer { dst, offset, size });
105 }
106
107 let dst_buffer = hub.buffers.get(dst).get()?;
108
109 dst_buffer.same_device_as(cmd_buf.as_ref())?;
110
111 let dst_pending = cmd_buf_data
112 .trackers
113 .buffers
114 .set_single(&dst_buffer, wgt::BufferUses::COPY_DST);
115
116 let snatch_guard = dst_buffer.device.snatchable_lock.read();
117 let dst_raw = dst_buffer.try_raw(&snatch_guard)?;
118 dst_buffer.check_usage(BufferUsages::COPY_DST)?;
119
120 if offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
122 return Err(ClearError::UnalignedBufferOffset(offset));
123 }
124
125 let size = size.unwrap_or(dst_buffer.size.saturating_sub(offset));
126 if size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
127 return Err(ClearError::UnalignedFillSize(size));
128 }
129 let end_offset =
130 offset
131 .checked_add(size)
132 .ok_or(ClearError::OffsetPlusSizeExceeds64BitBounds {
133 start_offset: offset,
134 requested_size: size,
135 })?;
136 if end_offset > dst_buffer.size {
137 return Err(ClearError::BufferOverrun {
138 start_offset: offset,
139 end_offset,
140 buffer_size: dst_buffer.size,
141 });
142 }
143
144 if offset == end_offset {
145 log::trace!("Ignoring fill_buffer of size 0");
146
147 cmd_buf_data_guard.mark_successful();
148 return Ok(());
149 }
150
151 cmd_buf_data.buffer_memory_init_actions.extend(
153 dst_buffer.initialization_status.read().create_action(
154 &dst_buffer,
155 offset..end_offset,
156 MemoryInitKind::ImplicitlyInitialized,
157 ),
158 );
159
160 let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, &snatch_guard));
162 let cmd_buf_raw = cmd_buf_data.encoder.open()?;
163 unsafe {
164 cmd_buf_raw.transition_buffers(dst_barrier.as_slice());
165 cmd_buf_raw.clear_buffer(dst_raw, offset..end_offset);
166 }
167
168 cmd_buf_data_guard.mark_successful();
169 Ok(())
170 }
171
172 pub fn command_encoder_clear_texture(
173 &self,
174 command_encoder_id: CommandEncoderId,
175 dst: TextureId,
176 subresource_range: &ImageSubresourceRange,
177 ) -> Result<(), ClearError> {
178 profiling::scope!("CommandEncoder::clear_texture");
179 api_log!("CommandEncoder::clear_texture {dst:?}");
180
181 let hub = &self.hub;
182
183 let cmd_buf = hub
184 .command_buffers
185 .get(command_encoder_id.into_command_buffer_id());
186 let mut cmd_buf_data = cmd_buf.data.lock();
187 let mut cmd_buf_data_guard = cmd_buf_data.record()?;
188 let cmd_buf_data = &mut *cmd_buf_data_guard;
189
190 #[cfg(feature = "trace")]
191 if let Some(ref mut list) = cmd_buf_data.commands {
192 list.push(TraceCommand::ClearTexture {
193 dst,
194 subresource_range: *subresource_range,
195 });
196 }
197
198 if !cmd_buf.support_clear_texture {
199 return Err(ClearError::MissingClearTextureFeature);
200 }
201
202 let dst_texture = hub.textures.get(dst).get()?;
203
204 dst_texture.same_device_as(cmd_buf.as_ref())?;
205
206 let clear_aspects =
208 hal::FormatAspects::new(dst_texture.desc.format, subresource_range.aspect);
209 if clear_aspects.is_empty() {
210 return Err(ClearError::MissingTextureAspect {
211 texture_format: dst_texture.desc.format,
212 subresource_range_aspects: subresource_range.aspect,
213 });
214 };
215
216 let subresource_mip_range = subresource_range.mip_range(dst_texture.full_range.mips.end);
218 if dst_texture.full_range.mips.start > subresource_mip_range.start
219 || dst_texture.full_range.mips.end < subresource_mip_range.end
220 {
221 return Err(ClearError::InvalidTextureLevelRange {
222 texture_level_range: dst_texture.full_range.mips.clone(),
223 subresource_base_mip_level: subresource_range.base_mip_level,
224 subresource_mip_level_count: subresource_range.mip_level_count,
225 });
226 }
227 let subresource_layer_range =
229 subresource_range.layer_range(dst_texture.full_range.layers.end);
230 if dst_texture.full_range.layers.start > subresource_layer_range.start
231 || dst_texture.full_range.layers.end < subresource_layer_range.end
232 {
233 return Err(ClearError::InvalidTextureLayerRange {
234 texture_layer_range: dst_texture.full_range.layers.clone(),
235 subresource_base_array_layer: subresource_range.base_array_layer,
236 subresource_array_layer_count: subresource_range.array_layer_count,
237 });
238 }
239
240 let device = &cmd_buf.device;
241 device.check_is_valid()?;
242 let (encoder, tracker) = cmd_buf_data.open_encoder_and_tracker()?;
243
244 let snatch_guard = device.snatchable_lock.read();
245 clear_texture(
246 &dst_texture,
247 TextureInitRange {
248 mip_range: subresource_mip_range,
249 layer_range: subresource_layer_range,
250 },
251 encoder,
252 &mut tracker.textures,
253 &device.alignments,
254 device.zero_buffer.as_ref(),
255 &snatch_guard,
256 )?;
257
258 cmd_buf_data_guard.mark_successful();
259 Ok(())
260 }
261}
262
263pub(crate) fn clear_texture<T: TextureTrackerSetSingle>(
264 dst_texture: &Arc<Texture>,
265 range: TextureInitRange,
266 encoder: &mut dyn hal::DynCommandEncoder,
267 texture_tracker: &mut T,
268 alignments: &hal::Alignments,
269 zero_buffer: &dyn hal::DynBuffer,
270 snatch_guard: &SnatchGuard<'_>,
271) -> Result<(), ClearError> {
272 let dst_raw = dst_texture.try_raw(snatch_guard)?;
273
274 let clear_usage = match dst_texture.clear_mode {
276 TextureClearMode::BufferCopy => wgt::TextureUses::COPY_DST,
277 TextureClearMode::RenderPass {
278 is_color: false, ..
279 } => wgt::TextureUses::DEPTH_STENCIL_WRITE,
280 TextureClearMode::Surface { .. } | TextureClearMode::RenderPass { is_color: true, .. } => {
281 wgt::TextureUses::COLOR_TARGET
282 }
283 TextureClearMode::None => {
284 return Err(ClearError::NoValidTextureClearMode(
285 dst_texture.error_ident(),
286 ));
287 }
288 };
289
290 let selector = TextureSelector {
291 mips: range.mip_range.clone(),
292 layers: range.layer_range.clone(),
293 };
294
295 let dst_barrier = texture_tracker
309 .set_single(dst_texture, selector, clear_usage)
310 .map(|pending| pending.into_hal(dst_raw))
311 .collect::<Vec<_>>();
312 unsafe {
313 encoder.transition_textures(&dst_barrier);
314 }
315
316 match dst_texture.clear_mode {
318 TextureClearMode::BufferCopy => clear_texture_via_buffer_copies(
319 &dst_texture.desc,
320 alignments,
321 zero_buffer,
322 range,
323 encoder,
324 dst_raw,
325 ),
326 TextureClearMode::Surface { .. } => {
327 clear_texture_via_render_passes(dst_texture, range, true, encoder)
328 }
329 TextureClearMode::RenderPass { is_color, .. } => {
330 clear_texture_via_render_passes(dst_texture, range, is_color, encoder)
331 }
332 TextureClearMode::None => {
333 return Err(ClearError::NoValidTextureClearMode(
334 dst_texture.error_ident(),
335 ));
336 }
337 }
338 Ok(())
339}
340
341fn clear_texture_via_buffer_copies(
342 texture_desc: &wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
343 alignments: &hal::Alignments,
344 zero_buffer: &dyn hal::DynBuffer, range: TextureInitRange,
346 encoder: &mut dyn hal::DynCommandEncoder,
347 dst_raw: &dyn hal::DynTexture,
348) {
349 assert!(!texture_desc.format.is_depth_stencil_format());
350
351 if texture_desc.format == wgt::TextureFormat::NV12 {
352 return;
354 }
355
356 let mut zero_buffer_copy_regions = Vec::new();
358 let buffer_copy_pitch = alignments.buffer_copy_pitch.get() as u32;
359 let (block_width, block_height) = texture_desc.format.block_dimensions();
360 let block_size = texture_desc.format.block_copy_size(None).unwrap();
361
362 let bytes_per_row_alignment = get_lowest_common_denom(buffer_copy_pitch, block_size);
363
364 for mip_level in range.mip_range {
365 let mut mip_size = texture_desc.mip_level_size(mip_level).unwrap();
366 mip_size.width = align_to(mip_size.width, block_width);
368 mip_size.height = align_to(mip_size.height, block_height);
369
370 let bytes_per_row = align_to(
371 mip_size.width / block_width * block_size,
372 bytes_per_row_alignment,
373 );
374
375 let max_rows_per_copy = crate::device::ZERO_BUFFER_SIZE as u32 / bytes_per_row;
376 let max_rows_per_copy = max_rows_per_copy / block_height * block_height;
378 assert!(
379 max_rows_per_copy > 0,
380 "Zero buffer size is too small to fill a single row \
381 of a texture with format {:?} and desc {:?}",
382 texture_desc.format,
383 texture_desc.size
384 );
385
386 let z_range = 0..(if texture_desc.dimension == wgt::TextureDimension::D3 {
387 mip_size.depth_or_array_layers
388 } else {
389 1
390 });
391
392 for array_layer in range.layer_range.clone() {
393 for z in z_range.clone() {
395 let mut num_rows_left = mip_size.height;
398 while num_rows_left > 0 {
399 let num_rows = num_rows_left.min(max_rows_per_copy);
400
401 zero_buffer_copy_regions.push(hal::BufferTextureCopy {
402 buffer_layout: wgt::TexelCopyBufferLayout {
403 offset: 0,
404 bytes_per_row: Some(bytes_per_row),
405 rows_per_image: None,
406 },
407 texture_base: hal::TextureCopyBase {
408 mip_level,
409 array_layer,
410 origin: wgt::Origin3d {
411 x: 0, y: mip_size.height - num_rows_left,
413 z,
414 },
415 aspect: hal::FormatAspects::COLOR,
416 },
417 size: hal::CopyExtent {
418 width: mip_size.width, height: num_rows,
420 depth: 1, },
422 });
423
424 num_rows_left -= num_rows;
425 }
426 }
427 }
428 }
429
430 unsafe {
431 encoder.copy_buffer_to_texture(zero_buffer, dst_raw, &zero_buffer_copy_regions);
432 }
433}
434
435fn clear_texture_via_render_passes(
436 dst_texture: &Texture,
437 range: TextureInitRange,
438 is_color: bool,
439 encoder: &mut dyn hal::DynCommandEncoder,
440) {
441 assert_eq!(dst_texture.desc.dimension, wgt::TextureDimension::D2);
442
443 let extent_base = wgt::Extent3d {
444 width: dst_texture.desc.size.width,
445 height: dst_texture.desc.size.height,
446 depth_or_array_layers: 1, };
448
449 for mip_level in range.mip_range {
450 let extent = extent_base.mip_level_size(mip_level, dst_texture.desc.dimension);
451 for depth_or_layer in range.layer_range.clone() {
452 let color_attachments_tmp;
453 let (color_attachments, depth_stencil_attachment) = if is_color {
454 color_attachments_tmp = [Some(hal::ColorAttachment {
455 target: hal::Attachment {
456 view: Texture::get_clear_view(
457 &dst_texture.clear_mode,
458 &dst_texture.desc,
459 mip_level,
460 depth_or_layer,
461 ),
462 usage: wgt::TextureUses::COLOR_TARGET,
463 },
464 resolve_target: None,
465 ops: hal::AttachmentOps::STORE,
466 clear_value: wgt::Color::TRANSPARENT,
467 })];
468 (&color_attachments_tmp[..], None)
469 } else {
470 (
471 &[][..],
472 Some(hal::DepthStencilAttachment {
473 target: hal::Attachment {
474 view: Texture::get_clear_view(
475 &dst_texture.clear_mode,
476 &dst_texture.desc,
477 mip_level,
478 depth_or_layer,
479 ),
480 usage: wgt::TextureUses::DEPTH_STENCIL_WRITE,
481 },
482 depth_ops: hal::AttachmentOps::STORE,
483 stencil_ops: hal::AttachmentOps::STORE,
484 clear_value: (0.0, 0),
485 }),
486 )
487 };
488 unsafe {
489 encoder.begin_render_pass(&hal::RenderPassDescriptor {
490 label: Some("(wgpu internal) clear_texture clear pass"),
491 extent,
492 sample_count: dst_texture.desc.sample_count,
493 color_attachments,
494 depth_stencil_attachment,
495 multiview: None,
496 timestamp_writes: None,
497 occlusion_query_set: None,
498 });
499 encoder.end_render_pass();
500 }
501 }
502 }
503}