mod bind; mod bundle; mod clear; mod compute; mod draw; mod memory_init; mod query; mod render; mod transfer; use std::slice; use std::sync::Arc; pub(crate) use self::clear::clear_texture; pub use self::{ bundle::*, clear::ClearError, compute::*, draw::*, query::*, render::*, transfer::*, }; use self::memory_init::CommandBufferTextureMemoryActions; use crate::device::{Device, DeviceError}; use crate::error::{ErrorFormatter, PrettyError}; use crate::hub::Hub; use crate::id::CommandBufferId; use crate::snatch::SnatchGuard; use crate::init_tracker::BufferInitTrackerAction; use crate::resource::{Resource, ResourceInfo, ResourceType}; use crate::track::{Tracker, UsageScope}; use crate::{api_log, global::Global, hal_api::HalApi, id, resource_log, Label}; use hal::CommandEncoder as _; use parking_lot::Mutex; use thiserror::Error; #[cfg(feature = "trace")] use crate::device::trace::Command as TraceCommand; const PUSH_CONSTANT_CLEAR_ARRAY: &[u32] = &[0_u32; 64]; #[derive(Debug)] pub(crate) enum CommandEncoderStatus { Recording, Finished, Error, } pub(crate) struct CommandEncoder { raw: A::CommandEncoder, list: Vec, is_open: bool, label: Option, } //TODO: handle errors better impl CommandEncoder { /// Closes the live encoder fn close_and_swap(&mut self) -> Result<(), DeviceError> { if self.is_open { self.is_open = false; let new = unsafe { self.raw.end_encoding()? }; self.list.insert(self.list.len() - 1, new); } Ok(()) } fn close(&mut self) -> Result<(), DeviceError> { if self.is_open { self.is_open = false; let cmd_buf = unsafe { self.raw.end_encoding()? }; self.list.push(cmd_buf); } Ok(()) } pub(crate) fn discard(&mut self) { if self.is_open { self.is_open = false; unsafe { self.raw.discard_encoding() }; } } fn open(&mut self) -> Result<&mut A::CommandEncoder, DeviceError> { if !self.is_open { self.is_open = true; let label = self.label.as_deref(); unsafe { self.raw.begin_encoding(label)? }; } Ok(&mut self.raw) } fn open_pass(&mut self, label: Option<&str>) -> Result<(), DeviceError> { self.is_open = true; unsafe { self.raw.begin_encoding(label)? }; Ok(()) } } pub struct BakedCommands { pub(crate) encoder: A::CommandEncoder, pub(crate) list: Vec, pub(crate) trackers: Tracker, buffer_memory_init_actions: Vec>, texture_memory_actions: CommandBufferTextureMemoryActions, } pub(crate) struct DestroyedBufferError(pub id::BufferId); pub(crate) struct DestroyedTextureError(pub id::TextureId); pub struct CommandBufferMutable { pub(crate) encoder: CommandEncoder, status: CommandEncoderStatus, pub(crate) trackers: Tracker, buffer_memory_init_actions: Vec>, texture_memory_actions: CommandBufferTextureMemoryActions, pub(crate) pending_query_resets: QueryResetMap, #[cfg(feature = "trace")] pub(crate) commands: Option>, } impl CommandBufferMutable { pub(crate) fn open_encoder_and_tracker( &mut self, ) -> Result<(&mut A::CommandEncoder, &mut Tracker), DeviceError> { let encoder = self.encoder.open()?; let tracker = &mut self.trackers; Ok((encoder, tracker)) } } pub struct CommandBuffer { pub(crate) device: Arc>, limits: wgt::Limits, support_clear_texture: bool, pub(crate) info: ResourceInfo>, pub(crate) data: Mutex>>, } impl Drop for CommandBuffer { fn drop(&mut self) { if self.data.lock().is_none() { return; } resource_log!("resource::CommandBuffer::drop {:?}", self.info.label()); let mut baked = self.extract_baked_commands(); unsafe { baked.encoder.reset_all(baked.list.into_iter()); } unsafe { use hal::Device; self.device.raw().destroy_command_encoder(baked.encoder); } } } impl CommandBuffer { pub(crate) fn new( encoder: A::CommandEncoder, device: &Arc>, #[cfg(feature = "trace")] enable_tracing: bool, label: Option, ) -> Self { CommandBuffer { device: device.clone(), limits: device.limits.clone(), support_clear_texture: device.features.contains(wgt::Features::CLEAR_TEXTURE), info: ResourceInfo::new( label .as_ref() .unwrap_or(&String::from("")) .as_str(), None, ), data: Mutex::new(Some(CommandBufferMutable { encoder: CommandEncoder { raw: encoder, is_open: false, list: Vec::new(), label, }, status: CommandEncoderStatus::Recording, trackers: Tracker::new(), buffer_memory_init_actions: Default::default(), texture_memory_actions: Default::default(), pending_query_resets: QueryResetMap::new(), #[cfg(feature = "trace")] commands: if enable_tracing { Some(Vec::new()) } else { None }, })), } } pub(crate) fn insert_barriers_from_tracker( raw: &mut A::CommandEncoder, base: &mut Tracker, head: &Tracker, snatch_guard: &SnatchGuard, ) { profiling::scope!("insert_barriers"); base.buffers.set_from_tracker(&head.buffers); base.textures.set_from_tracker(&head.textures); Self::drain_barriers(raw, base, snatch_guard); } pub(crate) fn insert_barriers_from_scope( raw: &mut A::CommandEncoder, base: &mut Tracker, head: &UsageScope, snatch_guard: &SnatchGuard, ) { profiling::scope!("insert_barriers"); base.buffers.set_from_usage_scope(&head.buffers); base.textures.set_from_usage_scope(&head.textures); Self::drain_barriers(raw, base, snatch_guard); } pub(crate) fn drain_barriers( raw: &mut A::CommandEncoder, base: &mut Tracker, snatch_guard: &SnatchGuard, ) { profiling::scope!("drain_barriers"); let buffer_barriers = base.buffers.drain_transitions(snatch_guard); let (transitions, textures) = base.textures.drain_transitions(snatch_guard); let texture_barriers = transitions .into_iter() .enumerate() .map(|(i, p)| p.into_hal(textures[i].unwrap().raw().unwrap())); unsafe { raw.transition_buffers(buffer_barriers); raw.transition_textures(texture_barriers); } } } impl CommandBuffer { fn get_encoder( hub: &Hub, id: id::CommandEncoderId, ) -> Result, CommandEncoderError> { let storage = hub.command_buffers.read(); match storage.get(id.transmute()) { Ok(cmd_buf) => match cmd_buf.data.lock().as_ref().unwrap().status { CommandEncoderStatus::Recording => Ok(cmd_buf.clone()), CommandEncoderStatus::Finished => Err(CommandEncoderError::NotRecording), CommandEncoderStatus::Error => Err(CommandEncoderError::Invalid), }, Err(_) => Err(CommandEncoderError::Invalid), } } pub fn is_finished(&self) -> bool { match self.data.lock().as_ref().unwrap().status { CommandEncoderStatus::Finished => true, _ => false, } } pub(crate) fn extract_baked_commands(&mut self) -> BakedCommands { log::trace!( "Extracting BakedCommands from CommandBuffer {:?}", self.info.label() ); let data = self.data.lock().take().unwrap(); BakedCommands { encoder: data.encoder.raw, list: data.encoder.list, trackers: data.trackers, buffer_memory_init_actions: data.buffer_memory_init_actions, texture_memory_actions: data.texture_memory_actions, } } pub(crate) fn from_arc_into_baked(self: Arc) -> BakedCommands { if let Some(mut command_buffer) = Arc::into_inner(self) { command_buffer.extract_baked_commands() } else { panic!("CommandBuffer cannot be destroyed because is still in use"); } } } impl Resource for CommandBuffer { const TYPE: ResourceType = "CommandBuffer"; type Marker = crate::id::markers::CommandBuffer; fn as_info(&self) -> &ResourceInfo { &self.info } fn as_info_mut(&mut self) -> &mut ResourceInfo { &mut self.info } fn label(&self) -> String { let str = match self.data.lock().as_ref().unwrap().encoder.label.as_ref() { Some(label) => label.clone(), _ => String::new(), }; str } } #[derive(Copy, Clone, Debug)] pub struct BasePassRef<'a, C> { pub label: Option<&'a str>, pub commands: &'a [C], pub dynamic_offsets: &'a [wgt::DynamicOffset], pub string_data: &'a [u8], pub push_constant_data: &'a [u32], } /// A stream of commands for a render pass or compute pass. /// /// This also contains side tables referred to by certain commands, /// like dynamic offsets for [`SetBindGroup`] or string data for /// [`InsertDebugMarker`]. /// /// Render passes use `BasePass`, whereas compute /// passes use `BasePass`. /// /// [`SetBindGroup`]: RenderCommand::SetBindGroup /// [`InsertDebugMarker`]: RenderCommand::InsertDebugMarker #[doc(hidden)] #[derive(Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct BasePass { pub label: Option, /// The stream of commands. pub commands: Vec, /// Dynamic offsets consumed by [`SetBindGroup`] commands in `commands`. /// /// Each successive `SetBindGroup` consumes the next /// [`num_dynamic_offsets`] values from this list. pub dynamic_offsets: Vec, /// Strings used by debug instructions. /// /// Each successive [`PushDebugGroup`] or [`InsertDebugMarker`] /// instruction consumes the next `len` bytes from this vector. pub string_data: Vec, /// Data used by `SetPushConstant` instructions. /// /// See the documentation for [`RenderCommand::SetPushConstant`] /// and [`ComputeCommand::SetPushConstant`] for details. pub push_constant_data: Vec, } impl BasePass { fn new(label: &Label) -> Self { Self { label: label.as_ref().map(|cow| cow.to_string()), commands: Vec::new(), dynamic_offsets: Vec::new(), string_data: Vec::new(), push_constant_data: Vec::new(), } } #[cfg(feature = "trace")] fn from_ref(base: BasePassRef) -> Self { Self { label: base.label.map(str::to_string), commands: base.commands.to_vec(), dynamic_offsets: base.dynamic_offsets.to_vec(), string_data: base.string_data.to_vec(), push_constant_data: base.push_constant_data.to_vec(), } } pub fn as_ref(&self) -> BasePassRef { BasePassRef { label: self.label.as_deref(), commands: &self.commands, dynamic_offsets: &self.dynamic_offsets, string_data: &self.string_data, push_constant_data: &self.push_constant_data, } } } #[derive(Clone, Debug, Error)] #[non_exhaustive] pub enum CommandEncoderError { #[error("Command encoder is invalid")] Invalid, #[error("Command encoder must be active")] NotRecording, #[error(transparent)] Device(#[from] DeviceError), } impl Global { pub fn command_encoder_finish( &self, encoder_id: id::CommandEncoderId, _desc: &wgt::CommandBufferDescriptor