1 mod bind;
2 mod bundle;
3 mod clear;
4 mod compute;
5 mod draw;
6 mod memory_init;
7 mod query;
8 mod render;
9 mod transfer;
10 
11 pub(crate) use self::clear::clear_texture_no_device;
12 pub use self::{
13     bundle::*, clear::ClearError, compute::*, draw::*, query::*, render::*, transfer::*,
14 };
15 
16 use self::memory_init::CommandBufferTextureMemoryActions;
17 
18 use crate::error::{ErrorFormatter, PrettyError};
19 use crate::init_tracker::BufferInitTrackerAction;
20 use crate::{
21     hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
22     id,
23     resource::{Buffer, Texture},
24     track::{BufferState, ResourceTracker, TextureState, TrackerSet},
25     Label, Stored,
26 };
27 
28 use hal::CommandEncoder as _;
29 use thiserror::Error;
30 
31 #[cfg(feature = "trace")]
32 use crate::device::trace::Command as TraceCommand;
33 
34 const PUSH_CONSTANT_CLEAR_ARRAY: &[u32] = &[0_u32; 64];
35 
36 #[derive(Debug)]
37 enum CommandEncoderStatus {
38     Recording,
39     Finished,
40     Error,
41 }
42 
43 struct CommandEncoder<A: hal::Api> {
44     raw: A::CommandEncoder,
45     list: Vec<A::CommandBuffer>,
46     is_open: bool,
47     label: Option<String>,
48 }
49 
50 //TODO: handle errors better
51 impl<A: hal::Api> CommandEncoder<A> {
close(&mut self)52     fn close(&mut self) {
53         if self.is_open {
54             self.is_open = false;
55             let cmd_buf = unsafe { self.raw.end_encoding().unwrap() };
56             self.list.push(cmd_buf);
57         }
58     }
59 
discard(&mut self)60     fn discard(&mut self) {
61         if self.is_open {
62             self.is_open = false;
63             unsafe { self.raw.discard_encoding() };
64         }
65     }
66 
open(&mut self) -> &mut A::CommandEncoder67     fn open(&mut self) -> &mut A::CommandEncoder {
68         if !self.is_open {
69             self.is_open = true;
70             let label = self.label.as_deref();
71             unsafe { self.raw.begin_encoding(label).unwrap() };
72         }
73         &mut self.raw
74     }
75 
open_pass(&mut self, label: Option<&str>)76     fn open_pass(&mut self, label: Option<&str>) {
77         self.is_open = true;
78         unsafe { self.raw.begin_encoding(label).unwrap() };
79     }
80 }
81 
82 pub struct BakedCommands<A: hal::Api> {
83     pub(crate) encoder: A::CommandEncoder,
84     pub(crate) list: Vec<A::CommandBuffer>,
85     pub(crate) trackers: TrackerSet,
86     buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
87     texture_memory_actions: CommandBufferTextureMemoryActions,
88 }
89 
90 pub(crate) struct DestroyedBufferError(pub id::BufferId);
91 pub(crate) struct DestroyedTextureError(pub id::TextureId);
92 
93 pub struct CommandBuffer<A: hal::Api> {
94     encoder: CommandEncoder<A>,
95     status: CommandEncoderStatus,
96     pub(crate) device_id: Stored<id::DeviceId>,
97     pub(crate) trackers: TrackerSet,
98     buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
99     texture_memory_actions: CommandBufferTextureMemoryActions,
100     limits: wgt::Limits,
101     support_clear_texture: bool,
102     #[cfg(feature = "trace")]
103     pub(crate) commands: Option<Vec<TraceCommand>>,
104 }
105 
106 impl<A: HalApi> CommandBuffer<A> {
new( encoder: A::CommandEncoder, device_id: Stored<id::DeviceId>, limits: wgt::Limits, _downlevel: wgt::DownlevelCapabilities, features: wgt::Features, #[cfg(feature = "trace")] enable_tracing: bool, label: &Label, ) -> Self107     pub(crate) fn new(
108         encoder: A::CommandEncoder,
109         device_id: Stored<id::DeviceId>,
110         limits: wgt::Limits,
111         _downlevel: wgt::DownlevelCapabilities,
112         features: wgt::Features,
113         #[cfg(feature = "trace")] enable_tracing: bool,
114         label: &Label,
115     ) -> Self {
116         CommandBuffer {
117             encoder: CommandEncoder {
118                 raw: encoder,
119                 is_open: false,
120                 list: Vec::new(),
121                 label: crate::LabelHelpers::borrow_option(label).map(|s| s.to_string()),
122             },
123             status: CommandEncoderStatus::Recording,
124             device_id,
125             trackers: TrackerSet::new(A::VARIANT),
126             buffer_memory_init_actions: Default::default(),
127             texture_memory_actions: Default::default(),
128             limits,
129             support_clear_texture: features.contains(wgt::Features::CLEAR_TEXTURE),
130             #[cfg(feature = "trace")]
131             commands: if enable_tracing {
132                 Some(Vec::new())
133             } else {
134                 None
135             },
136         }
137     }
138 
insert_barriers( raw: &mut A::CommandEncoder, base: &mut TrackerSet, head_buffers: &ResourceTracker<BufferState>, head_textures: &ResourceTracker<TextureState>, buffer_guard: &Storage<Buffer<A>, id::BufferId>, texture_guard: &Storage<Texture<A>, id::TextureId>, )139     pub(crate) fn insert_barriers(
140         raw: &mut A::CommandEncoder,
141         base: &mut TrackerSet,
142         head_buffers: &ResourceTracker<BufferState>,
143         head_textures: &ResourceTracker<TextureState>,
144         buffer_guard: &Storage<Buffer<A>, id::BufferId>,
145         texture_guard: &Storage<Texture<A>, id::TextureId>,
146     ) {
147         profiling::scope!("insert_barriers");
148         debug_assert_eq!(A::VARIANT, base.backend());
149 
150         let buffer_barriers = base.buffers.merge_replace(head_buffers).map(|pending| {
151             let buf = &buffer_guard[pending.id];
152             pending.into_hal(buf)
153         });
154         let texture_barriers = base.textures.merge_replace(head_textures).map(|pending| {
155             let tex = &texture_guard[pending.id];
156             pending.into_hal(tex)
157         });
158 
159         unsafe {
160             raw.transition_buffers(buffer_barriers);
161             raw.transition_textures(texture_barriers);
162         }
163     }
164 }
165 
166 impl<A: hal::Api> CommandBuffer<A> {
get_encoder_mut( storage: &mut Storage<Self, id::CommandEncoderId>, id: id::CommandEncoderId, ) -> Result<&mut Self, CommandEncoderError>167     fn get_encoder_mut(
168         storage: &mut Storage<Self, id::CommandEncoderId>,
169         id: id::CommandEncoderId,
170     ) -> Result<&mut Self, CommandEncoderError> {
171         match storage.get_mut(id) {
172             Ok(cmd_buf) => match cmd_buf.status {
173                 CommandEncoderStatus::Recording => Ok(cmd_buf),
174                 CommandEncoderStatus::Finished => Err(CommandEncoderError::NotRecording),
175                 CommandEncoderStatus::Error => Err(CommandEncoderError::Invalid),
176             },
177             Err(_) => Err(CommandEncoderError::Invalid),
178         }
179     }
180 
is_finished(&self) -> bool181     pub fn is_finished(&self) -> bool {
182         match self.status {
183             CommandEncoderStatus::Finished => true,
184             _ => false,
185         }
186     }
187 
into_baked(self) -> BakedCommands<A>188     pub(crate) fn into_baked(self) -> BakedCommands<A> {
189         BakedCommands {
190             encoder: self.encoder.raw,
191             list: self.encoder.list,
192             trackers: self.trackers,
193             buffer_memory_init_actions: self.buffer_memory_init_actions,
194             texture_memory_actions: self.texture_memory_actions,
195         }
196     }
197 }
198 
199 impl<A: hal::Api> crate::hub::Resource for CommandBuffer<A> {
200     const TYPE: &'static str = "CommandBuffer";
201 
life_guard(&self) -> &crate::LifeGuard202     fn life_guard(&self) -> &crate::LifeGuard {
203         unreachable!()
204     }
205 
label(&self) -> &str206     fn label(&self) -> &str {
207         self.encoder.label.as_ref().map_or("", |s| s.as_str())
208     }
209 }
210 
211 #[derive(Copy, Clone, Debug)]
212 pub struct BasePassRef<'a, C> {
213     pub label: Option<&'a str>,
214     pub commands: &'a [C],
215     pub dynamic_offsets: &'a [wgt::DynamicOffset],
216     pub string_data: &'a [u8],
217     pub push_constant_data: &'a [u32],
218 }
219 
220 #[doc(hidden)]
221 #[derive(Debug)]
222 #[cfg_attr(
223     any(feature = "serial-pass", feature = "trace"),
224     derive(serde::Serialize)
225 )]
226 #[cfg_attr(
227     any(feature = "serial-pass", feature = "replay"),
228     derive(serde::Deserialize)
229 )]
230 pub struct BasePass<C> {
231     pub label: Option<String>,
232     pub commands: Vec<C>,
233     pub dynamic_offsets: Vec<wgt::DynamicOffset>,
234     pub string_data: Vec<u8>,
235     pub push_constant_data: Vec<u32>,
236 }
237 
238 impl<C: Clone> BasePass<C> {
new(label: &Label) -> Self239     fn new(label: &Label) -> Self {
240         Self {
241             label: label.as_ref().map(|cow| cow.to_string()),
242             commands: Vec::new(),
243             dynamic_offsets: Vec::new(),
244             string_data: Vec::new(),
245             push_constant_data: Vec::new(),
246         }
247     }
248 
249     #[cfg(feature = "trace")]
from_ref(base: BasePassRef<C>) -> Self250     fn from_ref(base: BasePassRef<C>) -> Self {
251         Self {
252             label: base.label.map(str::to_string),
253             commands: base.commands.to_vec(),
254             dynamic_offsets: base.dynamic_offsets.to_vec(),
255             string_data: base.string_data.to_vec(),
256             push_constant_data: base.push_constant_data.to_vec(),
257         }
258     }
259 
as_ref(&self) -> BasePassRef<C>260     pub fn as_ref(&self) -> BasePassRef<C> {
261         BasePassRef {
262             label: self.label.as_deref(),
263             commands: &self.commands,
264             dynamic_offsets: &self.dynamic_offsets,
265             string_data: &self.string_data,
266             push_constant_data: &self.push_constant_data,
267         }
268     }
269 }
270 
271 #[derive(Clone, Debug, Error)]
272 pub enum CommandEncoderError {
273     #[error("command encoder is invalid")]
274     Invalid,
275     #[error("command encoder must be active")]
276     NotRecording,
277 }
278 
279 impl<G: GlobalIdentityHandlerFactory> Global<G> {
command_encoder_finish<A: HalApi>( &self, encoder_id: id::CommandEncoderId, _desc: &wgt::CommandBufferDescriptor<Label>, ) -> (id::CommandBufferId, Option<CommandEncoderError>)280     pub fn command_encoder_finish<A: HalApi>(
281         &self,
282         encoder_id: id::CommandEncoderId,
283         _desc: &wgt::CommandBufferDescriptor<Label>,
284     ) -> (id::CommandBufferId, Option<CommandEncoderError>) {
285         profiling::scope!("finish", "CommandEncoder");
286 
287         let hub = A::hub(self);
288         let mut token = Token::root();
289         let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token);
290 
291         let error = match cmd_buf_guard.get_mut(encoder_id) {
292             Ok(cmd_buf) => match cmd_buf.status {
293                 CommandEncoderStatus::Recording => {
294                     cmd_buf.encoder.close();
295                     cmd_buf.status = CommandEncoderStatus::Finished;
296                     //Note: if we want to stop tracking the swapchain texture view,
297                     // this is the place to do it.
298                     log::trace!("Command buffer {:?} {:#?}", encoder_id, cmd_buf.trackers);
299                     None
300                 }
301                 CommandEncoderStatus::Finished => Some(CommandEncoderError::NotRecording),
302                 CommandEncoderStatus::Error => {
303                     cmd_buf.encoder.discard();
304                     Some(CommandEncoderError::Invalid)
305                 }
306             },
307             Err(_) => Some(CommandEncoderError::Invalid),
308         };
309 
310         (encoder_id, error)
311     }
312 
command_encoder_push_debug_group<A: HalApi>( &self, encoder_id: id::CommandEncoderId, label: &str, ) -> Result<(), CommandEncoderError>313     pub fn command_encoder_push_debug_group<A: HalApi>(
314         &self,
315         encoder_id: id::CommandEncoderId,
316         label: &str,
317     ) -> Result<(), CommandEncoderError> {
318         profiling::scope!("push_debug_group", "CommandEncoder");
319 
320         let hub = A::hub(self);
321         let mut token = Token::root();
322 
323         let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token);
324         let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id)?;
325 
326         #[cfg(feature = "trace")]
327         if let Some(ref mut list) = cmd_buf.commands {
328             list.push(TraceCommand::PushDebugGroup(label.to_string()));
329         }
330 
331         let cmd_buf_raw = cmd_buf.encoder.open();
332         unsafe {
333             cmd_buf_raw.begin_debug_marker(label);
334         }
335         Ok(())
336     }
337 
command_encoder_insert_debug_marker<A: HalApi>( &self, encoder_id: id::CommandEncoderId, label: &str, ) -> Result<(), CommandEncoderError>338     pub fn command_encoder_insert_debug_marker<A: HalApi>(
339         &self,
340         encoder_id: id::CommandEncoderId,
341         label: &str,
342     ) -> Result<(), CommandEncoderError> {
343         profiling::scope!("insert_debug_marker", "CommandEncoder");
344 
345         let hub = A::hub(self);
346         let mut token = Token::root();
347 
348         let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token);
349         let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id)?;
350 
351         #[cfg(feature = "trace")]
352         if let Some(ref mut list) = cmd_buf.commands {
353             list.push(TraceCommand::InsertDebugMarker(label.to_string()));
354         }
355 
356         let cmd_buf_raw = cmd_buf.encoder.open();
357         unsafe {
358             cmd_buf_raw.insert_debug_marker(label);
359         }
360         Ok(())
361     }
362 
command_encoder_pop_debug_group<A: HalApi>( &self, encoder_id: id::CommandEncoderId, ) -> Result<(), CommandEncoderError>363     pub fn command_encoder_pop_debug_group<A: HalApi>(
364         &self,
365         encoder_id: id::CommandEncoderId,
366     ) -> Result<(), CommandEncoderError> {
367         profiling::scope!("pop_debug_marker", "CommandEncoder");
368 
369         let hub = A::hub(self);
370         let mut token = Token::root();
371 
372         let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token);
373         let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id)?;
374 
375         #[cfg(feature = "trace")]
376         if let Some(ref mut list) = cmd_buf.commands {
377             list.push(TraceCommand::PopDebugGroup);
378         }
379 
380         let cmd_buf_raw = cmd_buf.encoder.open();
381         unsafe {
382             cmd_buf_raw.end_debug_marker();
383         }
384         Ok(())
385     }
386 }
387 
push_constant_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn) where PushFn: FnMut(u32, &[u32]),388 fn push_constant_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
389 where
390     PushFn: FnMut(u32, &[u32]),
391 {
392     let mut count_words = 0_u32;
393     let size_words = size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT;
394     while count_words < size_words {
395         let count_bytes = count_words * wgt::PUSH_CONSTANT_ALIGNMENT;
396         let size_to_write_words =
397             (size_words - count_words).min(PUSH_CONSTANT_CLEAR_ARRAY.len() as u32);
398 
399         push_fn(
400             offset + count_bytes,
401             &PUSH_CONSTANT_CLEAR_ARRAY[0..size_to_write_words as usize],
402         );
403 
404         count_words += size_to_write_words;
405     }
406 }
407 
408 #[derive(Debug)]
409 struct StateChange<T> {
410     last_state: Option<T>,
411 }
412 
413 impl<T: Copy + PartialEq> StateChange<T> {
new() -> Self414     fn new() -> Self {
415         Self { last_state: None }
416     }
set_and_check_redundant(&mut self, new_state: T) -> bool417     fn set_and_check_redundant(&mut self, new_state: T) -> bool {
418         let already_set = self.last_state == Some(new_state);
419         self.last_state = Some(new_state);
420         already_set
421     }
is_unset(&self) -> bool422     fn is_unset(&self) -> bool {
423         self.last_state.is_none()
424     }
reset(&mut self)425     fn reset(&mut self) {
426         self.last_state = None;
427     }
428 }
429 
430 trait MapPassErr<T, O> {
map_pass_err(self, scope: PassErrorScope) -> Result<T, O>431     fn map_pass_err(self, scope: PassErrorScope) -> Result<T, O>;
432 }
433 
434 #[derive(Clone, Copy, Debug, Error)]
435 pub enum PassErrorScope {
436     #[error("In a bundle parameter")]
437     Bundle,
438     #[error("In a pass parameter")]
439     Pass(id::CommandEncoderId),
440     #[error("In a set_bind_group command")]
441     SetBindGroup(id::BindGroupId),
442     #[error("In a set_pipeline command")]
443     SetPipelineRender(id::RenderPipelineId),
444     #[error("In a set_pipeline command")]
445     SetPipelineCompute(id::ComputePipelineId),
446     #[error("In a set_push_constant command")]
447     SetPushConstant,
448     #[error("In a set_vertex_buffer command")]
449     SetVertexBuffer(id::BufferId),
450     #[error("In a set_index_buffer command")]
451     SetIndexBuffer(id::BufferId),
452     #[error("In a set_viewport command")]
453     SetViewport,
454     #[error("In a set_scissor_rect command")]
455     SetScissorRect,
456     #[error("In a draw command, indexed:{indexed} indirect:{indirect}")]
457     Draw {
458         indexed: bool,
459         indirect: bool,
460         pipeline: Option<id::RenderPipelineId>,
461     },
462     #[error("While resetting queries after the renderpass was ran")]
463     QueryReset,
464     #[error("In a write_timestamp command")]
465     WriteTimestamp,
466     #[error("In a begin_pipeline_statistics_query command")]
467     BeginPipelineStatisticsQuery,
468     #[error("In a end_pipeline_statistics_query command")]
469     EndPipelineStatisticsQuery,
470     #[error("In a execute_bundle command")]
471     ExecuteBundle,
472     #[error("In a dispatch command, indirect:{indirect}")]
473     Dispatch {
474         indirect: bool,
475         pipeline: Option<id::ComputePipelineId>,
476     },
477     #[error("In a pop_debug_group command")]
478     PopDebugGroup,
479 }
480 
481 impl PrettyError for PassErrorScope {
fmt_pretty(&self, fmt: &mut ErrorFormatter)482     fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
483         // This error is not in the error chain, only notes are needed
484         match *self {
485             Self::Pass(id) => {
486                 fmt.command_buffer_label(&id);
487             }
488             Self::SetBindGroup(id) => {
489                 fmt.bind_group_label(&id);
490             }
491             Self::SetPipelineRender(id) => {
492                 fmt.render_pipeline_label(&id);
493             }
494             Self::SetPipelineCompute(id) => {
495                 fmt.compute_pipeline_label(&id);
496             }
497             Self::SetVertexBuffer(id) => {
498                 fmt.buffer_label(&id);
499             }
500             Self::SetIndexBuffer(id) => {
501                 fmt.buffer_label(&id);
502             }
503             Self::Draw {
504                 pipeline: Some(id), ..
505             } => {
506                 fmt.render_pipeline_label(&id);
507             }
508             Self::Dispatch {
509                 pipeline: Some(id), ..
510             } => {
511                 fmt.compute_pipeline_label(&id);
512             }
513             _ => {}
514         }
515     }
516 }
517