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::fill_buffer");
83
84 let hub = A::hub(self);
85 let mut token = Token::root();
86 let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token);
87 let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id)
88 .map_err(|_| ClearError::InvalidCommandEncoder(command_encoder_id))?;
89 let (buffer_guard, _) = hub.buffers.read(&mut token);
90
91 #[cfg(feature = "trace")]
92 if let Some(ref mut list) = cmd_buf.commands {
93 list.push(TraceCommand::ClearBuffer { dst, offset, size });
94 }
95
96 let (dst_buffer, dst_pending) = cmd_buf
97 .trackers
98 .buffers
99 .set_single(&*buffer_guard, dst, hal::BufferUses::COPY_DST)
100 .ok_or(ClearError::InvalidBuffer(dst))?;
101 let dst_raw = dst_buffer
102 .raw
103 .as_ref()
104 .ok_or(ClearError::InvalidBuffer(dst))?;
105 if !dst_buffer.usage.contains(BufferUsages::COPY_DST) {
106 return Err(ClearError::MissingCopyDstUsageFlag(Some(dst), None));
107 }
108
109 if offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
111 return Err(ClearError::UnalignedBufferOffset(offset));
112 }
113 if let Some(size) = size {
114 if size.get() % wgt::COPY_BUFFER_ALIGNMENT != 0 {
115 return Err(ClearError::UnalignedFillSize(size));
116 }
117 let destination_end_offset = offset + size.get();
118 if destination_end_offset > dst_buffer.size {
119 return Err(ClearError::BufferOverrun {
120 start_offset: offset,
121 end_offset: destination_end_offset,
122 buffer_size: dst_buffer.size,
123 });
124 }
125 }
126
127 let end = match size {
128 Some(size) => offset + size.get(),
129 None => dst_buffer.size,
130 };
131 if offset == end {
132 log::trace!("Ignoring fill_buffer of size 0");
133 return Ok(());
134 }
135
136 cmd_buf
138 .buffer_memory_init_actions
139 .extend(dst_buffer.initialization_status.create_action(
140 dst,
141 offset..end,
142 MemoryInitKind::ImplicitlyInitialized,
143 ));
144 let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer));
146 let cmd_buf_raw = cmd_buf.encoder.open();
147 unsafe {
148 cmd_buf_raw.transition_buffers(dst_barrier.into_iter());
149 cmd_buf_raw.clear_buffer(dst_raw, offset..end);
150 }
151 Ok(())
152 }
153
154 pub fn command_encoder_clear_texture<A: HalApi>(
155 &self,
156 command_encoder_id: CommandEncoderId,
157 dst: TextureId,
158 subresource_range: &ImageSubresourceRange,
159 ) -> Result<(), ClearError> {
160 profiling::scope!("CommandEncoder::clear_texture");
161
162 let hub = A::hub(self);
163 let mut token = Token::root();
164 let (device_guard, mut token) = hub.devices.write(&mut token);
165 let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token);
166 let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id)
167 .map_err(|_| ClearError::InvalidCommandEncoder(command_encoder_id))?;
168 let (_, mut token) = hub.buffers.read(&mut token); let (texture_guard, _) = hub.textures.read(&mut token);
170
171 #[cfg(feature = "trace")]
172 if let Some(ref mut list) = cmd_buf.commands {
173 list.push(TraceCommand::ClearTexture {
174 dst,
175 subresource_range: *subresource_range,
176 });
177 }
178
179 if !cmd_buf.support_clear_texture {
180 return Err(ClearError::MissingClearTextureFeature);
181 }
182
183 let dst_texture = texture_guard
184 .get(dst)
185 .map_err(|_| ClearError::InvalidTexture(dst))?;
186
187 let clear_aspects =
189 hal::FormatAspects::new(dst_texture.desc.format, subresource_range.aspect);
190 if clear_aspects.is_empty() {
191 return Err(ClearError::MissingTextureAspect {
192 texture_format: dst_texture.desc.format,
193 subresource_range_aspects: subresource_range.aspect,
194 });
195 };
196
197 let subresource_mip_range = subresource_range.mip_range(dst_texture.full_range.mips.end);
199 if dst_texture.full_range.mips.start > subresource_mip_range.start
200 || dst_texture.full_range.mips.end < subresource_mip_range.end
201 {
202 return Err(ClearError::InvalidTextureLevelRange {
203 texture_level_range: dst_texture.full_range.mips.clone(),
204 subresource_base_mip_level: subresource_range.base_mip_level,
205 subresource_mip_level_count: subresource_range.mip_level_count,
206 });
207 }
208 let subresource_layer_range =
210 subresource_range.layer_range(dst_texture.full_range.layers.end);
211 if dst_texture.full_range.layers.start > subresource_layer_range.start
212 || dst_texture.full_range.layers.end < subresource_layer_range.end
213 {
214 return Err(ClearError::InvalidTextureLayerRange {
215 texture_layer_range: dst_texture.full_range.layers.clone(),
216 subresource_base_array_layer: subresource_range.base_array_layer,
217 subresource_array_layer_count: subresource_range.array_layer_count,
218 });
219 }
220
221 let device = &device_guard[cmd_buf.device_id.value];
222
223 clear_texture(
224 &*texture_guard,
225 Valid(dst),
226 TextureInitRange {
227 mip_range: subresource_mip_range,
228 layer_range: subresource_layer_range,
229 },
230 cmd_buf.encoder.open(),
231 &mut cmd_buf.trackers.textures,
232 &device.alignments,
233 &device.zero_buffer,
234 )
235 }
236}
237
238pub(crate) fn clear_texture<A: HalApi>(
239 storage: &storage::Storage<Texture<A>, TextureId>,
240 dst_texture_id: Valid<TextureId>,
241 range: TextureInitRange,
242 encoder: &mut A::CommandEncoder,
243 texture_tracker: &mut TextureTracker<A>,
244 alignments: &hal::Alignments,
245 zero_buffer: &A::Buffer,
246) -> Result<(), ClearError> {
247 let dst_texture = &storage[dst_texture_id];
248
249 let dst_raw = dst_texture
250 .inner
251 .as_raw()
252 .ok_or(ClearError::InvalidTexture(dst_texture_id.0))?;
253
254 let clear_usage = match dst_texture.clear_mode {
256 TextureClearMode::BufferCopy => hal::TextureUses::COPY_DST,
257 TextureClearMode::RenderPass {
258 is_color: false, ..
259 } => hal::TextureUses::DEPTH_STENCIL_WRITE,
260 TextureClearMode::RenderPass { is_color: true, .. } => hal::TextureUses::COLOR_TARGET,
261 TextureClearMode::None => {
262 return Err(ClearError::NoValidTextureClearMode(dst_texture_id.0));
263 }
264 };
265
266 let selector = TextureSelector {
267 mips: range.mip_range.clone(),
268 layers: range.layer_range.clone(),
269 };
270
271 let dst_barrier = texture_tracker
285 .set_single(dst_texture, dst_texture_id.0, selector, clear_usage)
286 .unwrap()
287 .map(|pending| pending.into_hal(dst_texture));
288 unsafe {
289 encoder.transition_textures(dst_barrier.into_iter());
290 }
291
292 match dst_texture.clear_mode {
294 TextureClearMode::BufferCopy => clear_texture_via_buffer_copies::<A>(
295 &dst_texture.desc,
296 alignments,
297 zero_buffer,
298 range,
299 encoder,
300 dst_raw,
301 ),
302 TextureClearMode::RenderPass { is_color, .. } => {
303 clear_texture_via_render_passes(dst_texture, range, is_color, encoder)?
304 }
305 TextureClearMode::None => {
306 return Err(ClearError::NoValidTextureClearMode(dst_texture_id.0));
307 }
308 }
309 Ok(())
310}
311
312fn clear_texture_via_buffer_copies<A: hal::Api>(
313 texture_desc: &wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
314 alignments: &hal::Alignments,
315 zero_buffer: &A::Buffer, range: TextureInitRange,
317 encoder: &mut A::CommandEncoder,
318 dst_raw: &A::Texture,
319) {
320 assert_eq!(
321 hal::FormatAspects::from(texture_desc.format),
322 hal::FormatAspects::COLOR
323 );
324
325 let mut zero_buffer_copy_regions = Vec::new();
327 let buffer_copy_pitch = alignments.buffer_copy_pitch.get() as u32;
328 let (block_width, block_height) = texture_desc.format.block_dimensions();
329 let block_size = texture_desc.format.block_size(None).unwrap();
330
331 let bytes_per_row_alignment = get_lowest_common_denom(buffer_copy_pitch, block_size);
332
333 for mip_level in range.mip_range {
334 let mut mip_size = texture_desc.mip_level_size(mip_level).unwrap();
335 mip_size.width = align_to(mip_size.width, block_width);
337 mip_size.height = align_to(mip_size.height, block_height);
338
339 let bytes_per_row = align_to(
340 mip_size.width / block_width * block_size,
341 bytes_per_row_alignment,
342 );
343
344 let max_rows_per_copy = crate::device::ZERO_BUFFER_SIZE as u32 / bytes_per_row;
345 let max_rows_per_copy = max_rows_per_copy / block_height * block_height;
347 assert!(
348 max_rows_per_copy > 0,
349 "Zero buffer size is too small to fill a single row \
350 of a texture with format {:?} and desc {:?}",
351 texture_desc.format,
352 texture_desc.size
353 );
354
355 let z_range = 0..(if texture_desc.dimension == wgt::TextureDimension::D3 {
356 mip_size.depth_or_array_layers
357 } else {
358 1
359 });
360
361 for array_layer in range.layer_range.clone() {
362 for z in z_range.clone() {
364 let mut num_rows_left = mip_size.height;
367 while num_rows_left > 0 {
368 let num_rows = num_rows_left.min(max_rows_per_copy);
369
370 zero_buffer_copy_regions.push(hal::BufferTextureCopy {
371 buffer_layout: wgt::ImageDataLayout {
372 offset: 0,
373 bytes_per_row: Some(bytes_per_row),
374 rows_per_image: None,
375 },
376 texture_base: hal::TextureCopyBase {
377 mip_level,
378 array_layer,
379 origin: wgt::Origin3d {
380 x: 0, y: mip_size.height - num_rows_left,
382 z,
383 },
384 aspect: hal::FormatAspects::COLOR,
385 },
386 size: hal::CopyExtent {
387 width: mip_size.width, height: num_rows,
389 depth: 1, },
391 });
392
393 num_rows_left -= num_rows;
394 }
395 }
396 }
397 }
398
399 unsafe {
400 encoder.copy_buffer_to_texture(zero_buffer, dst_raw, zero_buffer_copy_regions.into_iter());
401 }
402}
403
404fn clear_texture_via_render_passes<A: hal::Api>(
405 dst_texture: &Texture<A>,
406 range: TextureInitRange,
407 is_color: bool,
408 encoder: &mut A::CommandEncoder,
409) -> Result<(), ClearError> {
410 assert_eq!(dst_texture.desc.dimension, wgt::TextureDimension::D2);
411
412 let extent_base = wgt::Extent3d {
413 width: dst_texture.desc.size.width,
414 height: dst_texture.desc.size.height,
415 depth_or_array_layers: 1, };
417
418 for mip_level in range.mip_range {
419 let extent = extent_base.mip_level_size(mip_level, dst_texture.desc.dimension);
420 for depth_or_layer in range.layer_range.clone() {
421 let color_attachments_tmp;
422 let (color_attachments, depth_stencil_attachment) = if is_color {
423 color_attachments_tmp = [Some(hal::ColorAttachment {
424 target: hal::Attachment {
425 view: dst_texture.get_clear_view(mip_level, depth_or_layer),
426 usage: hal::TextureUses::COLOR_TARGET,
427 },
428 resolve_target: None,
429 ops: hal::AttachmentOps::STORE,
430 clear_value: wgt::Color::TRANSPARENT,
431 })];
432 (&color_attachments_tmp[..], None)
433 } else {
434 (
435 &[][..],
436 Some(hal::DepthStencilAttachment {
437 target: hal::Attachment {
438 view: dst_texture.get_clear_view(mip_level, depth_or_layer),
439 usage: hal::TextureUses::DEPTH_STENCIL_WRITE,
440 },
441 depth_ops: hal::AttachmentOps::STORE,
442 stencil_ops: hal::AttachmentOps::STORE,
443 clear_value: (0.0, 0),
444 }),
445 )
446 };
447 unsafe {
448 encoder.begin_render_pass(&hal::RenderPassDescriptor {
449 label: Some("(wgpu internal) clear_texture clear pass"),
450 extent,
451 sample_count: dst_texture.desc.sample_count,
452 color_attachments,
453 depth_stencil_attachment,
454 multiview: None,
455 });
456 encoder.end_render_pass();
457 }
458 }
459 }
460 Ok(())
461}