/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ mod bind; mod bundle; mod clear; mod compute; mod draw; mod query; mod render; mod transfer; pub use self::bundle::*; pub use self::compute::*; pub use self::draw::*; pub use self::query::*; pub use self::render::*; pub use self::transfer::*; use crate::{ hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token}, id, memory_init_tracker::MemoryInitTrackerAction, resource::{Buffer, Texture}, track::{BufferState, ResourceTracker, TextureState, TrackerSet}, Label, Stored, }; use hal::CommandEncoder as _; use smallvec::SmallVec; use thiserror::Error; const PUSH_CONSTANT_CLEAR_ARRAY: &[u32] = &[0_u32; 64]; #[derive(Debug)] enum CommandEncoderStatus { Recording, Finished, Error, } struct CommandEncoder { raw: A::CommandEncoder, list: Vec, is_open: bool, label: Option, } //TODO: handle errors better impl CommandEncoder { fn close(&mut self) { if self.is_open { self.is_open = false; let cmd_buf = unsafe { self.raw.end_encoding().unwrap() }; self.list.push(cmd_buf); } } fn open(&mut self) -> &mut A::CommandEncoder { if !self.is_open { self.is_open = true; let label = self.label.as_deref(); unsafe { self.raw.begin_encoding(label).unwrap() }; } &mut self.raw } } pub struct BackedCommands { pub(crate) encoder: A::CommandEncoder, pub(crate) list: Vec, pub(crate) trackers: TrackerSet, } pub struct CommandBuffer { encoder: CommandEncoder, status: CommandEncoderStatus, pub(crate) device_id: Stored, pub(crate) trackers: TrackerSet, pub(crate) used_swap_chains: SmallVec<[Stored; 1]>, pub(crate) buffer_memory_init_actions: Vec>, limits: wgt::Limits, downlevel: wgt::DownlevelCapabilities, support_fill_buffer_texture: bool, #[cfg(feature = "trace")] pub(crate) commands: Option>, } impl CommandBuffer { pub(crate) fn new( encoder: A::CommandEncoder, device_id: Stored, limits: wgt::Limits, downlevel: wgt::DownlevelCapabilities, features: wgt::Features, #[cfg(feature = "trace")] enable_tracing: bool, label: &Label, ) -> Self { CommandBuffer { encoder: CommandEncoder { raw: encoder, is_open: false, list: Vec::new(), label: crate::LabelHelpers::borrow_option(label).map(|s| s.to_string()), }, status: CommandEncoderStatus::Recording, device_id, trackers: TrackerSet::new(A::VARIANT), used_swap_chains: Default::default(), buffer_memory_init_actions: Default::default(), limits, downlevel, support_fill_buffer_texture: features.contains(wgt::Features::CLEAR_COMMANDS), #[cfg(feature = "trace")] commands: if enable_tracing { Some(Vec::new()) } else { None }, } } pub(crate) fn insert_barriers( raw: &mut A::CommandEncoder, base: &mut TrackerSet, head_buffers: &ResourceTracker, head_textures: &ResourceTracker, buffer_guard: &Storage, id::BufferId>, texture_guard: &Storage, id::TextureId>, ) { profiling::scope!("insert_barriers"); debug_assert_eq!(A::VARIANT, base.backend()); let buffer_barriers = base.buffers.merge_replace(head_buffers).map(|pending| { let buf = &buffer_guard[pending.id]; pending.into_hal(buf) }); let texture_barriers = base.textures.merge_replace(head_textures).map(|pending| { let tex = &texture_guard[pending.id]; pending.into_hal(tex) }); unsafe { raw.transition_buffers(buffer_barriers); raw.transition_textures(texture_barriers); } } } impl CommandBuffer { fn get_encoder_mut( storage: &mut Storage, id: id::CommandEncoderId, ) -> Result<&mut Self, CommandEncoderError> { match storage.get_mut(id) { Ok(cmd_buf) => match cmd_buf.status { CommandEncoderStatus::Recording => Ok(cmd_buf), CommandEncoderStatus::Finished => Err(CommandEncoderError::NotRecording), CommandEncoderStatus::Error => Err(CommandEncoderError::Invalid), }, Err(_) => Err(CommandEncoderError::Invalid), } } pub fn is_finished(&self) -> bool { match self.status { CommandEncoderStatus::Finished => true, _ => false, } } pub(crate) fn into_baked(self) -> BackedCommands { BackedCommands { encoder: self.encoder.raw, list: self.encoder.list, trackers: self.trackers, } } } impl crate::hub::Resource for CommandBuffer { const TYPE: &'static str = "CommandBuffer"; fn life_guard(&self) -> &crate::LifeGuard { unreachable!() } fn label(&self) -> &str { self.encoder.label.as_ref().map_or("", |s| s.as_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], } #[doc(hidden)] #[derive(Debug)] #[cfg_attr( any(feature = "serial-pass", feature = "trace"), derive(serde::Serialize) )] #[cfg_attr( any(feature = "serial-pass", feature = "replay"), derive(serde::Deserialize) )] pub struct BasePass { pub label: Option, pub commands: Vec, pub dynamic_offsets: Vec, pub string_data: Vec, 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)] pub enum CommandEncoderError { #[error("command encoder is invalid")] Invalid, #[error("command encoder must be active")] NotRecording, } impl Global { pub fn command_encoder_finish( &self, encoder_id: id::CommandEncoderId, _desc: &wgt::CommandBufferDescriptor