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