Skip to content

Commit 04a3401

Browse files
Extract command encoding to free functions (#8210)
1 parent 67bdd80 commit 04a3401

File tree

8 files changed

+2027
-1895
lines changed

8 files changed

+2027
-1895
lines changed

wgpu-core/src/command/clear.rs

Lines changed: 167 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use core::ops::Range;
55
use crate::command::Command as TraceCommand;
66
use crate::{
77
api_log,
8-
command::EncoderStateError,
8+
command::{CommandBufferMutable, CommandEncoder, EncoderStateError},
99
device::{DeviceError, MissingFeatures},
1010
get_lowest_common_denom,
1111
global::Global,
@@ -119,74 +119,7 @@ impl Global {
119119
let cmd_enc = hub.command_encoders.get(command_encoder_id);
120120
let mut cmd_buf_data = cmd_enc.data.lock();
121121
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), ClearError> {
122-
#[cfg(feature = "trace")]
123-
if let Some(ref mut list) = cmd_buf_data.trace_commands {
124-
list.push(TraceCommand::ClearBuffer { dst, offset, size });
125-
}
126-
127-
cmd_enc.device.check_is_valid()?;
128-
129-
let dst_buffer = hub.buffers.get(dst).get()?;
130-
131-
dst_buffer.same_device_as(cmd_enc.as_ref())?;
132-
133-
let dst_pending = cmd_buf_data
134-
.trackers
135-
.buffers
136-
.set_single(&dst_buffer, wgt::BufferUses::COPY_DST);
137-
138-
let snatch_guard = dst_buffer.device.snatchable_lock.read();
139-
let dst_raw = dst_buffer.try_raw(&snatch_guard)?;
140-
dst_buffer.check_usage(BufferUsages::COPY_DST)?;
141-
142-
// Check if offset & size are valid.
143-
if offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
144-
return Err(ClearError::UnalignedBufferOffset(offset));
145-
}
146-
147-
let size = size.unwrap_or(dst_buffer.size.saturating_sub(offset));
148-
if size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
149-
return Err(ClearError::UnalignedFillSize(size));
150-
}
151-
let end_offset =
152-
offset
153-
.checked_add(size)
154-
.ok_or(ClearError::OffsetPlusSizeExceeds64BitBounds {
155-
start_offset: offset,
156-
requested_size: size,
157-
})?;
158-
if end_offset > dst_buffer.size {
159-
return Err(ClearError::BufferOverrun {
160-
start_offset: offset,
161-
end_offset,
162-
buffer_size: dst_buffer.size,
163-
});
164-
}
165-
166-
if offset == end_offset {
167-
log::trace!("Ignoring fill_buffer of size 0");
168-
return Ok(());
169-
}
170-
171-
// Mark dest as initialized.
172-
cmd_buf_data.buffer_memory_init_actions.extend(
173-
dst_buffer.initialization_status.read().create_action(
174-
&dst_buffer,
175-
offset..end_offset,
176-
MemoryInitKind::ImplicitlyInitialized,
177-
),
178-
);
179-
180-
// actual hal barrier & operation
181-
let dst_barrier =
182-
dst_pending.map(|pending| pending.into_hal(&dst_buffer, &snatch_guard));
183-
let cmd_buf_raw = cmd_buf_data.encoder.open()?;
184-
unsafe {
185-
cmd_buf_raw.transition_buffers(dst_barrier.as_slice());
186-
cmd_buf_raw.clear_buffer(dst_raw, offset..end_offset);
187-
}
188-
189-
Ok(())
122+
clear_buffer(cmd_buf_data, hub, &cmd_enc, dst, offset, size)
190123
})
191124
}
192125

@@ -204,83 +137,181 @@ impl Global {
204137
let cmd_enc = hub.command_encoders.get(command_encoder_id);
205138
let mut cmd_buf_data = cmd_enc.data.lock();
206139
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), ClearError> {
207-
#[cfg(feature = "trace")]
208-
if let Some(ref mut list) = cmd_buf_data.trace_commands {
209-
list.push(TraceCommand::ClearTexture {
210-
dst,
211-
subresource_range: *subresource_range,
212-
});
213-
}
140+
clear_texture_cmd(cmd_buf_data, hub, &cmd_enc, dst, subresource_range)
141+
})
142+
}
143+
}
214144

215-
cmd_enc.device.check_is_valid()?;
145+
pub(super) fn clear_buffer(
146+
cmd_buf_data: &mut CommandBufferMutable,
147+
hub: &crate::hub::Hub,
148+
cmd_enc: &Arc<CommandEncoder>,
149+
dst: BufferId,
150+
offset: BufferAddress,
151+
size: Option<BufferAddress>,
152+
) -> Result<(), ClearError> {
153+
#[cfg(feature = "trace")]
154+
if let Some(ref mut list) = cmd_buf_data.trace_commands {
155+
list.push(TraceCommand::ClearBuffer { dst, offset, size });
156+
}
216157

217-
cmd_enc
218-
.device
219-
.require_features(wgt::Features::CLEAR_TEXTURE)?;
158+
cmd_enc.device.check_is_valid()?;
220159

221-
let dst_texture = hub.textures.get(dst).get()?;
160+
let dst_buffer = hub.buffers.get(dst).get()?;
222161

223-
dst_texture.same_device_as(cmd_enc.as_ref())?;
162+
dst_buffer.same_device_as(cmd_enc.as_ref())?;
224163

225-
// Check if subresource aspects are valid.
226-
let clear_aspects =
227-
hal::FormatAspects::new(dst_texture.desc.format, subresource_range.aspect);
228-
if clear_aspects.is_empty() {
229-
return Err(ClearError::MissingTextureAspect {
230-
texture_format: dst_texture.desc.format,
231-
subresource_range_aspects: subresource_range.aspect,
232-
});
233-
};
164+
let dst_pending = cmd_buf_data
165+
.trackers
166+
.buffers
167+
.set_single(&dst_buffer, wgt::BufferUses::COPY_DST);
234168

235-
// Check if subresource level range is valid
236-
let subresource_mip_range =
237-
subresource_range.mip_range(dst_texture.full_range.mips.end);
238-
if dst_texture.full_range.mips.start > subresource_mip_range.start
239-
|| dst_texture.full_range.mips.end < subresource_mip_range.end
240-
{
241-
return Err(ClearError::InvalidTextureLevelRange {
242-
texture_level_range: dst_texture.full_range.mips.clone(),
243-
subresource_base_mip_level: subresource_range.base_mip_level,
244-
subresource_mip_level_count: subresource_range.mip_level_count,
245-
});
246-
}
247-
// Check if subresource layer range is valid
248-
let subresource_layer_range =
249-
subresource_range.layer_range(dst_texture.full_range.layers.end);
250-
if dst_texture.full_range.layers.start > subresource_layer_range.start
251-
|| dst_texture.full_range.layers.end < subresource_layer_range.end
252-
{
253-
return Err(ClearError::InvalidTextureLayerRange {
254-
texture_layer_range: dst_texture.full_range.layers.clone(),
255-
subresource_base_array_layer: subresource_range.base_array_layer,
256-
subresource_array_layer_count: subresource_range.array_layer_count,
257-
});
258-
}
169+
let snatch_guard = dst_buffer.device.snatchable_lock.read();
170+
let dst_raw = dst_buffer.try_raw(&snatch_guard)?;
171+
dst_buffer.check_usage(BufferUsages::COPY_DST)?;
259172

260-
let device = &cmd_enc.device;
261-
device.check_is_valid()?;
262-
let (encoder, tracker) = cmd_buf_data.open_encoder_and_tracker()?;
263-
264-
let snatch_guard = device.snatchable_lock.read();
265-
clear_texture(
266-
&dst_texture,
267-
TextureInitRange {
268-
mip_range: subresource_mip_range,
269-
layer_range: subresource_layer_range,
270-
},
271-
encoder,
272-
&mut tracker.textures,
273-
&device.alignments,
274-
device.zero_buffer.as_ref(),
275-
&snatch_guard,
276-
device.instance_flags,
277-
)?;
278-
279-
Ok(())
280-
})
173+
// Check if offset & size are valid.
174+
if offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
175+
return Err(ClearError::UnalignedBufferOffset(offset));
176+
}
177+
178+
let size = size.unwrap_or(dst_buffer.size.saturating_sub(offset));
179+
if size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
180+
return Err(ClearError::UnalignedFillSize(size));
181+
}
182+
let end_offset =
183+
offset
184+
.checked_add(size)
185+
.ok_or(ClearError::OffsetPlusSizeExceeds64BitBounds {
186+
start_offset: offset,
187+
requested_size: size,
188+
})?;
189+
if end_offset > dst_buffer.size {
190+
return Err(ClearError::BufferOverrun {
191+
start_offset: offset,
192+
end_offset,
193+
buffer_size: dst_buffer.size,
194+
});
281195
}
196+
197+
if offset == end_offset {
198+
log::trace!("Ignoring fill_buffer of size 0");
199+
return Ok(());
200+
}
201+
202+
// Mark dest as initialized.
203+
cmd_buf_data.buffer_memory_init_actions.extend(
204+
dst_buffer.initialization_status.read().create_action(
205+
&dst_buffer,
206+
offset..end_offset,
207+
MemoryInitKind::ImplicitlyInitialized,
208+
),
209+
);
210+
211+
// actual hal barrier & operation
212+
let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, &snatch_guard));
213+
let cmd_buf_raw = cmd_buf_data.encoder.open()?;
214+
unsafe {
215+
cmd_buf_raw.transition_buffers(dst_barrier.as_slice());
216+
cmd_buf_raw.clear_buffer(dst_raw, offset..end_offset);
217+
}
218+
219+
Ok(())
220+
}
221+
222+
/// Validate and encode a "Clear Texture" command.
223+
///
224+
/// This function implements `CommandEncoder::clear_texture` when invoked via
225+
/// the command encoder APIs or trace playback. It has the suffix `_cmd` to
226+
/// distinguish it from [`clear_texture`]. [`clear_texture`], used internally by
227+
/// this function, is a lower-level function that encodes a texture clear
228+
/// operation without validating it.
229+
pub(super) fn clear_texture_cmd(
230+
cmd_buf_data: &mut CommandBufferMutable,
231+
hub: &crate::hub::Hub,
232+
cmd_enc: &Arc<CommandEncoder>,
233+
dst: TextureId,
234+
subresource_range: &ImageSubresourceRange,
235+
) -> Result<(), ClearError> {
236+
#[cfg(feature = "trace")]
237+
if let Some(ref mut list) = cmd_buf_data.trace_commands {
238+
list.push(TraceCommand::ClearTexture {
239+
dst,
240+
subresource_range: *subresource_range,
241+
});
242+
}
243+
244+
cmd_enc.device.check_is_valid()?;
245+
246+
cmd_enc
247+
.device
248+
.require_features(wgt::Features::CLEAR_TEXTURE)?;
249+
250+
let dst_texture = hub.textures.get(dst).get()?;
251+
252+
dst_texture.same_device_as(cmd_enc.as_ref())?;
253+
254+
// Check if subresource aspects are valid.
255+
let clear_aspects = hal::FormatAspects::new(dst_texture.desc.format, subresource_range.aspect);
256+
if clear_aspects.is_empty() {
257+
return Err(ClearError::MissingTextureAspect {
258+
texture_format: dst_texture.desc.format,
259+
subresource_range_aspects: subresource_range.aspect,
260+
});
261+
};
262+
263+
// Check if subresource level range is valid
264+
let subresource_mip_range = subresource_range.mip_range(dst_texture.full_range.mips.end);
265+
if dst_texture.full_range.mips.start > subresource_mip_range.start
266+
|| dst_texture.full_range.mips.end < subresource_mip_range.end
267+
{
268+
return Err(ClearError::InvalidTextureLevelRange {
269+
texture_level_range: dst_texture.full_range.mips.clone(),
270+
subresource_base_mip_level: subresource_range.base_mip_level,
271+
subresource_mip_level_count: subresource_range.mip_level_count,
272+
});
273+
}
274+
// Check if subresource layer range is valid
275+
let subresource_layer_range = subresource_range.layer_range(dst_texture.full_range.layers.end);
276+
if dst_texture.full_range.layers.start > subresource_layer_range.start
277+
|| dst_texture.full_range.layers.end < subresource_layer_range.end
278+
{
279+
return Err(ClearError::InvalidTextureLayerRange {
280+
texture_layer_range: dst_texture.full_range.layers.clone(),
281+
subresource_base_array_layer: subresource_range.base_array_layer,
282+
subresource_array_layer_count: subresource_range.array_layer_count,
283+
});
284+
}
285+
286+
let device = &cmd_enc.device;
287+
device.check_is_valid()?;
288+
let (encoder, tracker) = cmd_buf_data.open_encoder_and_tracker()?;
289+
290+
let snatch_guard = device.snatchable_lock.read();
291+
clear_texture(
292+
&dst_texture,
293+
TextureInitRange {
294+
mip_range: subresource_mip_range,
295+
layer_range: subresource_layer_range,
296+
},
297+
encoder,
298+
&mut tracker.textures,
299+
&device.alignments,
300+
device.zero_buffer.as_ref(),
301+
&snatch_guard,
302+
device.instance_flags,
303+
)?;
304+
305+
Ok(())
282306
}
283307

308+
/// Encode a texture clear operation.
309+
///
310+
/// This function encodes a texture clear operation without validating it.
311+
/// Texture clears requested via the API call this function via
312+
/// [`clear_texture_cmd`], which does the validation. This function is also
313+
/// called directly from various places within wgpu that need to clear a
314+
/// texture.
284315
pub(crate) fn clear_texture<T: TextureTrackerSetSingle>(
285316
dst_texture: &Arc<Texture>,
286317
range: TextureInitRange,

0 commit comments

Comments
 (0)