1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 mod allocator;
6 mod bind;
7 mod bundle;
8 mod clear;
9 mod compute;
10 mod draw;
11 mod query;
12 mod render;
13 mod transfer;
14 
15 pub(crate) use self::allocator::CommandAllocator;
16 pub use self::allocator::CommandAllocatorError;
17 pub use self::bundle::*;
18 pub use self::compute::*;
19 pub use self::draw::*;
20 pub use self::query::*;
21 pub use self::render::*;
22 pub use self::transfer::*;
23 
24 use crate::{
25     device::{all_buffer_stages, all_image_stages},
26     hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Storage, Token},
27     id,
28     memory_init_tracker::MemoryInitTrackerAction,
29     resource::{Buffer, Texture},
30     track::{BufferState, ResourceTracker, TextureState, TrackerSet},
31     Label, PrivateFeatures, Stored,
32 };
33 
34 use hal::command::CommandBuffer as _;
35 use smallvec::SmallVec;
36 use thiserror::Error;
37 
38 use std::thread::ThreadId;
39 
40 const PUSH_CONSTANT_CLEAR_ARRAY: &[u32] = &[0_u32; 64];
41 
42 #[derive(Debug)]
43 enum CommandEncoderStatus {
44     Recording,
45     Finished,
46     Error,
47 }
48 
49 #[derive(Debug)]
50 pub struct CommandBuffer<B: hal::Backend> {
51     pub(crate) raw: Vec<B::CommandBuffer>,
52     status: CommandEncoderStatus,
53     recorded_thread_id: ThreadId,
54     pub(crate) device_id: Stored<id::DeviceId>,
55     pub(crate) trackers: TrackerSet,
56     pub(crate) used_swap_chains: SmallVec<[Stored<id::SwapChainId>; 1]>,
57     pub(crate) buffer_memory_init_actions: Vec<MemoryInitTrackerAction<id::BufferId>>,
58     limits: wgt::Limits,
59     downlevel: wgt::DownlevelProperties,
60     private_features: PrivateFeatures,
61     support_fill_buffer_texture: bool,
62     has_labels: bool,
63     #[cfg(feature = "trace")]
64     pub(crate) commands: Option<Vec<crate::device::trace::Command>>,
65     #[cfg(debug_assertions)]
66     pub(crate) label: String,
67 }
68 
69 impl<B: GfxBackend> CommandBuffer<B> {
get_encoder_mut( storage: &mut Storage<Self, id::CommandEncoderId>, id: id::CommandEncoderId, ) -> Result<&mut Self, CommandEncoderError>70     fn get_encoder_mut(
71         storage: &mut Storage<Self, id::CommandEncoderId>,
72         id: id::CommandEncoderId,
73     ) -> Result<&mut Self, CommandEncoderError> {
74         match storage.get_mut(id) {
75             Ok(cmd_buf) => match cmd_buf.status {
76                 CommandEncoderStatus::Recording => Ok(cmd_buf),
77                 CommandEncoderStatus::Finished => Err(CommandEncoderError::NotRecording),
78                 CommandEncoderStatus::Error => Err(CommandEncoderError::Invalid),
79             },
80             Err(_) => Err(CommandEncoderError::Invalid),
81         }
82     }
83 
is_finished(&self) -> bool84     pub fn is_finished(&self) -> bool {
85         match self.status {
86             CommandEncoderStatus::Finished => true,
87             _ => false,
88         }
89     }
90 
insert_barriers( raw: &mut B::CommandBuffer, base: &mut TrackerSet, head_buffers: &ResourceTracker<BufferState>, head_textures: &ResourceTracker<TextureState>, buffer_guard: &Storage<Buffer<B>, id::BufferId>, texture_guard: &Storage<Texture<B>, id::TextureId>, )91     pub(crate) fn insert_barriers(
92         raw: &mut B::CommandBuffer,
93         base: &mut TrackerSet,
94         head_buffers: &ResourceTracker<BufferState>,
95         head_textures: &ResourceTracker<TextureState>,
96         buffer_guard: &Storage<Buffer<B>, id::BufferId>,
97         texture_guard: &Storage<Texture<B>, id::TextureId>,
98     ) {
99         use hal::command::CommandBuffer as _;
100 
101         profiling::scope!("insert_barriers");
102         debug_assert_eq!(B::VARIANT, base.backend());
103 
104         let buffer_barriers = base.buffers.merge_replace(head_buffers).map(|pending| {
105             let buf = &buffer_guard[pending.id];
106             pending.into_hal(buf)
107         });
108         let texture_barriers = base.textures.merge_replace(head_textures).map(|pending| {
109             let tex = &texture_guard[pending.id];
110             pending.into_hal(tex)
111         });
112 
113         //TODO: be more deliberate about the stages
114         let stages = all_buffer_stages() | all_image_stages();
115         unsafe {
116             raw.pipeline_barrier(
117                 stages..stages,
118                 hal::memory::Dependencies::empty(),
119                 buffer_barriers.chain(texture_barriers),
120             );
121         }
122     }
123 }
124 
125 impl<B: hal::Backend> crate::hub::Resource for CommandBuffer<B> {
126     const TYPE: &'static str = "CommandBuffer";
127 
life_guard(&self) -> &crate::LifeGuard128     fn life_guard(&self) -> &crate::LifeGuard {
129         unreachable!()
130     }
131 
label(&self) -> &str132     fn label(&self) -> &str {
133         #[cfg(debug_assertions)]
134         return &self.label;
135         #[cfg(not(debug_assertions))]
136         return "";
137     }
138 }
139 
140 #[derive(Copy, Clone, Debug)]
141 pub struct BasePassRef<'a, C> {
142     pub label: Option<&'a str>,
143     pub commands: &'a [C],
144     pub dynamic_offsets: &'a [wgt::DynamicOffset],
145     pub string_data: &'a [u8],
146     pub push_constant_data: &'a [u32],
147 }
148 
149 #[doc(hidden)]
150 #[derive(Debug)]
151 #[cfg_attr(
152     any(feature = "serial-pass", feature = "trace"),
153     derive(serde::Serialize)
154 )]
155 #[cfg_attr(
156     any(feature = "serial-pass", feature = "replay"),
157     derive(serde::Deserialize)
158 )]
159 pub struct BasePass<C> {
160     pub label: Option<String>,
161     pub commands: Vec<C>,
162     pub dynamic_offsets: Vec<wgt::DynamicOffset>,
163     pub string_data: Vec<u8>,
164     pub push_constant_data: Vec<u32>,
165 }
166 
167 impl<C: Clone> BasePass<C> {
new(label: &Label) -> Self168     fn new(label: &Label) -> Self {
169         Self {
170             label: label.as_ref().map(|cow| cow.to_string()),
171             commands: Vec::new(),
172             dynamic_offsets: Vec::new(),
173             string_data: Vec::new(),
174             push_constant_data: Vec::new(),
175         }
176     }
177 
178     #[cfg(feature = "trace")]
from_ref(base: BasePassRef<C>) -> Self179     fn from_ref(base: BasePassRef<C>) -> Self {
180         Self {
181             label: base.label.map(str::to_string),
182             commands: base.commands.to_vec(),
183             dynamic_offsets: base.dynamic_offsets.to_vec(),
184             string_data: base.string_data.to_vec(),
185             push_constant_data: base.push_constant_data.to_vec(),
186         }
187     }
188 
as_ref(&self) -> BasePassRef<C>189     pub fn as_ref(&self) -> BasePassRef<C> {
190         BasePassRef {
191             label: self.label.as_deref(),
192             commands: &self.commands,
193             dynamic_offsets: &self.dynamic_offsets,
194             string_data: &self.string_data,
195             push_constant_data: &self.push_constant_data,
196         }
197     }
198 }
199 
200 #[derive(Clone, Debug, Error)]
201 pub enum CommandEncoderError {
202     #[error("command encoder is invalid")]
203     Invalid,
204     #[error("command encoder must be active")]
205     NotRecording,
206 }
207 
208 impl<G: GlobalIdentityHandlerFactory> Global<G> {
command_encoder_finish<B: GfxBackend>( &self, encoder_id: id::CommandEncoderId, _desc: &wgt::CommandBufferDescriptor<Label>, ) -> (id::CommandBufferId, Option<CommandEncoderError>)209     pub fn command_encoder_finish<B: GfxBackend>(
210         &self,
211         encoder_id: id::CommandEncoderId,
212         _desc: &wgt::CommandBufferDescriptor<Label>,
213     ) -> (id::CommandBufferId, Option<CommandEncoderError>) {
214         profiling::scope!("finish", "CommandEncoder");
215 
216         let hub = B::hub(self);
217         let mut token = Token::root();
218         let (swap_chain_guard, mut token) = hub.swap_chains.read(&mut token);
219         //TODO: actually close the last recorded command buffer
220         let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token);
221 
222         let error = match CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id) {
223             Ok(cmd_buf) => {
224                 cmd_buf.status = CommandEncoderStatus::Finished;
225                 // stop tracking the swapchain image, if used
226                 for sc_id in cmd_buf.used_swap_chains.iter() {
227                     let view_id = swap_chain_guard[sc_id.value]
228                         .acquired_view_id
229                         .as_ref()
230                         .expect("Used swap chain frame has already presented");
231                     cmd_buf.trackers.views.remove(view_id.value);
232                 }
233                 log::trace!("Command buffer {:?} {:#?}", encoder_id, cmd_buf.trackers);
234                 None
235             }
236             Err(e) => Some(e),
237         };
238 
239         (encoder_id, error)
240     }
241 
command_encoder_push_debug_group<B: GfxBackend>( &self, encoder_id: id::CommandEncoderId, label: &str, ) -> Result<(), CommandEncoderError>242     pub fn command_encoder_push_debug_group<B: GfxBackend>(
243         &self,
244         encoder_id: id::CommandEncoderId,
245         label: &str,
246     ) -> Result<(), CommandEncoderError> {
247         profiling::scope!("push_debug_group", "CommandEncoder");
248 
249         let hub = B::hub(self);
250         let mut token = Token::root();
251 
252         let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token);
253         let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id)?;
254         let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap();
255 
256         unsafe {
257             cmd_buf_raw.begin_debug_marker(label, 0);
258         }
259         Ok(())
260     }
261 
command_encoder_insert_debug_marker<B: GfxBackend>( &self, encoder_id: id::CommandEncoderId, label: &str, ) -> Result<(), CommandEncoderError>262     pub fn command_encoder_insert_debug_marker<B: GfxBackend>(
263         &self,
264         encoder_id: id::CommandEncoderId,
265         label: &str,
266     ) -> Result<(), CommandEncoderError> {
267         profiling::scope!("insert_debug_marker", "CommandEncoder");
268 
269         let hub = B::hub(self);
270         let mut token = Token::root();
271 
272         let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token);
273         let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id)?;
274         let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap();
275 
276         unsafe {
277             cmd_buf_raw.insert_debug_marker(label, 0);
278         }
279         Ok(())
280     }
281 
command_encoder_pop_debug_group<B: GfxBackend>( &self, encoder_id: id::CommandEncoderId, ) -> Result<(), CommandEncoderError>282     pub fn command_encoder_pop_debug_group<B: GfxBackend>(
283         &self,
284         encoder_id: id::CommandEncoderId,
285     ) -> Result<(), CommandEncoderError> {
286         profiling::scope!("pop_debug_marker", "CommandEncoder");
287 
288         let hub = B::hub(self);
289         let mut token = Token::root();
290 
291         let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token);
292         let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id)?;
293         let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap();
294 
295         unsafe {
296             cmd_buf_raw.end_debug_marker();
297         }
298         Ok(())
299     }
300 }
301 
push_constant_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn) where PushFn: FnMut(u32, &[u32]),302 fn push_constant_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
303 where
304     PushFn: FnMut(u32, &[u32]),
305 {
306     let mut count_words = 0_u32;
307     let size_words = size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT;
308     while count_words < size_words {
309         let count_bytes = count_words * wgt::PUSH_CONSTANT_ALIGNMENT;
310         let size_to_write_words =
311             (size_words - count_words).min(PUSH_CONSTANT_CLEAR_ARRAY.len() as u32);
312 
313         push_fn(
314             offset + count_bytes,
315             &PUSH_CONSTANT_CLEAR_ARRAY[0..size_to_write_words as usize],
316         );
317 
318         count_words += size_to_write_words;
319     }
320 }
321 
322 #[derive(Debug)]
323 struct StateChange<T> {
324     last_state: Option<T>,
325 }
326 
327 impl<T: Copy + PartialEq> StateChange<T> {
new() -> Self328     fn new() -> Self {
329         Self { last_state: None }
330     }
set_and_check_redundant(&mut self, new_state: T) -> bool331     fn set_and_check_redundant(&mut self, new_state: T) -> bool {
332         let already_set = self.last_state == Some(new_state);
333         self.last_state = Some(new_state);
334         already_set
335     }
is_unset(&self) -> bool336     fn is_unset(&self) -> bool {
337         self.last_state.is_none()
338     }
reset(&mut self)339     fn reset(&mut self) {
340         self.last_state = None;
341     }
342 }
343 
344 trait MapPassErr<T, O> {
map_pass_err(self, scope: PassErrorScope) -> Result<T, O>345     fn map_pass_err(self, scope: PassErrorScope) -> Result<T, O>;
346 }
347 
348 #[derive(Clone, Copy, Debug, Error)]
349 pub enum PassErrorScope {
350     #[error("In a bundle parameter")]
351     Bundle,
352     #[error("In a pass parameter")]
353     Pass(id::CommandEncoderId),
354     #[error("In a set_bind_group command")]
355     SetBindGroup(id::BindGroupId),
356     #[error("In a set_pipeline command")]
357     SetPipelineRender(id::RenderPipelineId),
358     #[error("In a set_pipeline command")]
359     SetPipelineCompute(id::ComputePipelineId),
360     #[error("In a set_push_constant command")]
361     SetPushConstant,
362     #[error("In a set_vertex_buffer command")]
363     SetVertexBuffer(id::BufferId),
364     #[error("In a set_index_buffer command")]
365     SetIndexBuffer(id::BufferId),
366     #[error("In a set_viewport command")]
367     SetViewport,
368     #[error("In a set_scissor_rect command")]
369     SetScissorRect,
370     #[error("In a draw command, indexed:{indexed} indirect:{indirect}")]
371     Draw {
372         indexed: bool,
373         indirect: bool,
374         pipeline: Option<id::RenderPipelineId>,
375     },
376     #[error("While resetting queries after the renderpass was ran")]
377     QueryReset,
378     #[error("In a write_timestamp command")]
379     WriteTimestamp,
380     #[error("In a begin_pipeline_statistics_query command")]
381     BeginPipelineStatisticsQuery,
382     #[error("In a end_pipeline_statistics_query command")]
383     EndPipelineStatisticsQuery,
384     #[error("In a execute_bundle command")]
385     ExecuteBundle,
386     #[error("In a dispatch command, indirect:{indirect}")]
387     Dispatch {
388         indirect: bool,
389         pipeline: Option<id::ComputePipelineId>,
390     },
391     #[error("In a pop_debug_group command")]
392     PopDebugGroup,
393 }
394