[wgpu-core] split command encoders from command buffers

This commit is contained in:
teoxoy 2025-07-18 19:48:20 +02:00 committed by Teodor Tanasoaia
parent 83badd52ab
commit 63f3df86c8
23 changed files with 245 additions and 236 deletions

View File

@ -2,7 +2,6 @@
use std::borrow::Cow;
use std::cell::RefCell;
use std::sync::atomic::{AtomicBool, Ordering};
use deno_core::cppgc::Ptr;
use deno_core::op2;
@ -29,19 +28,11 @@ pub struct GPUCommandEncoder {
pub id: wgpu_core::id::CommandEncoderId,
pub label: String,
pub finished: AtomicBool,
}
impl Drop for GPUCommandEncoder {
fn drop(&mut self) {
// Command encoders and command buffers are both the same wgpu object.
// At the time `finished` is set, ownership of the id (and
// responsibility for dropping it) transfers from the encoder to the
// buffer.
if !self.finished.load(Ordering::SeqCst) {
self.instance.command_encoder_drop(self.id);
}
self.instance.command_encoder_drop(self.id);
}
}
@ -416,34 +407,22 @@ impl GPUCommandEncoder {
fn finish(
&self,
#[webidl] descriptor: crate::command_buffer::GPUCommandBufferDescriptor,
) -> Result<GPUCommandBuffer, JsErrorBox> {
) -> GPUCommandBuffer {
let wgpu_descriptor = wgpu_types::CommandBufferDescriptor {
label: crate::transform_label(descriptor.label.clone()),
};
// TODO(https://github.com/gfx-rs/wgpu/issues/7812): This is not right,
// it should be a validation error, and it would be nice if we can just
// let wgpu generate it for us. The problem is that if the encoder was
// already finished, we transferred ownership of the id to a command
// buffer, so we have to bail out before we mint a duplicate command
// buffer with the same id below.
if self.finished.fetch_or(true, Ordering::SeqCst) {
return Err(JsErrorBox::type_error(
"The command encoder has already finished.",
));
}
let (id, err) = self
.instance
.command_encoder_finish(self.id, &wgpu_descriptor);
.command_encoder_finish(self.id, &wgpu_descriptor, None);
self.error_handler.push_error(err);
Ok(GPUCommandBuffer {
GPUCommandBuffer {
instance: self.instance.clone(),
id,
label: descriptor.label,
})
}
}
fn push_debug_group(&self, #[webidl] group_label: String) {

View File

@ -498,7 +498,6 @@ impl GPUDevice {
error_handler: self.error_handler.clone(),
id,
label,
finished: Default::default(),
}
}

View File

@ -7,6 +7,7 @@ fn main() {
use player::GlobalPlay as _;
use wgc::device::trace;
use wgpu_core::identity::IdentityManager;
use std::{
fs,
@ -52,7 +53,8 @@ fn main() {
.unwrap();
let global = wgc::global::Global::new("player", &wgt::InstanceDescriptor::default());
let mut command_buffer_id_manager = wgc::identity::IdentityManager::new();
let mut command_encoder_id_manager = IdentityManager::new();
let mut command_buffer_id_manager = IdentityManager::new();
#[cfg(feature = "winit")]
let surface = unsafe {
@ -102,7 +104,14 @@ fn main() {
unsafe { global.device_start_graphics_debugger_capture(device) };
while let Some(action) = actions.pop() {
global.process(device, queue, action, &dir, &mut command_buffer_id_manager);
global.process(
device,
queue,
action,
&dir,
&mut command_encoder_id_manager,
&mut command_buffer_id_manager,
);
}
unsafe { global.device_stop_graphics_debugger_capture(device) };
@ -164,6 +173,7 @@ fn main() {
queue,
action,
&dir,
&mut command_encoder_id_manager,
&mut command_buffer_id_manager,
);
}

View File

@ -6,7 +6,7 @@
extern crate wgpu_core as wgc;
extern crate wgpu_types as wgt;
use wgc::device::trace;
use wgc::{device::trace, identity::IdentityManager};
use std::{borrow::Cow, fs, path::Path};
@ -15,6 +15,7 @@ pub trait GlobalPlay {
&self,
encoder: wgc::id::CommandEncoderId,
commands: Vec<trace::Command>,
command_buffer_id_manager: &mut IdentityManager<wgc::id::markers::CommandBuffer>,
) -> wgc::id::CommandBufferId;
fn process(
&self,
@ -22,7 +23,8 @@ pub trait GlobalPlay {
queue: wgc::id::QueueId,
action: trace::Action,
dir: &Path,
comb_manager: &mut wgc::identity::IdentityManager<wgc::id::markers::CommandBuffer>,
command_encoder_id_manager: &mut IdentityManager<wgc::id::markers::CommandEncoder>,
command_buffer_id_manager: &mut IdentityManager<wgc::id::markers::CommandBuffer>,
);
}
@ -31,6 +33,7 @@ impl GlobalPlay for wgc::global::Global {
&self,
encoder: wgc::id::CommandEncoderId,
commands: Vec<trace::Command>,
command_buffer_id_manager: &mut IdentityManager<wgc::id::markers::CommandBuffer>,
) -> wgc::id::CommandBufferId {
for command in commands {
match command {
@ -172,8 +175,11 @@ impl GlobalPlay for wgc::global::Global {
}
}
}
let (cmd_buf, error) =
self.command_encoder_finish(encoder, &wgt::CommandBufferDescriptor { label: None });
let (cmd_buf, error) = self.command_encoder_finish(
encoder,
&wgt::CommandBufferDescriptor { label: None },
Some(command_buffer_id_manager.process()),
);
if let Some(e) = error {
panic!("{e}");
}
@ -186,7 +192,8 @@ impl GlobalPlay for wgc::global::Global {
queue: wgc::id::QueueId,
action: trace::Action,
dir: &Path,
comb_manager: &mut wgc::identity::IdentityManager<wgc::id::markers::CommandBuffer>,
command_encoder_id_manager: &mut IdentityManager<wgc::id::markers::CommandEncoder>,
command_buffer_id_manager: &mut IdentityManager<wgc::id::markers::CommandBuffer>,
) {
use wgc::device::trace::Action;
log::debug!("action {action:?}");
@ -379,12 +386,12 @@ impl GlobalPlay for wgc::global::Global {
let (encoder, error) = self.device_create_command_encoder(
device,
&wgt::CommandEncoderDescriptor { label: None },
Some(comb_manager.process().into_command_encoder_id()),
Some(command_encoder_id_manager.process()),
);
if let Some(e) = error {
panic!("{e}");
}
let cmdbuf = self.encode_commands(encoder, commands);
let cmdbuf = self.encode_commands(encoder, commands, command_buffer_id_manager);
self.queue_submit(queue, &[cmdbuf]).unwrap();
}
Action::CreateBlas { id, desc, sizes } => {

View File

@ -20,6 +20,7 @@ use std::{
path::{Path, PathBuf},
slice,
};
use wgc::identity::IdentityManager;
#[derive(serde::Deserialize)]
struct RawId {
@ -104,7 +105,8 @@ impl Test<'_> {
panic!("{e:?}");
}
let mut command_buffer_id_manager = wgc::identity::IdentityManager::new();
let mut command_encoder_id_manager = IdentityManager::new();
let mut command_buffer_id_manager = IdentityManager::new();
println!("\t\t\tRunning...");
for action in self.actions {
global.process(
@ -112,6 +114,7 @@ impl Test<'_> {
queue_id,
action,
dir,
&mut command_encoder_id_manager,
&mut command_buffer_id_manager,
);
}

View File

@ -179,7 +179,7 @@ async fn draw_test_with_reports(
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report();
assert_eq!(report.command_buffers.num_allocated, 1);
assert_eq!(report.command_encoders.num_allocated, 1);
assert_eq!(report.buffers.num_allocated, 1);
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
@ -206,7 +206,7 @@ async fn draw_test_with_reports(
assert_eq!(report.pipeline_layouts.num_allocated, 1);
assert_eq!(report.render_pipelines.num_allocated, 1);
assert_eq!(report.compute_pipelines.num_allocated, 0);
assert_eq!(report.command_buffers.num_allocated, 1);
assert_eq!(report.command_encoders.num_allocated, 1);
assert_eq!(report.render_bundles.num_allocated, 0);
assert_eq!(report.texture_views.num_allocated, 1);
assert_eq!(report.textures.num_allocated, 1);
@ -223,7 +223,7 @@ async fn draw_test_with_reports(
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report();
assert_eq!(report.command_buffers.num_kept_from_user, 1);
assert_eq!(report.command_encoders.num_kept_from_user, 1);
assert_eq!(report.render_pipelines.num_kept_from_user, 0);
assert_eq!(report.pipeline_layouts.num_kept_from_user, 0);
assert_eq!(report.bind_group_layouts.num_kept_from_user, 0);
@ -231,7 +231,7 @@ async fn draw_test_with_reports(
assert_eq!(report.buffers.num_kept_from_user, 0);
assert_eq!(report.texture_views.num_kept_from_user, 0);
assert_eq!(report.textures.num_kept_from_user, 0);
assert_eq!(report.command_buffers.num_allocated, 1);
assert_eq!(report.command_encoders.num_allocated, 1);
assert_eq!(report.render_pipelines.num_allocated, 0);
assert_eq!(report.pipeline_layouts.num_allocated, 0);
assert_eq!(report.bind_group_layouts.num_allocated, 0);
@ -240,12 +240,18 @@ async fn draw_test_with_reports(
assert_eq!(report.texture_views.num_allocated, 0);
assert_eq!(report.textures.num_allocated, 0);
let submit_index = ctx.queue.submit(Some(encoder.finish()));
let command_buffer = encoder.finish();
// TODO: fix in https://github.com/gfx-rs/wgpu/pull/5141
// let global_report = ctx.instance.generate_report().unwrap();
// let report = global_report.hub_report();
// assert_eq!(report.command_buffers.num_allocated, 0);
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report();
assert_eq!(report.command_encoders.num_allocated, 0);
assert_eq!(report.command_buffers.num_allocated, 1);
let submit_index = ctx.queue.submit(Some(command_buffer));
let global_report = ctx.instance.generate_report().unwrap();
let report = global_report.hub_report();
assert_eq!(report.command_buffers.num_allocated, 0);
ctx.async_poll(wgpu::PollType::wait_for(submit_index))
.await

View File

@ -346,7 +346,7 @@ impl Global {
let hub = &self.hub;
let cmd_buf = hub.command_buffers.get(id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(id);
let mut cmd_buf_data = cmd_buf.data.lock();
cmd_buf_data.record_as_hal_mut(|opt_cmd_buf| -> R {
hal_command_encoder_callback(opt_cmd_buf.and_then(|cmd_buf| {

View File

@ -6,7 +6,7 @@ use crate::device::trace::Command as TraceCommand;
use crate::{
api_log,
command::EncoderStateError,
device::DeviceError,
device::{DeviceError, MissingFeatures},
get_lowest_common_denom,
global::Global,
id::{BufferId, CommandEncoderId, TextureId},
@ -30,10 +30,10 @@ use wgt::{
#[derive(Clone, Debug, Error)]
#[non_exhaustive]
pub enum ClearError {
#[error("To use clear_texture the CLEAR_TEXTURE feature needs to be enabled")]
MissingClearTextureFeature,
#[error(transparent)]
DestroyedResource(#[from] DestroyedResourceError),
#[error(transparent)]
MissingFeatures(#[from] MissingFeatures),
#[error("{0} can not be cleared")]
NoValidTextureClearMode(ResourceErrorIdent),
#[error("Buffer clear size {0:?} is not a multiple of `COPY_BUFFER_ALIGNMENT`")]
@ -84,12 +84,12 @@ impl WebGpuError for ClearError {
fn webgpu_error_type(&self) -> ErrorType {
let e: &dyn WebGpuError = match self {
Self::DestroyedResource(e) => e,
Self::MissingFeatures(e) => e,
Self::MissingBufferUsage(e) => e,
Self::Device(e) => e,
Self::EncoderState(e) => e,
Self::InvalidResource(e) => e,
Self::NoValidTextureClearMode(..)
| Self::MissingClearTextureFeature
| Self::UnalignedFillSize(..)
| Self::UnalignedBufferOffset(..)
| Self::OffsetPlusSizeExceeds64BitBounds { .. }
@ -115,9 +115,7 @@ impl Global {
let hub = &self.hub;
let cmd_buf = hub
.command_buffers
.get(command_encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), ClearError> {
#[cfg(feature = "trace")]
@ -202,9 +200,7 @@ impl Global {
let hub = &self.hub;
let cmd_buf = hub
.command_buffers
.get(command_encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), ClearError> {
#[cfg(feature = "trace")]
@ -217,9 +213,9 @@ impl Global {
cmd_buf.device.check_is_valid()?;
if !cmd_buf.support_clear_texture {
return Err(ClearError::MissingClearTextureFeature);
}
cmd_buf
.device
.require_features(wgt::Features::CLEAR_TEXTURE)?;
let dst_texture = hub.textures.get(dst).get()?;

View File

@ -7,7 +7,9 @@ use wgt::{
use alloc::{borrow::Cow, boxed::Box, sync::Arc, vec::Vec};
use core::{fmt, str};
use crate::command::{pass, EncoderStateError, PassStateError, TimestampWritesError};
use crate::command::{
pass, CommandEncoder, EncoderStateError, PassStateError, TimestampWritesError,
};
use crate::resource::DestroyedResourceError;
use crate::{binding_model::BindError, resource::RawResourceAccess};
use crate::{
@ -18,8 +20,8 @@ use crate::{
end_pipeline_statistics_query,
memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState},
pass_base, pass_try, validate_and_begin_pipeline_statistics_query, ArcPassTimestampWrites,
BasePass, BindGroupStateChange, CommandBuffer, CommandEncoderError, MapPassErr,
PassErrorScope, PassTimestampWrites, QueryUseError, StateChange,
BasePass, BindGroupStateChange, CommandEncoderError, MapPassErr, PassErrorScope,
PassTimestampWrites, QueryUseError, StateChange,
},
device::{DeviceError, MissingDownlevelFlags, MissingFeatures},
global::Global,
@ -46,12 +48,12 @@ pub struct ComputePass {
/// All pass data & records is stored here.
base: ComputeBasePass,
/// Parent command buffer that this pass records commands into.
/// Parent command encoder that this pass records commands into.
///
/// If this is `Some`, then the pass is in WebGPU's "open" state. If it is
/// `None`, then the pass is in the "ended" state.
/// See <https://www.w3.org/TR/webgpu/#encoder-state>
parent: Option<Arc<CommandBuffer>>,
parent: Option<Arc<CommandEncoder>>,
timestamp_writes: Option<ArcPassTimestampWrites>,
@ -61,8 +63,8 @@ pub struct ComputePass {
}
impl ComputePass {
/// If the parent command buffer is invalid, the returned pass will be invalid.
fn new(parent: Arc<CommandBuffer>, desc: ArcComputePassDescriptor) -> Self {
/// If the parent command encoder is invalid, the returned pass will be invalid.
fn new(parent: Arc<CommandEncoder>, desc: ArcComputePassDescriptor) -> Self {
let ArcComputePassDescriptor {
label,
timestamp_writes,
@ -78,7 +80,7 @@ impl ComputePass {
}
}
fn new_invalid(parent: Arc<CommandBuffer>, label: &Label, err: ComputePassError) -> Self {
fn new_invalid(parent: Arc<CommandEncoder>, label: &Label, err: ComputePassError) -> Self {
Self {
base: BasePass::new_invalid(label, err),
parent: Some(parent),
@ -308,7 +310,7 @@ impl<'scope, 'snatch_guard, 'cmd_buf, 'raw_encoder>
);
}
CommandBuffer::drain_barriers(
CommandEncoder::drain_barriers(
self.general.raw_encoder,
&mut self.intermediate_trackers,
self.general.snatch_guard,
@ -342,7 +344,7 @@ impl Global {
let label = desc.label.as_deref().map(Cow::Borrowed);
let cmd_buf = hub.command_buffers.get(encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
match cmd_buf_data.lock_encoder() {
@ -433,10 +435,7 @@ impl Global {
) {
#[cfg(feature = "trace")]
{
let cmd_buf = self
.hub
.command_buffers
.get(encoder_id.into_command_buffer_id());
let cmd_buf = self.hub.command_encoders.get(encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
let cmd_buf_data = cmd_buf_data.get_inner();
@ -750,10 +749,10 @@ impl Global {
..
} = state;
// Stop the current command buffer.
// Stop the current command encoder.
encoder.close().map_pass_err(pass_scope)?;
// Create a new command buffer, which we will insert _before_ the body of the compute pass.
// Create a new command encoder, which we will insert _before_ the body of the compute pass.
//
// Use that buffer to insert barriers and clear discarded images.
let transit = encoder
@ -766,13 +765,13 @@ impl Global {
device,
&snatch_guard,
);
CommandBuffer::insert_barriers_from_tracker(
CommandEncoder::insert_barriers_from_tracker(
transit,
tracker,
&intermediate_trackers,
&snatch_guard,
);
// Close the command buffer, and swap it with the previous.
// Close the command encoder, and swap it with the previous.
encoder.close_and_swap().map_pass_err(pass_scope)?;
Ok(())
@ -782,7 +781,7 @@ impl Global {
fn set_pipeline(
state: &mut State,
cmd_buf: &CommandBuffer,
cmd_buf: &CommandEncoder,
pipeline: Arc<ComputePipeline>,
) -> Result<(), ComputePassErrorInner> {
pipeline.same_device_as(cmd_buf)?;
@ -859,7 +858,7 @@ fn dispatch(state: &mut State, groups: [u32; 3]) -> Result<(), ComputePassErrorI
fn dispatch_indirect(
state: &mut State,
cmd_buf: &CommandBuffer,
cmd_buf: &CommandEncoder,
buffer: Arc<Buffer>,
offset: u64,
) -> Result<(), ComputePassErrorInner> {

View File

@ -145,6 +145,9 @@ impl CommandEncoderStatus {
// Encoder is ended. Invalidate the encoder, do not record anything,
// and return an immediate validation error.
Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
Self::Error(CommandEncoderError::State(EncoderStateError::Ended)) => {
Err(EncoderStateError::Ended)
}
// Encoder is already invalid. Do not record anything, but do not
// return an immediate validation error.
Self::Error(_) => Ok(()),
@ -173,6 +176,7 @@ impl CommandEncoderStatus {
self.invalidate(EncoderStateError::Ended);
f(None)
}
Self::Error(CommandEncoderError::State(EncoderStateError::Ended)) => f(None),
Self::Error(_) => f(None),
Self::Transitioning => unreachable!(),
}
@ -210,6 +214,10 @@ impl CommandEncoderStatus {
Err(EncoderStateError::Ended)
}
Self::Locked(_) => Err(self.invalidate(EncoderStateError::Locked)),
st @ Self::Error(CommandEncoderError::State(EncoderStateError::Ended)) => {
*self = st;
Err(EncoderStateError::Ended)
}
st @ Self::Error(_) => {
*self = st;
Err(EncoderStateError::Invalid)
@ -254,6 +262,10 @@ impl CommandEncoderStatus {
*self = Self::Error(EncoderStateError::Unlocked.into());
Err(EncoderStateError::Unlocked)
}
st @ Self::Error(CommandEncoderError::State(EncoderStateError::Ended)) => {
*self = st;
Err(EncoderStateError::Ended)
}
st @ Self::Error(_) => {
// Encoder is invalid. Do not record anything, but do not
// return an immediate validation error.
@ -374,6 +386,26 @@ impl<'a> ops::DerefMut for RecordingGuard<'a> {
}
}
pub(crate) struct CommandEncoder {
pub(crate) device: Arc<Device>,
pub(crate) label: String,
/// The mutable state of this command encoder.
pub(crate) data: Mutex<CommandEncoderStatus>,
}
crate::impl_resource_type!(CommandEncoder);
crate::impl_labeled!(CommandEncoder);
crate::impl_parent_device!(CommandEncoder);
crate::impl_storage_item!(CommandEncoder);
impl Drop for CommandEncoder {
fn drop(&mut self) {
resource_log!("Drop {}", self.error_ident());
}
}
/// A raw [`CommandEncoder`][rce], and the raw [`CommandBuffer`][rcb]s built from it.
///
/// Each wgpu-core [`CommandBuffer`] owns an instance of this type, which is
@ -385,16 +417,11 @@ impl<'a> ops::DerefMut for RecordingGuard<'a> {
/// commands into the middle of a recorded stream. However, hal queue submission
/// accepts a series of command buffers at once, so we can simply break the
/// stream up into multiple buffers, and then reorder the buffers. See
/// [`CommandEncoder::close_and_swap`] for a specific example of this.
///
/// Note that a [`CommandEncoderId`] actually refers to a [`CommandBuffer`].
/// Methods that take a command encoder id actually look up the command buffer,
/// and then use its encoder.
/// [`InnerCommandEncoder::close_and_swap`] for a specific example of this.
///
/// [rce]: hal::Api::CommandEncoder
/// [rcb]: hal::Api::CommandBuffer
/// [`CommandEncoderId`]: crate::id::CommandEncoderId
pub(crate) struct CommandEncoder {
pub(crate) struct InnerCommandEncoder {
/// The underlying `wgpu_hal` [`CommandEncoder`].
///
/// Successfully executed command buffers' encoders are saved in a
@ -427,10 +454,10 @@ pub(crate) struct CommandEncoder {
/// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
pub(crate) is_open: bool,
pub(crate) hal_label: Option<String>,
pub(crate) label: String,
}
impl CommandEncoder {
impl InnerCommandEncoder {
/// Finish the current command buffer and insert it just before
/// the last element in [`self.list`][l].
///
@ -453,7 +480,7 @@ impl CommandEncoder {
///
/// - If the encoder is not open.
///
/// [l]: CommandEncoder::list
/// [l]: InnerCommandEncoder::list
/// [`transition_buffers`]: hal::CommandEncoder::transition_buffers
/// [`transition_textures`]: hal::CommandEncoder::transition_textures
fn close_and_swap(&mut self) -> Result<(), DeviceError> {
@ -476,7 +503,7 @@ impl CommandEncoder {
///
/// - If the encoder is not open.
///
/// [l]: CommandEncoder::list
/// [l]: InnerCommandEncoder::list
pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> {
assert!(self.is_open);
self.is_open = false;
@ -497,7 +524,7 @@ impl CommandEncoder {
///
/// - If the encoder is not open.
///
/// [l]: CommandEncoder::list
/// [l]: InnerCommandEncoder::list
pub(crate) fn close(&mut self) -> Result<(), DeviceError> {
assert!(self.is_open);
self.is_open = false;
@ -518,7 +545,7 @@ impl CommandEncoder {
///
/// On return, the underlying hal encoder is closed.
///
/// [l]: CommandEncoder::list
/// [l]: InnerCommandEncoder::list
fn close_if_open(&mut self) -> Result<(), DeviceError> {
if self.is_open {
self.is_open = false;
@ -536,7 +563,7 @@ impl CommandEncoder {
pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
if !self.is_open {
self.is_open = true;
let hal_label = self.hal_label.as_deref();
let hal_label = hal_label(Some(&self.label), self.device.instance_flags);
unsafe { self.raw.begin_encoding(hal_label) }
.map_err(|e| self.device.handle_hal_error(e))?;
}
@ -567,7 +594,7 @@ impl CommandEncoder {
}
}
impl Drop for CommandEncoder {
impl Drop for InnerCommandEncoder {
fn drop(&mut self) {
if self.is_open {
unsafe { self.raw.discard_encoding() };
@ -584,7 +611,7 @@ impl Drop for CommandEncoder {
/// Look at the documentation for [`CommandBufferMutable`] for an explanation of
/// the fields in this struct. This is the "built" counterpart to that type.
pub(crate) struct BakedCommands {
pub(crate) encoder: CommandEncoder,
pub(crate) encoder: InnerCommandEncoder,
pub(crate) trackers: Tracker,
pub(crate) temp_resources: Vec<TempResource>,
pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
@ -598,7 +625,7 @@ pub struct CommandBufferMutable {
/// they belong to.
///
/// [`wgpu_hal::Api::CommandBuffer`]: hal::Api::CommandBuffer
pub(crate) encoder: CommandEncoder,
pub(crate) encoder: InnerCommandEncoder,
/// All the resources that the commands recorded so far have referred to.
pub(crate) trackers: Tracker,
@ -647,25 +674,11 @@ impl CommandBufferMutable {
/// A buffer of commands to be submitted to the GPU for execution.
///
/// Whereas the WebGPU API uses two separate types for command buffers and
/// encoders, this type is a fusion of the two:
///
/// - During command recording, this holds a [`CommandEncoder`] accepting this
/// buffer's commands. In this state, the [`CommandBuffer`] type behaves like
/// a WebGPU `GPUCommandEncoder`.
///
/// - Once command recording is finished by calling
/// [`Global::command_encoder_finish`], no further recording is allowed. The
/// internal [`CommandEncoder`] is retained solely as a storage pool for the
/// raw command buffers. In this state, the value behaves like a WebGPU
/// `GPUCommandBuffer`.
///
/// - Once a command buffer is submitted to the queue, it is removed from the id
/// registry, and its contents are taken to construct a [`BakedCommands`],
/// whose contents eventually become the property of the submission queue.
/// Once a command buffer is submitted to the queue, its contents are taken
/// to construct a [`BakedCommands`], whose contents eventually become the
/// property of the submission queue.
pub struct CommandBuffer {
pub(crate) device: Arc<Device>,
support_clear_texture: bool,
/// The `label` from the descriptor used to create the resource.
label: String,
@ -679,25 +692,24 @@ impl Drop for CommandBuffer {
}
}
impl CommandBuffer {
impl CommandEncoder {
pub(crate) fn new(
encoder: Box<dyn hal::DynCommandEncoder>,
device: &Arc<Device>,
label: &Label,
) -> Self {
CommandBuffer {
CommandEncoder {
device: device.clone(),
support_clear_texture: device.features.contains(wgt::Features::CLEAR_TEXTURE),
label: label.to_string(),
data: Mutex::new(
rank::COMMAND_BUFFER_DATA,
CommandEncoderStatus::Recording(CommandBufferMutable {
encoder: CommandEncoder {
encoder: InnerCommandEncoder {
raw: ManuallyDrop::new(encoder),
list: Vec::new(),
device: device.clone(),
is_open: false,
hal_label: label.to_hal(device.instance_flags).map(str::to_owned),
label: label.to_string(),
},
trackers: Tracker::new(),
buffer_memory_init_actions: Default::default(),
@ -723,9 +735,8 @@ impl CommandBuffer {
label: &Label,
err: CommandEncoderError,
) -> Self {
CommandBuffer {
CommandEncoder {
device: device.clone(),
support_clear_texture: device.features.contains(wgt::Features::CLEAR_TEXTURE),
label: label.to_string(),
data: Mutex::new(rank::COMMAND_BUFFER_DATA, CommandEncoderStatus::Error(err)),
}
@ -1123,22 +1134,40 @@ impl Global {
pub fn command_encoder_finish(
&self,
encoder_id: id::CommandEncoderId,
_desc: &wgt::CommandBufferDescriptor<Label>,
desc: &wgt::CommandBufferDescriptor<Label>,
id_in: Option<id::CommandBufferId>,
) -> (id::CommandBufferId, Option<CommandEncoderError>) {
profiling::scope!("CommandEncoder::finish");
let hub = &self.hub;
let cmd_buf = hub.command_buffers.get(encoder_id.into_command_buffer_id());
let cmd_enc = hub.command_encoders.get(encoder_id);
let mut data_guard = cmd_enc.data.lock();
// Errors related to destroyed resources are not reported until the
// command buffer is submitted.
let error = match cmd_buf.data.lock().finish() {
let error = match data_guard.finish() {
Err(e) if !e.is_destroyed_error() => Some(e),
_ => None,
};
(encoder_id.into_command_buffer_id(), error)
let data = mem::replace(
&mut *data_guard,
CommandEncoderStatus::Error(EncoderStateError::Ended.into()),
);
drop(data_guard);
let cmd_buf = CommandBuffer {
device: cmd_enc.device.clone(),
label: desc.label.to_string(),
data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),
};
let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(Arc::new(cmd_buf));
(cmd_buf_id, error)
}
pub fn command_encoder_push_debug_group(
@ -1151,7 +1180,7 @@ impl Global {
let hub = &self.hub;
let cmd_buf = hub.command_buffers.get(encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
#[cfg(feature = "trace")]
@ -1186,7 +1215,7 @@ impl Global {
let hub = &self.hub;
let cmd_buf = hub.command_buffers.get(encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
#[cfg(feature = "trace")]
@ -1220,7 +1249,7 @@ impl Global {
let hub = &self.hub;
let cmd_buf = hub.command_buffers.get(encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
#[cfg(feature = "trace")]

View File

@ -3,7 +3,7 @@
use crate::binding_model::{BindError, BindGroup, PushConstantUploadError};
use crate::command::bind::Binder;
use crate::command::memory_init::{CommandBufferTextureMemoryActions, SurfacesInDiscardState};
use crate::command::{CommandBuffer, QueryResetMap, QueryUseError};
use crate::command::{CommandEncoder, QueryResetMap, QueryUseError};
use crate::device::{Device, DeviceError, MissingFeatures};
use crate::init_tracker::BufferInitTrackerAction;
use crate::pipeline::LateSizedBufferGroup;
@ -82,7 +82,7 @@ pub(crate) struct BaseState<'scope, 'snatch_guard, 'cmd_buf, 'raw_encoder> {
pub(crate) fn set_bind_group<E>(
state: &mut BaseState,
cmd_buf: &CommandBuffer,
cmd_buf: &CommandEncoder,
dynamic_offsets: &[DynamicOffset],
index: u32,
num_dynamic_offsets: usize,
@ -287,7 +287,7 @@ where
pub(crate) fn write_timestamp<E>(
state: &mut BaseState,
cmd_buf: &CommandBuffer,
cmd_buf: &CommandEncoder,
pending_query_resets: Option<&mut QueryResetMap>,
query_set: Arc<QuerySet>,
query_index: u32,

View File

@ -4,7 +4,7 @@ use core::{iter, mem};
#[cfg(feature = "trace")]
use crate::device::trace::Command as TraceCommand;
use crate::{
command::{CommandBuffer, EncoderStateError},
command::{CommandEncoder, EncoderStateError},
device::{DeviceError, MissingFeatures},
global::Global,
id,
@ -307,7 +307,7 @@ pub(super) fn validate_and_begin_pipeline_statistics_query(
query_set: Arc<QuerySet>,
raw_encoder: &mut dyn hal::DynCommandEncoder,
tracker: &mut StatelessTracker<QuerySet>,
cmd_buf: &CommandBuffer,
cmd_buf: &CommandEncoder,
query_index: u32,
reset_state: Option<&mut QueryResetMap>,
active_query: &mut Option<(Arc<QuerySet>, u32)>,
@ -363,9 +363,7 @@ impl Global {
) -> Result<(), EncoderStateError> {
let hub = &self.hub;
let cmd_buf = hub
.command_buffers
.get(command_encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), QueryError> {
#[cfg(feature = "trace")]
@ -406,9 +404,7 @@ impl Global {
) -> Result<(), EncoderStateError> {
let hub = &self.hub;
let cmd_buf = hub
.command_buffers
.get(command_encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), QueryError> {
#[cfg(feature = "trace")]

View File

@ -67,9 +67,7 @@ impl Global {
let hub = &self.hub;
let cmd_buf = hub
.command_buffers
.get(command_encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
cmd_buf_data.record_with(
@ -109,9 +107,7 @@ impl Global {
let hub = &self.hub;
let cmd_buf = hub
.command_buffers
.get(command_encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(command_encoder_id);
let mut build_command = AsBuild::default();

View File

@ -11,8 +11,8 @@ use wgt::{
use crate::command::{
pass, pass_base, pass_try, validate_and_begin_occlusion_query,
validate_and_begin_pipeline_statistics_query, EncoderStateError, PassStateError,
TimestampWritesError,
validate_and_begin_pipeline_statistics_query, EncoderStateError, InnerCommandEncoder,
PassStateError, TimestampWritesError,
};
use crate::pipeline::{RenderPipeline, VertexStep};
use crate::resource::RawResourceAccess;
@ -24,8 +24,8 @@ use crate::{
bind::Binder,
end_occlusion_query, end_pipeline_statistics_query,
memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState},
ArcPassTimestampWrites, BasePass, BindGroupStateChange, CommandBuffer, CommandEncoderError,
DrawError, ExecutionError, MapPassErr, PassErrorScope, PassTimestampWrites, QueryUseError,
ArcPassTimestampWrites, BasePass, BindGroupStateChange, CommandEncoderError, DrawError,
ExecutionError, MapPassErr, PassErrorScope, PassTimestampWrites, QueryUseError,
RenderCommandError, StateChange,
},
device::{
@ -258,12 +258,12 @@ pub struct RenderPass {
/// All pass data & records is stored here.
base: BasePass<ArcRenderCommand, RenderPassError>,
/// Parent command buffer that this pass records commands into.
/// Parent command encoder that this pass records commands into.
///
/// If this is `Some`, then the pass is in WebGPU's "open" state. If it is
/// `None`, then the pass is in the "ended" state.
/// See <https://www.w3.org/TR/webgpu/#encoder-state>
parent: Option<Arc<CommandBuffer>>,
parent: Option<Arc<CommandEncoder>>,
color_attachments:
ArrayVec<Option<ArcRenderPassColorAttachment>, { hal::MAX_COLOR_ATTACHMENTS }>,
@ -277,8 +277,8 @@ pub struct RenderPass {
}
impl RenderPass {
/// If the parent command buffer is invalid, the returned pass will be invalid.
fn new(parent: Arc<CommandBuffer>, desc: ArcRenderPassDescriptor) -> Self {
/// If the parent command encoder is invalid, the returned pass will be invalid.
fn new(parent: Arc<CommandEncoder>, desc: ArcRenderPassDescriptor) -> Self {
let ArcRenderPassDescriptor {
label,
timestamp_writes,
@ -300,7 +300,7 @@ impl RenderPass {
}
}
fn new_invalid(parent: Arc<CommandBuffer>, label: &Label, err: RenderPassError) -> Self {
fn new_invalid(parent: Arc<CommandEncoder>, label: &Label, err: RenderPassError) -> Self {
Self {
base: BasePass::new_invalid(label, err),
parent: Some(parent),
@ -955,7 +955,7 @@ impl RenderPassInfo {
mut depth_stencil_attachment: Option<ArcRenderPassDepthStencilAttachment>,
mut timestamp_writes: Option<ArcPassTimestampWrites>,
mut occlusion_query_set: Option<Arc<QuerySet>>,
encoder: &mut CommandEncoder,
encoder: &mut InnerCommandEncoder,
trackers: &mut Tracker,
texture_memory_actions: &mut CommandBufferTextureMemoryActions,
pending_query_resets: &mut QueryResetMap,
@ -1665,7 +1665,7 @@ impl Global {
let scope = PassErrorScope::Pass;
let hub = &self.hub;
let cmd_buf = hub.command_buffers.get(encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
match cmd_buf_data.lock_encoder() {
@ -1739,10 +1739,7 @@ impl Global {
) {
#[cfg(feature = "trace")]
{
let cmd_buf = self
.hub
.command_buffers
.get(encoder_id.into_command_buffer_id());
let cmd_buf = self.hub.command_encoders.get(encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
let cmd_buf_data = cmd_buf_data.get_inner();
@ -2233,7 +2230,7 @@ impl Global {
cmd_buf_data.pending_query_resets.reset_queries(transit);
CommandBuffer::insert_barriers_from_scope(transit, tracker, &scope, snatch_guard);
CommandEncoder::insert_barriers_from_scope(transit, tracker, &scope, snatch_guard);
if let Some(ref indirect_validation) = device.indirect_validation {
indirect_validation
@ -2259,7 +2256,7 @@ impl Global {
fn set_pipeline(
state: &mut State,
cmd_buf: &Arc<CommandBuffer>,
cmd_buf: &Arc<CommandEncoder>,
pipeline: Arc<RenderPipeline>,
) -> Result<(), RenderPassErrorInner> {
api_log!("RenderPass::set_pipeline {}", pipeline.error_ident());
@ -2326,7 +2323,7 @@ fn set_pipeline(
// This function is duplicative of `bundle::set_index_buffer`.
fn set_index_buffer(
state: &mut State,
cmd_buf: &Arc<CommandBuffer>,
cmd_buf: &Arc<CommandEncoder>,
buffer: Arc<crate::resource::Buffer>,
index_format: IndexFormat,
offset: u64,
@ -2374,7 +2371,7 @@ fn set_index_buffer(
// This function is duplicative of `render::set_vertex_buffer`.
fn set_vertex_buffer(
state: &mut State,
cmd_buf: &Arc<CommandBuffer>,
cmd_buf: &Arc<CommandEncoder>,
slot: u32,
buffer: Arc<crate::resource::Buffer>,
offset: u64,
@ -2607,7 +2604,7 @@ fn multi_draw_indirect(
state: &mut State,
indirect_draw_validation_resources: &mut crate::indirect_validation::DrawResources,
indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
cmd_buf: &Arc<CommandBuffer>,
cmd_buf: &Arc<CommandEncoder>,
indirect_buffer: Arc<crate::resource::Buffer>,
offset: u64,
count: u32,
@ -2788,7 +2785,7 @@ fn multi_draw_indirect(
fn multi_draw_indirect_count(
state: &mut State,
cmd_buf: &Arc<CommandBuffer>,
cmd_buf: &Arc<CommandEncoder>,
indirect_buffer: Arc<crate::resource::Buffer>,
offset: u64,
count_buffer: Arc<crate::resource::Buffer>,
@ -2901,7 +2898,7 @@ fn execute_bundle(
state: &mut State,
indirect_draw_validation_resources: &mut crate::indirect_validation::DrawResources,
indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
cmd_buf: &Arc<CommandBuffer>,
cmd_buf: &Arc<CommandEncoder>,
bundle: Arc<super::RenderBundle>,
) -> Result<(), RenderPassErrorInner> {
api_log!("RenderPass::execute_bundle {}", bundle.error_ident());

View File

@ -640,9 +640,7 @@ impl Global {
let hub = &self.hub;
let cmd_buf = hub
.command_buffers
.get(command_encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
let device = &cmd_buf.device;
@ -809,9 +807,7 @@ impl Global {
let hub = &self.hub;
let cmd_buf = hub
.command_buffers
.get(command_encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
let device = &cmd_buf.device;
@ -967,9 +963,7 @@ impl Global {
let hub = &self.hub;
let cmd_buf = hub
.command_buffers
.get(command_encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
let device = &cmd_buf.device;
@ -1142,9 +1136,7 @@ impl Global {
let hub = &self.hub;
let cmd_buf = hub
.command_buffers
.get(command_encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
let device = &cmd_buf.device;

View File

@ -2,7 +2,7 @@ use thiserror::Error;
use wgt::error::{ErrorType, WebGpuError};
use crate::{
command::{CommandBuffer, CommandEncoderError, EncoderStateError},
command::{CommandEncoder, CommandEncoderError, EncoderStateError},
device::DeviceError,
global::Global,
id::{BufferId, CommandEncoderId, TextureId},
@ -22,9 +22,7 @@ impl Global {
let hub = &self.hub;
// Lock command encoder for recording
let cmd_buf = hub
.command_buffers
.get(command_encoder_id.into_command_buffer_id());
let cmd_buf = hub.command_encoders.get(command_encoder_id);
let mut cmd_buf_data = cmd_buf.data.lock();
cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
// Get and lock device
@ -63,7 +61,7 @@ impl Global {
// Record any needed barriers based on tracker data
let cmd_buf_raw = cmd_buf_data.encoder.open()?;
CommandBuffer::insert_barriers_from_scope(
CommandEncoder::insert_barriers_from_scope(
cmd_buf_raw,
&mut cmd_buf_data.trackers,
&usage_scope,

View File

@ -9,7 +9,7 @@ use crate::{
self, BindGroupEntry, BindingResource, BufferBinding, ResolvedBindGroupDescriptor,
ResolvedBindGroupEntry, ResolvedBindingResource, ResolvedBufferBinding,
},
command::{self, CommandBuffer},
command::{self, CommandEncoder},
conv,
device::{bgl, life::WaitIdleError, DeviceError, DeviceLostClosure},
global::Global,
@ -1051,46 +1051,39 @@ impl Global {
profiling::scope!("Device::create_command_encoder");
let hub = &self.hub;
let fid = hub
.command_buffers
.prepare(id_in.map(|id| id.into_command_buffer_id()));
let fid = hub.command_encoders.prepare(id_in);
let device = self.hub.devices.get(device_id);
let error = 'error: {
let command_buffer = match device.create_command_encoder(&desc.label) {
Ok(command_buffer) => command_buffer,
let cmd_enc = match device.create_command_encoder(&desc.label) {
Ok(cmd_enc) => cmd_enc,
Err(e) => break 'error e,
};
let id = fid.assign(command_buffer);
let id = fid.assign(cmd_enc);
api_log!("Device::create_command_encoder -> {id:?}");
return (id.into_command_encoder_id(), None);
return (id, None);
};
let id = fid.assign(Arc::new(CommandBuffer::new_invalid(
let id = fid.assign(Arc::new(CommandEncoder::new_invalid(
&device,
&desc.label,
error.clone().into(),
)));
(id.into_command_encoder_id(), Some(error))
(id, Some(error))
}
pub fn command_encoder_drop(&self, command_encoder_id: id::CommandEncoderId) {
profiling::scope!("CommandEncoder::drop");
api_log!("CommandEncoder::drop {command_encoder_id:?}");
let hub = &self.hub;
let _cmd_buf = hub
.command_buffers
.remove(command_encoder_id.into_command_buffer_id());
let _cmd_enc = self.hub.command_encoders.remove(command_encoder_id);
}
pub fn command_buffer_drop(&self, command_buffer_id: id::CommandBufferId) {
profiling::scope!("CommandBuffer::drop");
api_log!("CommandBuffer::drop {command_buffer_id:?}");
self.command_encoder_drop(command_buffer_id.into_command_encoder_id())
let _cmd_buf = self.hub.command_buffers.remove(command_buffer_id);
}
pub fn device_create_render_bundle_encoder(

View File

@ -20,7 +20,7 @@ use crate::{
api_log,
command::{
extract_texture_selector, validate_linear_texture_data, validate_texture_copy_range,
ClearError, CommandAllocator, CommandBuffer, CommandEncoderError, CopySide,
ClearError, CommandAllocator, CommandBuffer, CommandEncoder, CommandEncoderError, CopySide,
TexelCopyTextureInfo, TransferError,
},
conv,
@ -278,7 +278,7 @@ pub enum TempResource {
/// [`CommandBuffer`]: hal::Api::CommandBuffer
/// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
pub(crate) struct EncoderInFlight {
inner: crate::command::CommandEncoder,
inner: crate::command::InnerCommandEncoder,
pub(crate) trackers: Tracker,
pub(crate) temp_resources: Vec<TempResource>,
/// We only need to keep these resources alive.
@ -394,12 +394,12 @@ impl PendingWrites {
.map_err(|e| device.handle_hal_error(e))?;
let encoder = EncoderInFlight {
inner: crate::command::CommandEncoder {
inner: crate::command::InnerCommandEncoder {
raw: ManuallyDrop::new(mem::replace(&mut self.command_encoder, new_encoder)),
list: vec![cmd_buf],
device: device.clone(),
is_open: false,
hal_label: None,
label: "(wgpu internal) PendingWrites command encoder".into(),
},
trackers: Tracker::new(),
temp_resources: mem::take(&mut self.temp_resources),
@ -1237,7 +1237,7 @@ impl Queue {
//Note: stateless trackers are not merged:
// device already knows these resources exist.
CommandBuffer::insert_barriers_from_device_tracker(
CommandEncoder::insert_barriers_from_device_tracker(
baked.encoder.raw.as_mut(),
&mut trackers,
&baked.trackers,

View File

@ -1859,7 +1859,7 @@ impl Device {
pub(crate) fn create_command_encoder(
self: &Arc<Self>,
label: &crate::Label,
) -> Result<Arc<command::CommandBuffer>, DeviceError> {
) -> Result<Arc<command::CommandEncoder>, DeviceError> {
self.check_is_valid()?;
let queue = self.get_queue().unwrap();
@ -1869,11 +1869,11 @@ impl Device {
.acquire_encoder(self.raw(), queue.raw())
.map_err(|e| self.handle_hal_error(e))?;
let command_buffer = command::CommandBuffer::new(encoder, self, label);
let cmd_enc = command::CommandEncoder::new(encoder, self, label);
let command_buffer = Arc::new(command_buffer);
let cmd_enc = Arc::new(cmd_enc);
Ok(command_buffer)
Ok(cmd_enc)
}
/// Generate information about late-validated buffer bindings for pipelines.

View File

@ -105,7 +105,7 @@ use core::fmt::Debug;
use crate::{
binding_model::{BindGroup, BindGroupLayout, PipelineLayout},
command::{CommandBuffer, RenderBundle},
command::{CommandBuffer, CommandEncoder, RenderBundle},
device::{queue::Queue, Device},
instance::Adapter,
pipeline::{ComputePipeline, PipelineCache, RenderPipeline, ShaderModule},
@ -124,6 +124,7 @@ pub struct HubReport {
pub shader_modules: RegistryReport,
pub bind_group_layouts: RegistryReport,
pub bind_groups: RegistryReport,
pub command_encoders: RegistryReport,
pub command_buffers: RegistryReport,
pub render_bundles: RegistryReport,
pub render_pipelines: RegistryReport,
@ -171,6 +172,7 @@ pub struct Hub {
pub(crate) shader_modules: Registry<Fallible<ShaderModule>>,
pub(crate) bind_group_layouts: Registry<Fallible<BindGroupLayout>>,
pub(crate) bind_groups: Registry<Fallible<BindGroup>>,
pub(crate) command_encoders: Registry<Arc<CommandEncoder>>,
pub(crate) command_buffers: Registry<Arc<CommandBuffer>>,
pub(crate) render_bundles: Registry<Fallible<RenderBundle>>,
pub(crate) render_pipelines: Registry<Fallible<RenderPipeline>>,
@ -196,6 +198,7 @@ impl Hub {
shader_modules: Registry::new(),
bind_group_layouts: Registry::new(),
bind_groups: Registry::new(),
command_encoders: Registry::new(),
command_buffers: Registry::new(),
render_bundles: Registry::new(),
render_pipelines: Registry::new(),
@ -221,6 +224,7 @@ impl Hub {
shader_modules: self.shader_modules.generate_report(),
bind_group_layouts: self.bind_group_layouts.generate_report(),
bind_groups: self.bind_groups.generate_report(),
command_encoders: self.command_encoders.generate_report(),
command_buffers: self.command_buffers.generate_report(),
render_bundles: self.render_bundles.generate_report(),
render_pipelines: self.render_pipelines.generate_report(),

View File

@ -280,21 +280,6 @@ ids! {
pub type TlasId Tlas;
}
// The CommandBuffer type serves both as encoder and
// buffer, which is why the 2 functions below exist.
impl CommandEncoderId {
pub fn into_command_buffer_id(self) -> CommandBufferId {
Id(self.0, PhantomData)
}
}
impl CommandBufferId {
pub fn into_command_encoder_id(self) -> CommandEncoderId {
Id(self.0, PhantomData)
}
}
#[test]
fn test_id() {
let indexes = [0, Index::MAX / 2 - 1, Index::MAX / 2 + 1, Index::MAX];

View File

@ -13,6 +13,18 @@ fn extract_marker<'a>(data: &'a [u8], range: &core::ops::Range<u32>) -> &'a str
core::str::from_utf8(&data[range.start as usize..range.end as usize]).unwrap()
}
fn to_debug_str(s: &str) -> &str {
// The spec mentions that if the length given to debug functions is negative,
// the implementation will access the ptr and look for a null that terminates
// the string but some implementations will try to access the ptr even if the
// length is 0.
if s.is_empty() {
"<empty>"
} else {
s
}
}
fn get_2d_target(target: u32, array_layer: u32) -> u32 {
const CUBEMAP_FACES: [u32; 6] = [
glow::TEXTURE_CUBE_MAP_POSITIVE_X,
@ -1586,7 +1598,7 @@ impl super::Queue {
glow::DEBUG_TYPE_MARKER,
DEBUG_ID,
glow::DEBUG_SEVERITY_NOTIFICATION,
marker,
to_debug_str(marker),
)
}
};
@ -1599,7 +1611,11 @@ impl super::Queue {
.private_caps
.contains(PrivateCapabilities::DEBUG_FNS)
{
gl.push_debug_group(glow::DEBUG_SOURCE_APPLICATION, DEBUG_ID, marker)
gl.push_debug_group(
glow::DEBUG_SOURCE_APPLICATION,
DEBUG_ID,
to_debug_str(marker),
)
}
};
}
@ -1865,7 +1881,13 @@ impl crate::Queue for super::Queue {
.private_caps
.contains(PrivateCapabilities::DEBUG_FNS)
{
unsafe { gl.push_debug_group(glow::DEBUG_SOURCE_APPLICATION, DEBUG_ID, label) };
unsafe {
gl.push_debug_group(
glow::DEBUG_SOURCE_APPLICATION,
DEBUG_ID,
to_debug_str(label),
)
};
}
}

View File

@ -580,7 +580,6 @@ pub struct CoreCommandEncoder {
pub(crate) context: ContextWgpuCore,
id: wgc::id::CommandEncoderId,
error_sink: ErrorSink,
open: bool,
}
#[derive(Debug)]
@ -1588,7 +1587,6 @@ impl dispatch::DeviceInterface for CoreDevice {
context: self.context.clone(),
id,
error_sink: Arc::clone(&self.error_sink),
open: true,
}
.into()
}
@ -2390,8 +2388,10 @@ impl dispatch::CommandEncoderInterface for CoreCommandEncoder {
fn finish(&mut self) -> dispatch::DispatchCommandBuffer {
let descriptor = wgt::CommandBufferDescriptor::default();
self.open = false; // prevent the drop
let (id, error) = self.context.0.command_encoder_finish(self.id, &descriptor);
let (id, error) = self
.context
.0
.command_encoder_finish(self.id, &descriptor, None);
if let Some(cause) = error {
self.context
.handle_error_nolabel(&self.error_sink, cause, "a CommandEncoder");
@ -2646,9 +2646,7 @@ impl dispatch::CommandEncoderInterface for CoreCommandEncoder {
impl Drop for CoreCommandEncoder {
fn drop(&mut self) {
if self.open {
self.context.0.command_encoder_drop(self.id)
}
self.context.0.command_encoder_drop(self.id)
}
}