1use std::ops::Range;
2
3#[cfg(feature = "trace")]
4use crate::device::trace::Command as TraceCommand;
5use crate::{
6 command::CommandBuffer,
7 get_lowest_common_denom,
8 global::Global,
9 hal_api::HalApi,
10 hub::Token,
11 id::{BufferId, CommandEncoderId, DeviceId, TextureId, Valid},
12 identity::GlobalIdentityHandlerFactory,
13 init_tracker::{MemoryInitKind, TextureInitRange},
14 resource::{Texture, TextureClearMode},
15 storage,
16 track::{TextureSelector, TextureTracker},
17};
18
19use hal::CommandEncoder as _;
20use thiserror::Error;
21use wgt::{
22 math::align_to, BufferAddress, BufferSize, BufferUsages, ImageSubresourceRange, TextureAspect,
23};
24
25#[derive(Clone, Debug, Error)]
27#[non_exhaustive]
28pub enum ClearError {
29 #[error("To use clear_texture the CLEAR_TEXTURE feature needs to be enabled")]
30 MissingClearTextureFeature,
31 #[error("Command encoder {0:?} is invalid")]
32 InvalidCommandEncoder(CommandEncoderId),
33 #[error("Device {0:?} is invalid")]
34 InvalidDevice(DeviceId),
35 #[error("Buffer {0:?} is invalid or destroyed")]
36 InvalidBuffer(BufferId),
37 #[error("Texture {0:?} is invalid or destroyed")]
38 InvalidTexture(TextureId),
39 #[error("Texture {0:?} can not be cleared")]
40 NoValidTextureClearMode(TextureId),
41 #[error("Buffer clear size {0:?} is not a multiple of `COPY_BUFFER_ALIGNMENT`")]
42 UnalignedFillSize(BufferSize),
43 #[error("Buffer offset {0:?} is not a multiple of `COPY_BUFFER_ALIGNMENT`")]
44 UnalignedBufferOffset(BufferAddress),
45 #[error("Clear of {start_offset}..{end_offset} would end up overrunning the bounds of the buffer of size {buffer_size}")]
46 BufferOverrun {
47 start_offset: BufferAddress,
48 end_offset: BufferAddress,
49 buffer_size: BufferAddress,
50 },
51 #[error("Destination buffer is missing the `COPY_DST` usage flag")]
52 MissingCopyDstUsageFlag(Option<BufferId>, Option<TextureId>),
53 #[error("Texture lacks the aspects that were specified in the image subresource range. Texture with format {texture_format:?}, specified was {subresource_range_aspects:?}")]
54 MissingTextureAspect {
55 texture_format: wgt::TextureFormat,
56 subresource_range_aspects: TextureAspect,
57 },
58 #[error("Image subresource level range is outside of the texture's level range. texture range is {texture_level_range:?}, \
59whereas subesource range specified start {subresource_base_mip_level} and count {subresource_mip_level_count:?}")]
60 InvalidTextureLevelRange {
61 texture_level_range: Range<u32>,
62 subresource_base_mip_level: u32,
63 subresource_mip_level_count: Option<u32>,
64 },
65 #[error("Image subresource layer range is outside of the texture's layer range. texture range is {texture_layer_range:?}, \
66whereas subesource range specified start {subresource_base_array_layer} and count {subresource_array_layer_count:?}")]
67 InvalidTextureLayerRange {
68 texture_layer_range: Range<u32>,
69 subresource_base_array_layer: u32,
70 subresource_array_layer_count: Option<u32>,
71 },
72}
73
74impl<G: GlobalIdentityHandlerFactory> Global<G> {
75 pub fn command_encoder_clear_buffer<A: HalApi>(
76 &self,
77 command_encoder_id: CommandEncoderId,
78 dst: BufferId,
79 offset: BufferAddress,
80 size: Option<BufferSize>,
81 ) -> Result<(), ClearError> {
82 profiling::scope!("CommandEncoder::clear_buffer");
83 log::trace!("CommandEncoder::clear_buffer {dst:?}");
84
85 let hub = A::hub(self);
86 let mut token = Token::root();
87 let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token);
88 let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id)
89 .map_err(|_| ClearError::InvalidCommandEncoder(command_encoder_id))?;
90 let (buffer_guard, _) = hub.buffers.read(&mut token);
91
92 #[cfg(feature = "trace")]
93 if let Some(ref mut list) = cmd_buf.commands {
94 list.push(TraceCommand::ClearBuffer { dst, offset, size });
95 }
96
97 let (dst_buffer, dst_pending) = cmd_buf
98 .trackers
99 .buffers
100 .set_single(&*buffer_guard, dst, hal::BufferUses::COPY_DST)
101 .ok_or(ClearError::InvalidBuffer(dst))?;
102 let dst_raw = dst_buffer
103 .raw
104 .as_ref()
105 .ok_or(ClearError::InvalidBuffer(dst))?;
106 if !dst_buffer.usage.contains(BufferUsages::COPY_DST) {
107 return Err(ClearError::MissingCopyDstUsageFlag(Some(dst), None));
108 }
109
110 if offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
112 return Err(ClearError::UnalignedBufferOffset(offset));
113 }
114 if let Some(size) = size {
115 if size.get() % wgt::COPY_BUFFER_ALIGNMENT != 0 {
116 return Err(ClearError::UnalignedFillSize(size));
117 }
118 let destination_end_offset = offset + size.get();
119 if destination_end_offset > dst_buffer.size {
120 return Err(ClearError::BufferOverrun {
121 start_offset: offset,
122 end_offset: destination_end_offset,
123 buffer_size: dst_buffer.size,
124 });
125 }
126 }
127
128 let end = match size {
129 Some(size) => offset + size.get(),
130 None => dst_buffer.size,
131 };
132 if offset == end {
133 log::trace!("Ignoring fill_buffer of size 0");
134 return Ok(());
135 }
136
137 cmd_buf
139 .buffer_memory_init_actions
140 .extend(dst_buffer.initialization_status.create_action(
141 dst,
142 offset..end,
143 MemoryInitKind::ImplicitlyInitialized,
144 ));
145 let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer));
147 let cmd_buf_raw = cmd_buf.encoder.open();
148 unsafe {
149 cmd_buf_raw.transition_buffers(dst_barrier.into_iter());
150 cmd_buf_raw.clear_buffer(dst_raw, offset..end);
151 }
152 Ok(())
153 }
154
155 pub fn command_encoder_clear_texture<A: HalApi>(
156 &self,
157 command_encoder_id: CommandEncoderId,
158 dst: TextureId,
159 subresource_range: &ImageSubresourceRange,
160 ) -> Result<(), ClearError> {
161 profiling::scope!("CommandEncoder::clear_texture");
162 log::trace!("CommandEncoder::clear_texture {dst:?}");
163
164 let hub = A::hub(self);
165 let mut token = Token::root();
166 let (device_guard, mut token) = hub.devices.write(&mut token);
167 let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token);
168 let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id)
169 .map_err(|_| ClearError::InvalidCommandEncoder(command_encoder_id))?;
170 let (_, mut token) = hub.buffers.read(&mut token); let (texture_guard, _) = hub.textures.read(&mut token);
172
173 #[cfg(feature = "trace")]
174 if let Some(ref mut list) = cmd_buf.commands {
175 list.push(TraceCommand::ClearTexture {
176 dst,
177 subresource_range: *subresource_range,
178 });
179 }
180
181 if !cmd_buf.support_clear_texture {
182 return Err(ClearError::MissingClearTextureFeature);
183 }
184
185 let dst_texture = texture_guard
186 .get(dst)
187 .map_err(|_| ClearError::InvalidTexture(dst))?;
188
189 let clear_aspects =
191 hal::FormatAspects::new(dst_texture.desc.format, subresource_range.aspect);
192 if clear_aspects.is_empty() {
193 return Err(ClearError::MissingTextureAspect {
194 texture_format: dst_texture.desc.format,
195 subresource_range_aspects: subresource_range.aspect,
196 });
197 };
198
199 let subresource_mip_range = subresource_range.mip_range(dst_texture.full_range.mips.end);
201 if dst_texture.full_range.mips.start > subresource_mip_range.start
202 || dst_texture.full_range.mips.end < subresource_mip_range.end
203 {
204 return Err(ClearError::InvalidTextureLevelRange {
205 texture_level_range: dst_texture.full_range.mips.clone(),
206 subresource_base_mip_level: subresource_range.base_mip_level,
207 subresource_mip_level_count: subresource_range.mip_level_count,
208 });
209 }
210 let subresource_layer_range =
212 subresource_range.layer_range(dst_texture.full_range.layers.end);
213 if dst_texture.full_range.layers.start > subresource_layer_range.start
214 || dst_texture.full_range.layers.end < subresource_layer_range.end
215 {
216 return Err(ClearError::InvalidTextureLayerRange {
217 texture_layer_range: dst_texture.full_range.layers.clone(),
218 subresource_base_array_layer: subresource_range.base_array_layer,
219 subresource_array_layer_count: subresource_range.array_layer_count,
220 });
221 }
222
223 let device = &device_guard[cmd_buf.device_id.value];
224 if !device.is_valid() {
225 return Err(ClearError::InvalidDevice(cmd_buf.device_id.value.0));
226 }
227
228 clear_texture(
229 &*texture_guard,
230 Valid(dst),
231 TextureInitRange {
232 mip_range: subresource_mip_range,
233 layer_range: subresource_layer_range,
234 },
235 cmd_buf.encoder.open(),
236 &mut cmd_buf.trackers.textures,
237 &device.alignments,
238 &device.zero_buffer,
239 )
240 }
241}
242
243pub(crate) fn clear_texture<A: HalApi>(
244 storage: &storage::Storage<Texture<A>, TextureId>,
245 dst_texture_id: Valid<TextureId>,
246 range: TextureInitRange,
247 encoder: &mut A::CommandEncoder,
248 texture_tracker: &mut TextureTracker<A>,
249 alignments: &hal::Alignments,
250 zero_buffer: &A::Buffer,
251) -> Result<(), ClearError> {
252 let dst_texture = &storage[dst_texture_id];
253
254 let dst_raw = dst_texture
255 .inner
256 .as_raw()
257 .ok_or(ClearError::InvalidTexture(dst_texture_id.0))?;
258
259 let clear_usage = match dst_texture.clear_mode {
261 TextureClearMode::BufferCopy => hal::TextureUses::COPY_DST,
262 TextureClearMode::RenderPass {
263 is_color: false, ..
264 } => hal::TextureUses::DEPTH_STENCIL_WRITE,
265 TextureClearMode::RenderPass { is_color: true, .. } => hal::TextureUses::COLOR_TARGET,
266 TextureClearMode::None => {
267 return Err(ClearError::NoValidTextureClearMode(dst_texture_id.0));
268 }
269 };
270
271 let selector = TextureSelector {
272 mips: range.mip_range.clone(),
273 layers: range.layer_range.clone(),
274 };
275
276 let dst_barrier = texture_tracker
290 .set_single(dst_texture, dst_texture_id.0, selector, clear_usage)
291 .unwrap()
292 .map(|pending| pending.into_hal(dst_texture));
293 unsafe {
294 encoder.transition_textures(dst_barrier.into_iter());
295 }
296
297 match dst_texture.clear_mode {
299 TextureClearMode::BufferCopy => clear_texture_via_buffer_copies::<A>(
300 &dst_texture.desc,
301 alignments,
302 zero_buffer,
303 range,
304 encoder,
305 dst_raw,
306 ),
307 TextureClearMode::RenderPass { is_color, .. } => {
308 clear_texture_via_render_passes(dst_texture, range, is_color, encoder)?
309 }
310 TextureClearMode::None => {
311 return Err(ClearError::NoValidTextureClearMode(dst_texture_id.0));
312 }
313 }
314 Ok(())
315}
316
317fn clear_texture_via_buffer_copies<A: hal::Api>(
318 texture_desc: &wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
319 alignments: &hal::Alignments,
320 zero_buffer: &A::Buffer, range: TextureInitRange,
322 encoder: &mut A::CommandEncoder,
323 dst_raw: &A::Texture,
324) {
325 assert_eq!(
326 hal::FormatAspects::from(texture_desc.format),
327 hal::FormatAspects::COLOR
328 );
329
330 let mut zero_buffer_copy_regions = Vec::new();
332 let buffer_copy_pitch = alignments.buffer_copy_pitch.get() as u32;
333 let (block_width, block_height) = texture_desc.format.block_dimensions();
334 let block_size = texture_desc.format.block_size(None).unwrap();
335
336 let bytes_per_row_alignment = get_lowest_common_denom(buffer_copy_pitch, block_size);
337
338 for mip_level in range.mip_range {
339 let mut mip_size = texture_desc.mip_level_size(mip_level).unwrap();
340 mip_size.width = align_to(mip_size.width, block_width);
342 mip_size.height = align_to(mip_size.height, block_height);
343
344 let bytes_per_row = align_to(
345 mip_size.width / block_width * block_size,
346 bytes_per_row_alignment,
347 );
348
349 let max_rows_per_copy = crate::device::ZERO_BUFFER_SIZE as u32 / bytes_per_row;
350 let max_rows_per_copy = max_rows_per_copy / block_height * block_height;
352 assert!(
353 max_rows_per_copy > 0,
354 "Zero buffer size is too small to fill a single row \
355 of a texture with format {:?} and desc {:?}",
356 texture_desc.format,
357 texture_desc.size
358 );
359
360 let z_range = 0..(if texture_desc.dimension == wgt::TextureDimension::D3 {
361 mip_size.depth_or_array_layers
362 } else {
363 1
364 });
365
366 for array_layer in range.layer_range.clone() {
367 for z in z_range.clone() {
369 let mut num_rows_left = mip_size.height;
372 while num_rows_left > 0 {
373 let num_rows = num_rows_left.min(max_rows_per_copy);
374
375 zero_buffer_copy_regions.push(hal::BufferTextureCopy {
376 buffer_layout: wgt::ImageDataLayout {
377 offset: 0,
378 bytes_per_row: Some(bytes_per_row),
379 rows_per_image: None,
380 },
381 texture_base: hal::TextureCopyBase {
382 mip_level,
383 array_layer,
384 origin: wgt::Origin3d {
385 x: 0, y: mip_size.height - num_rows_left,
387 z,
388 },
389 aspect: hal::FormatAspects::COLOR,
390 },
391 size: hal::CopyExtent {
392 width: mip_size.width, height: num_rows,
394 depth: 1, },
396 });
397
398 num_rows_left -= num_rows;
399 }
400 }
401 }
402 }
403
404 unsafe {
405 encoder.copy_buffer_to_texture(zero_buffer, dst_raw, zero_buffer_copy_regions.into_iter());
406 }
407}
408
409fn clear_texture_via_render_passes<A: hal::Api>(
410 dst_texture: &Texture<A>,
411 range: TextureInitRange,
412 is_color: bool,
413 encoder: &mut A::CommandEncoder,
414) -> Result<(), ClearError> {
415 assert_eq!(dst_texture.desc.dimension, wgt::TextureDimension::D2);
416
417 let extent_base = wgt::Extent3d {
418 width: dst_texture.desc.size.width,
419 height: dst_texture.desc.size.height,
420 depth_or_array_layers: 1, };
422
423 for mip_level in range.mip_range {
424 let extent = extent_base.mip_level_size(mip_level, dst_texture.desc.dimension);
425 for depth_or_layer in range.layer_range.clone() {
426 let color_attachments_tmp;
427 let (color_attachments, depth_stencil_attachment) = if is_color {
428 color_attachments_tmp = [Some(hal::ColorAttachment {
429 target: hal::Attachment {
430 view: dst_texture.get_clear_view(mip_level, depth_or_layer),
431 usage: hal::TextureUses::COLOR_TARGET,
432 },
433 resolve_target: None,
434 ops: hal::AttachmentOps::STORE,
435 clear_value: wgt::Color::TRANSPARENT,
436 })];
437 (&color_attachments_tmp[..], None)
438 } else {
439 (
440 &[][..],
441 Some(hal::DepthStencilAttachment {
442 target: hal::Attachment {
443 view: dst_texture.get_clear_view(mip_level, depth_or_layer),
444 usage: hal::TextureUses::DEPTH_STENCIL_WRITE,
445 },
446 depth_ops: hal::AttachmentOps::STORE,
447 stencil_ops: hal::AttachmentOps::STORE,
448 clear_value: (0.0, 0),
449 }),
450 )
451 };
452 unsafe {
453 encoder.begin_render_pass(&hal::RenderPassDescriptor {
454 label: Some("(wgpu internal) clear_texture clear pass"),
455 extent,
456 sample_count: dst_texture.desc.sample_count,
457 color_attachments,
458 depth_stencil_attachment,
459 multiview: None,
460 timestamp_writes: None,
461 occlusion_query_set: None,
462 });
463 encoder.end_render_pass();
464 }
465 }
466 }
467 Ok(())
468}