From c2bb2d5dfc265b7d1f5c8b8bbf5c141065280e28 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 14 Jun 2021 01:35:05 -0400 Subject: [PATCH] Experimental command pool API --- wgpu-core/src/command/clear.rs | 4 +- wgpu-core/src/command/compute.rs | 2 +- wgpu-core/src/command/mod.rs | 122 ++++++++++++++------- wgpu-core/src/command/query.rs | 4 +- wgpu-core/src/command/render.rs | 57 +++++----- wgpu-core/src/command/transfer.rs | 10 +- wgpu-core/src/device/life.rs | 18 ++- wgpu-core/src/device/mod.rs | 76 ++++++++++--- wgpu-core/src/device/queue.rs | 176 ++++++++++++++++++++---------- wgpu-core/src/hub.rs | 6 +- wgpu-hal/examples/halmark/main.rs | 57 +++++++--- wgpu-hal/src/empty.rs | 25 +++-- wgpu-hal/src/lib.rs | 44 ++++++-- wgpu-hal/src/metal/adapter.rs | 8 +- wgpu-hal/src/metal/device.rs | 33 ++---- wgpu-hal/src/metal/mod.rs | 131 +++++++++++++++------- wgpu-hal/src/vulkan/device.rs | 12 +- wgpu-hal/src/vulkan/mod.rs | 20 +++- 18 files changed, 541 insertions(+), 264 deletions(-) diff --git a/wgpu-core/src/command/clear.rs b/wgpu-core/src/command/clear.rs index 6a50ac708b..02b57eeeaf 100644 --- a/wgpu-core/src/command/clear.rs +++ b/wgpu-core/src/command/clear.rs @@ -144,7 +144,7 @@ impl Global { // actual hal barrier & operation let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer)); - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { cmd_buf_raw.transition_buffers(dst_barrier); cmd_buf_raw.fill_buffer(dst_raw, offset..end, 0); @@ -246,7 +246,7 @@ impl Global { // actual hal barrier & operation let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_texture)); - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { cmd_buf_raw.transition_textures(dst_barrier); /*TODO: image clears diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index be2cee4a6a..ef765f2082 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -270,7 +270,7 @@ impl Global { CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id).map_pass_err(scope)?; // will be reset to true if recording is done without errors cmd_buf.status = CommandEncoderStatus::Error; - let raw = cmd_buf.raw.last_mut().unwrap(); + let raw = cmd_buf.encoder.open(); #[cfg(feature = "trace")] if let Some(ref mut list) = cmd_buf.commands { diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs index d640698304..f9bbd46198 100644 --- a/wgpu-core/src/command/mod.rs +++ b/wgpu-core/src/command/mod.rs @@ -27,12 +27,10 @@ use crate::{ Label, Stored, }; -use hal::CommandBuffer as _; +use hal::{CommandBuffer as _, CommandPool as _}; use smallvec::SmallVec; use thiserror::Error; -use std::thread; - const PUSH_CONSTANT_CLEAR_ARRAY: &[u32] = &[0_u32; 64]; #[derive(Debug)] @@ -42,11 +40,43 @@ enum CommandEncoderStatus { Error, } -#[derive(Debug)] +struct CommandEncoder { + pool: A::CommandPool, + list: Vec, + is_open: bool, + label: String, +} + +impl CommandEncoder { + fn close(&mut self) { + if self.is_open { + self.is_open = false; + unsafe { self.list.last_mut().unwrap().finish() }; + } + } + + fn open(&mut self) -> &mut A::CommandBuffer { + if !self.is_open { + self.is_open = true; + let hal_desc = hal::CommandBufferDescriptor { + label: Some(&self.label), + }; + let raw = unsafe { self.pool.allocate(&hal_desc).unwrap() }; //TODO: handle this better + self.list.push(raw); + } + self.list.last_mut().unwrap() + } +} + +pub struct BackedCommands { + pub(crate) pool: A::CommandPool, + pub(crate) list: Vec, + pub(crate) trackers: TrackerSet, +} + pub struct CommandBuffer { - pub(crate) raw: Vec, + encoder: CommandEncoder, status: CommandEncoderStatus, - recorded_thread_id: thread::ThreadId, pub(crate) device_id: Stored, pub(crate) trackers: TrackerSet, pub(crate) used_swap_chains: SmallVec<[Stored; 1]>, @@ -56,24 +86,26 @@ pub struct CommandBuffer { support_fill_buffer_texture: bool, #[cfg(feature = "trace")] pub(crate) commands: Option>, - #[cfg(debug_assertions)] - pub(crate) label: String, } impl CommandBuffer { pub(crate) fn new( - raw: A::CommandBuffer, + pool: A::CommandPool, device_id: Stored, limits: wgt::Limits, downlevel: wgt::DownlevelCapabilities, features: wgt::Features, #[cfg(feature = "trace")] enable_tracing: bool, - #[cfg(debug_assertions)] label: &Label, + label: &Label, ) -> Self { CommandBuffer { - raw: vec![raw], + encoder: CommandEncoder { + pool, + is_open: false, + list: Vec::new(), + label: crate::LabelHelpers::borrow_or_default(label).to_string(), + }, status: CommandEncoderStatus::Recording, - recorded_thread_id: thread::current().id(), device_id, trackers: TrackerSet::new(A::VARIANT), used_swap_chains: Default::default(), @@ -87,29 +119,6 @@ impl CommandBuffer { } else { None }, - #[cfg(debug_assertions)] - label: crate::LabelHelpers::borrow_or_default(label).to_string(), - } - } - - 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, } } @@ -140,6 +149,37 @@ impl CommandBuffer { } } +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 { + pool: self.encoder.pool, + list: self.encoder.list, + trackers: self.trackers, + } + } +} + impl crate::hub::Resource for CommandBuffer { const TYPE: &'static str = "CommandBuffer"; @@ -148,10 +188,7 @@ impl crate::hub::Resource for CommandBuffer { } fn label(&self) -> &str { - #[cfg(debug_assertions)] - return &self.label; - #[cfg(not(debug_assertions))] - return ""; + &self.encoder.label } } @@ -239,6 +276,7 @@ impl Global { let error = match CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id) { Ok(cmd_buf) => { + cmd_buf.encoder.close(); cmd_buf.status = CommandEncoderStatus::Finished; // stop tracking the swapchain image, if used for sc_id in cmd_buf.used_swap_chains.iter() { @@ -269,7 +307,7 @@ impl Global { let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id)?; - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { cmd_buf_raw.begin_debug_marker(label); @@ -289,7 +327,7 @@ impl Global { let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id)?; - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { cmd_buf_raw.insert_debug_marker(label); @@ -308,7 +346,7 @@ impl Global { let (mut cmd_buf_guard, _) = hub.command_buffers.write(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id)?; - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { cmd_buf_raw.end_debug_marker(); diff --git a/wgpu-core/src/command/query.rs b/wgpu-core/src/command/query.rs index aa6e5e3d1c..8c8e3a2456 100644 --- a/wgpu-core/src/command/query.rs +++ b/wgpu-core/src/command/query.rs @@ -288,7 +288,7 @@ impl Global { let (query_set_guard, _) = hub.query_sets.read(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut cmd_buf_guard, command_encoder_id)?; - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let cmd_buf_raw = cmd_buf.encoder.open(); #[cfg(feature = "trace")] if let Some(ref mut list) = cmd_buf.commands { @@ -329,7 +329,7 @@ impl Global { let (buffer_guard, _) = hub.buffers.read(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut cmd_buf_guard, command_encoder_id)?; - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let cmd_buf_raw = cmd_buf.encoder.open(); #[cfg(feature = "trace")] if let Some(ref mut list) = cmd_buf.commands { diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 200115e59b..5d1bc615ce 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -24,7 +24,7 @@ use crate::{ }; use arrayvec::ArrayVec; -use hal::{CommandBuffer as _, Device as _}; +use hal::{CommandBuffer as _, CommandPool as _}; use thiserror::Error; use wgt::{ BufferAddress, BufferSize, BufferUsage, Color, IndexFormat, InputStepMode, TextureUsage, @@ -846,12 +846,14 @@ impl Global { let (device_guard, mut token) = hub.devices.read(&mut token); - let (cmd_buf_raw, trackers, query_reset_state) = { + let (pass_raw, trackers, query_reset_state) = { // read-only lock guard let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmb_guard, encoder_id).map_pass_err(scope)?; + // close everything while the new command encoder is filled + cmd_buf.encoder.close(); // will be reset to true if recording is done without errors cmd_buf.status = CommandEncoderStatus::Error; @@ -866,9 +868,10 @@ impl Global { let device = &device_guard[cmd_buf.device_id.value]; let mut raw = unsafe { - device - .raw - .create_command_buffer(&hal::CommandBufferDescriptor { label: base.label }) + cmd_buf + .encoder + .pool + .allocate(&hal::CommandBufferDescriptor { label: base.label }) .unwrap() //TODO: handle this better }; @@ -1736,6 +1739,9 @@ impl Global { log::trace!("Merging {:?} with the render pass", encoder_id); let (trackers, used_swapchain) = info.finish(&mut raw, &*texture_guard).map_pass_err(scope)?; + unsafe { + raw.finish(); + } cmd_buf.status = CommandEncoderStatus::Recording; cmd_buf.used_swap_chains.extend(used_swapchain); (raw, trackers, query_reset_state) @@ -1748,29 +1754,28 @@ impl Global { let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmb_guard, encoder_id).map_pass_err(scope)?; - let last_cmd_buf = cmd_buf.raw.last_mut().unwrap(); + { + let transit = cmd_buf.encoder.open(); + query_reset_state + .reset_queries( + transit, + &query_set_guard, + cmd_buf.device_id.value.0.backend(), + ) + .map_err(RenderCommandError::InvalidQuerySet) + .map_pass_err(PassErrorScope::QueryReset)?; - query_reset_state - .reset_queries( - last_cmd_buf, - &query_set_guard, - cmd_buf.device_id.value.0.backend(), - ) - .map_err(RenderCommandError::InvalidQuerySet) - .map_pass_err(PassErrorScope::QueryReset)?; - - super::CommandBuffer::insert_barriers( - last_cmd_buf, - &mut cmd_buf.trackers, - &trackers.buffers, - &trackers.textures, - &*buffer_guard, - &*texture_guard, - ); - unsafe { - last_cmd_buf.finish(); + super::CommandBuffer::insert_barriers( + transit, + &mut cmd_buf.trackers, + &trackers.buffers, + &trackers.textures, + &*buffer_guard, + &*texture_guard, + ); } - cmd_buf.raw.push(cmd_buf_raw); + cmd_buf.encoder.close(); + cmd_buf.encoder.list.push(pass_raw); Ok(()) } diff --git a/wgpu-core/src/command/transfer.rs b/wgpu-core/src/command/transfer.rs index 0499653008..044130383a 100644 --- a/wgpu-core/src/command/transfer.rs +++ b/wgpu-core/src/command/transfer.rs @@ -431,7 +431,7 @@ impl Global { dst_offset: destination_offset, size: wgt::BufferSize::new(size).unwrap(), }; - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { cmd_buf_raw.transition_buffers(src_barrier.into_iter().chain(dst_barrier)); cmd_buf_raw.copy_buffer_to_buffer(src_raw, dst_raw, iter::once(region)); @@ -450,6 +450,7 @@ impl Global { let hub = A::hub(self); let mut token = Token::root(); + let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id)?; let (buffer_guard, mut token) = hub.buffers.read(&mut token); @@ -554,7 +555,7 @@ impl Global { depth_or_array_layers: copy_size.depth_or_array_layers, }, }; - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { cmd_buf_raw.transition_buffers(src_barriers); cmd_buf_raw.transition_textures(dst_barriers); @@ -574,6 +575,7 @@ impl Global { let hub = A::hub(self); let mut token = Token::root(); + let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id)?; let (buffer_guard, mut token) = hub.buffers.read(&mut token); @@ -682,7 +684,7 @@ impl Global { depth_or_array_layers: copy_size.depth_or_array_layers, }, }; - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { cmd_buf_raw.transition_buffers(dst_barriers); cmd_buf_raw.transition_textures(src_barriers); @@ -805,7 +807,7 @@ impl Global { depth_or_array_layers: copy_size.depth_or_array_layers, }, }; - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { cmd_buf_raw.transition_textures(barriers.into_iter()); cmd_buf_raw.copy_texture_to_texture( diff --git a/wgpu-core/src/device/life.rs b/wgpu-core/src/device/life.rs index 02c95277cf..184f805085 100644 --- a/wgpu-core/src/device/life.rs +++ b/wgpu-core/src/device/life.rs @@ -5,7 +5,10 @@ #[cfg(feature = "trace")] use crate::device::trace; use crate::{ - device::{queue::TempResource, DeviceError}, + device::{ + queue::{PoolExecution, TempResource}, + DeviceError, + }, hub::{GlobalIdentityHandlerFactory, HalApi, Hub, Token}, id, resource, track::TrackerSet, @@ -165,6 +168,7 @@ struct ActiveSubmission { index: SubmissionIndex, last_resources: NonReferencedResources, mapped: Vec>, + pool_executions: Vec>, } #[derive(Clone, Debug, Error)] @@ -221,6 +225,7 @@ impl LifetimeTracker { index: SubmissionIndex, new_suspects: &SuspectedResources, temp_resources: impl Iterator>, + pool_executions: Vec>, ) { let mut last_resources = NonReferencedResources::new(); for res in temp_resources { @@ -246,6 +251,7 @@ impl LifetimeTracker { index, last_resources, mapped: Vec::new(), + pool_executions, }); } @@ -254,7 +260,11 @@ impl LifetimeTracker { } /// Returns the last submission index that is done. - pub fn triage_submissions(&mut self, last_done: SubmissionIndex) { + pub fn triage_submissions( + &mut self, + last_done: SubmissionIndex, + pool_allocator: &Mutex>, + ) { profiling::scope!("triage_submissions"); //TODO: enable when `is_sorted_by_key` is stable @@ -269,6 +279,10 @@ impl LifetimeTracker { log::trace!("Active submission {} is done", a.index); self.free_resources.extend(a.last_resources); self.ready_to_map.extend(a.mapped); + for pool_exec in a.pool_executions { + let cmd_pool = unsafe { pool_exec.finish() }; + pool_allocator.lock().release_pool(cmd_pool); + } } } diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 0caf8d006d..0a4fcdb412 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -16,7 +16,7 @@ use crate::{ use arrayvec::ArrayVec; use copyless::VecHelper as _; -use hal::{CommandBuffer as _, Device as _}; +use hal::{CommandBuffer as _, CommandPool as _, Device as _}; use parking_lot::{Mutex, MutexGuard}; use thiserror::Error; use wgt::{BufferAddress, TextureFormat, TextureViewDimension}; @@ -180,6 +180,30 @@ fn fire_map_callbacks>(callback } } +struct PoolAllocator { + free_pools: Vec, +} + +impl PoolAllocator { + fn acquire_pool( + &mut self, + device: &A::Device, + queue: &A::Queue, + ) -> Result { + match self.free_pools.pop() { + Some(pool) => Ok(pool), + None => unsafe { + let hal_desc = hal::CommandPoolDescriptor { label: None, queue }; + device.create_command_pool(&hal_desc) + }, + } + } + + fn release_pool(&mut self, pool: A::CommandPool) { + self.free_pools.push(pool); + } +} + /// Structure describing a logical device. Some members are internally mutable, /// stored behind mutexes. /// TODO: establish clear order of locking for these: @@ -199,6 +223,7 @@ pub struct Device { //desc_allocator: Mutex>, //Note: The submission index here corresponds to the last submission that is done. pub(crate) life_guard: LifeGuard, + pool_allocator: Mutex>, pub(crate) active_submission_index: SubmissionIndex, fence: A::Fence, /// Has to be locked temporarily only (locked last) @@ -240,11 +265,20 @@ impl Device { let fence = unsafe { open.device.create_fence() }.map_err(|_| CreateDeviceError::OutOfMemory)?; + let mut palloc = PoolAllocator { + free_pools: Vec::new(), + }; + let pending_pool = palloc + .acquire_pool(&open.device, &open.queue) + .map_err(|_| CreateDeviceError::OutOfMemory)?; + let pending_writes = queue::PendingWrites::new(pending_pool); + Ok(Self { raw: open.device, adapter_id, queue: open.queue, life_guard: LifeGuard::new(""), + pool_allocator: Mutex::new(palloc), active_submission_index: 0, fence, trackers: Mutex::new(TrackerSet::new(A::VARIANT)), @@ -268,7 +302,7 @@ impl Device { limits: desc.limits.clone(), features: desc.features, downlevel, - pending_writes: queue::PendingWrites::new(), + pending_writes, }) } @@ -329,7 +363,7 @@ impl Device { } }; - life_tracker.triage_submissions(last_done_index); + life_tracker.triage_submissions(last_done_index, &self.pool_allocator); let callbacks = life_tracker.handle_mapping(hub, &self.raw, &self.trackers, token); life_tracker.cleanup(&self.raw); @@ -2179,7 +2213,8 @@ impl Device { .wait(&self.fence, submission_index, !0) .map_err(DeviceError::from)? }; - self.lock_life(token).triage_submissions(submission_index); + self.lock_life(token) + .triage_submissions(submission_index, &self.pool_allocator); } Ok(()) } @@ -2241,6 +2276,18 @@ impl Device { } } + pub(crate) fn destroy_command_buffer(&self, cmd_buf: command::CommandBuffer) { + let mut baked = cmd_buf.into_baked(); + for raw in baked.list { + unsafe { + baked.pool.free(raw); + } + } + unsafe { + self.raw.destroy_command_pool(baked.pool); + } + } + /// Wait for idle and remove resources that we can, before we die. pub(crate) fn prepare_to_die(&mut self) { let mut life_tracker = self.life_tracker.lock(); @@ -2248,7 +2295,7 @@ impl Device { if let Err(error) = unsafe { self.raw.wait(&self.fence, current_index, CLEANUP_WAIT_MS) } { log::error!("failed to wait for the device: {:?}", error); } - life_tracker.triage_submissions(current_index); + life_tracker.triage_submissions(current_index, &self.pool_allocator); life_tracker.cleanup(&self.raw); } @@ -3430,33 +3477,28 @@ impl Global { Ok(device) => device, Err(_) => break DeviceError::Invalid, }; - let dev_stored = Stored { value: id::Valid(device_id), ref_count: device.life_guard.add_ref(), }; - let cmd_buf_result = unsafe { - device - .raw - .create_command_buffer(&hal::CommandBufferDescriptor { - label: desc.label.borrow_option(), - }) + let hal_desc = hal::CommandPoolDescriptor { + label: None, + queue: &device.queue, }; - let raw = match cmd_buf_result { - Ok(raw) => raw, - Err(error) => break DeviceError::from(error), + let pool = match unsafe { device.raw.create_command_pool(&hal_desc) } { + Ok(pool) => pool, + Err(_) => break DeviceError::OutOfMemory, }; let command_buffer = command::CommandBuffer::new( - raw, + pool, dev_stored, device.limits.clone(), device.downlevel, device.features, #[cfg(feature = "trace")] device.trace.is_some(), - #[cfg(debug_assertions)] &desc.label, ); diff --git a/wgpu-core/src/device/queue.rs b/wgpu-core/src/device/queue.rs index 93002d7d8d..6c524d0a4e 100644 --- a/wgpu-core/src/device/queue.rs +++ b/wgpu-core/src/device/queue.rs @@ -18,11 +18,19 @@ use crate::{ FastHashMap, FastHashSet, }; -use hal::{CommandBuffer as _, Device as _, Queue as _}; +use hal::{CommandBuffer as _, CommandPool as _, Device as _, Queue as _}; +use parking_lot::Mutex; use smallvec::SmallVec; -use std::{iter, num::NonZeroU32, ops::Range, ptr}; +use std::{iter, mem, num::NonZeroU32, ops::Range, ptr}; use thiserror::Error; +/// Number of command buffers that we generate from the same pool +/// for the write_xxx commands, before the pool is recycled. +/// +/// If we don't stop at some point, the pool will grow forever, +/// without a concrete moment of when it can be cleared. +const WRITE_COMMAND_BUFFERS_PER_POOL: usize = 64; + struct StagingData { buffer: A::Buffer, cmdbuf: A::CommandBuffer, @@ -52,30 +60,52 @@ pub enum TempResource { Texture(A::Texture), } +/// A queue execution for a particular command pool. +pub(super) struct PoolExecution { + cmd_pool: A::CommandPool, + cmd_buffers: Vec, +} + +impl PoolExecution { + pub(super) unsafe fn finish(mut self) -> A::CommandPool { + for cmd_buf in self.cmd_buffers { + self.cmd_pool.free(cmd_buf); + } + self.cmd_pool + } +} + #[derive(Debug)] pub(crate) struct PendingWrites { + pub command_pool: A::CommandPool, pub command_buffer: Option, pub temp_resources: Vec>, pub dst_buffers: FastHashSet, pub dst_textures: FastHashSet, + pub executing_command_buffers: Vec, } impl PendingWrites { - pub fn new() -> Self { + pub fn new(command_pool: A::CommandPool) -> Self { Self { + command_pool, command_buffer: None, temp_resources: Vec::new(), dst_buffers: FastHashSet::default(), dst_textures: FastHashSet::default(), + executing_command_buffers: Vec::new(), } } - pub fn dispose(self, device: &A::Device) { - if let Some(cmd_buf) = self.command_buffer { - unsafe { - device.destroy_command_buffer(cmd_buf); - } + pub fn dispose(mut self, device: &A::Device) { + if let Some(raw) = self.command_buffer { + unsafe { self.command_pool.free(raw) }; } + for raw in self.executing_command_buffers { + unsafe { self.command_pool.free(raw) }; + } + unsafe { device.destroy_command_pool(self.command_pool) }; + for resource in self.temp_resources { match resource { TempResource::Buffer(buffer) => unsafe { @@ -99,28 +129,48 @@ impl PendingWrites { } #[must_use] - fn finish(&mut self) -> Option { + fn pre_submit(&mut self) -> Option<&A::CommandBuffer> { self.dst_buffers.clear(); self.dst_textures.clear(); - self.command_buffer.take().map(|mut cmd_buf| unsafe { - cmd_buf.finish(); - cmd_buf - }) + let mut cmd_buf = self.command_buffer.take()?; + unsafe { cmd_buf.finish() }; + self.executing_command_buffers.push(cmd_buf); + self.executing_command_buffers.last() } - fn create_cmd_buf(device: &A::Device) -> A::CommandBuffer { + #[must_use] + fn post_submit( + &mut self, + pool_alloc: &Mutex>, + device: &A::Device, + queue: &A::Queue, + ) -> Option> { + assert!(self.command_buffer.is_none()); + if self.executing_command_buffers.len() >= WRITE_COMMAND_BUFFERS_PER_POOL { + let new_pool = pool_alloc.lock().acquire_pool(device, queue).unwrap(); + Some(PoolExecution { + cmd_pool: mem::replace(&mut self.command_pool, new_pool), + cmd_buffers: mem::take(&mut self.executing_command_buffers), + }) + } else { + None + } + } + + fn create_cmd_buf(&mut self) -> A::CommandBuffer { unsafe { - device - .create_command_buffer(&hal::CommandBufferDescriptor { + self.command_pool + .allocate(&hal::CommandBufferDescriptor { label: Some("_PendingWrites"), }) .unwrap() } } - fn borrow_cmd_buf(&mut self, device: &A::Device) -> &mut A::CommandBuffer { + fn borrow_cmd_buf(&mut self) -> &mut A::CommandBuffer { if self.command_buffer.is_none() { - self.command_buffer = Some(Self::create_cmd_buf(device)); + let raw = self.create_cmd_buf(); + self.command_buffer = Some(raw); } self.command_buffer.as_mut().unwrap() } @@ -161,7 +211,7 @@ impl RequiredBufferInits { impl super::Device { pub fn borrow_pending_writes(&mut self) -> &mut A::CommandBuffer { - self.pending_writes.borrow_cmd_buf(&self.raw) + self.pending_writes.borrow_cmd_buf() } fn prepare_stage(&mut self, size: wgt::BufferAddress) -> Result, DeviceError> { @@ -176,7 +226,7 @@ impl super::Device { let cmdbuf = match self.pending_writes.command_buffer.take() { Some(cmdbuf) => cmdbuf, - None => PendingWrites::::create_cmd_buf(&self.raw), + None => self.pending_writes.create_cmd_buf(), }; Ok(StagingData { buffer, cmdbuf }) } @@ -190,7 +240,7 @@ impl super::Device { .dst_buffers .extend(required_buffer_inits.map.keys()); - let cmd_buf = self.pending_writes.borrow_cmd_buf(&self.raw); + let cmd_buf = self.pending_writes.borrow_cmd_buf(); let mut trackers = self.trackers.lock(); for (buffer_id, mut ranges) in required_buffer_inits.map.drain() { @@ -566,10 +616,10 @@ impl Global { let device = device_guard .get_mut(queue_id) .map_err(|_| DeviceError::Invalid)?; - let pending_write_command_buffer = device.pending_writes.finish(); device.temp_suspected.clear(); device.active_submission_index += 1; let submit_index = device.active_submission_index; + let mut active_executions = Vec::new(); { let mut signal_swapchain_semaphores = SmallVec::<[_; 1]>::new(); @@ -599,9 +649,12 @@ impl Global { // finish all the command buffers first for &cmb_id in command_buffer_ids { - let cmdbuf = match command_buffer_guard.get_mut(cmb_id) { - Ok(cmdbuf) => cmdbuf, - Err(_) => continue, + let mut cmdbuf = match hub + .command_buffers + .unregister_locked(cmb_id, &mut *command_buffer_guard) + { + Some(cmdbuf) => cmdbuf, + None => continue, }; #[cfg(feature = "trace")] if let Some(ref trace) = device.trace { @@ -611,6 +664,7 @@ impl Global { )); } if !cmdbuf.is_finished() { + device.destroy_command_buffer(cmdbuf); continue; } @@ -695,33 +749,36 @@ impl Global { } } - unsafe { - // the last buffer was open, closing now - cmdbuf.raw.last_mut().unwrap().finish(); - } + let mut baked = cmdbuf.into_baked(); + // execute resource transitions + let transit_desc = hal::CommandBufferDescriptor { + label: Some("_Transit"), + }; let mut transit = unsafe { - device - .raw - .create_command_buffer(&hal::CommandBufferDescriptor { - label: Some("_Transit"), - }) + baked + .pool + .allocate(&transit_desc) .map_err(DeviceError::from)? }; log::trace!("Stitching command buffer {:?} before submission", cmb_id); - trackers.merge_extend_stateless(&cmdbuf.trackers); + trackers.merge_extend_stateless(&baked.trackers); CommandBuffer::insert_barriers( &mut transit, &mut *trackers, - &cmdbuf.trackers.buffers, - &cmdbuf.trackers.textures, + &baked.trackers.buffers, + &baked.trackers.textures, &*buffer_guard, &*texture_guard, ); unsafe { transit.finish(); } - cmdbuf.raw.insert(0, transit); + baked.list.insert(0, transit); + active_executions.push(PoolExecution { + cmd_pool: baked.pool, + cmd_buffers: baked.list, + }); } log::trace!("Device after submission {}: {:#?}", submit_index, trackers); @@ -732,25 +789,24 @@ impl Global { } } - //Note: we could technically avoid the heap Vec here - let mut command_buffers = Vec::new(); - command_buffers.extend(pending_write_command_buffer); - for &cmd_buf_id in command_buffer_ids.iter() { - match command_buffer_guard.get_mut(cmd_buf_id) { - Ok(cmd_buf) if cmd_buf.is_finished() => { - command_buffers.extend(cmd_buf.raw.drain(..)); - } - _ => {} - } - } - + let super::Device { + ref mut pending_writes, + ref mut queue, + ref mut fence, + .. + } = *device; + let refs = pending_writes + .pre_submit() + .into_iter() + .chain( + active_executions + .iter() + .flat_map(|pool_execution| pool_execution.cmd_buffers.iter()), + ) + .collect::>(); unsafe { - device - .queue - .submit( - command_buffers.into_iter(), - Some((&mut device.fence, submit_index)), - ) + queue + .submit(&refs, Some((fence, submit_index))) .map_err(DeviceError::from)?; } } @@ -762,10 +818,18 @@ impl Global { }; profiling::scope!("cleanup"); + if let Some(pending_execution) = device.pending_writes.post_submit( + &device.pool_allocator, + &device.raw, + &device.queue, + ) { + active_executions.push(pending_execution); + } super::Device::lock_life_internal(&device.life_tracker, &mut token).track_submission( submit_index, &device.temp_suspected, device.pending_writes.temp_resources.drain(..), + active_executions, ); callbacks diff --git a/wgpu-core/src/hub.rs b/wgpu-core/src/hub.rs index aa259cb7fc..8d056232da 100644 --- a/wgpu-core/src/hub.rs +++ b/wgpu-core/src/hub.rs @@ -626,11 +626,7 @@ impl Hub { for element in self.command_buffers.data.write().map.drain(..) { if let Element::Occupied(command_buffer, _) = element { let device = &devices[command_buffer.device_id.value]; - for raw in command_buffer.raw { - unsafe { - device.raw.destroy_command_buffer(raw); - } - } + device.destroy_command_buffer(command_buffer); } } for element in self.bind_groups.data.write().map.drain(..) { diff --git a/wgpu-hal/examples/halmark/main.rs b/wgpu-hal/examples/halmark/main.rs index a2dee078d5..2f8a1b7d98 100644 --- a/wgpu-hal/examples/halmark/main.rs +++ b/wgpu-hal/examples/halmark/main.rs @@ -1,6 +1,9 @@ extern crate wgpu_hal as hal; -use hal::{Adapter as _, CommandBuffer as _, Device as _, Instance as _, Queue as _, Surface as _}; +use hal::{ + Adapter as _, CommandBuffer as _, CommandPool as _, Device as _, Instance as _, Queue as _, + Surface as _, +}; use std::{borrow::Borrow, iter, mem, num::NonZeroU32, ptr, time::Instant}; @@ -26,6 +29,11 @@ struct Locals { _pad: u32, } +struct UsedResources { + view: A::TextureView, + cmd_buf: A::CommandBuffer, +} + #[allow(dead_code)] struct Example { instance: A::Instance, @@ -45,7 +53,8 @@ struct Example { view: A::TextureView, fence: A::Fence, fence_value: hal::FenceValue, - old_views: Vec<(hal::FenceValue, A::TextureView)>, + cmd_pool: A::CommandPool, + old_resources: Vec<(hal::FenceValue, UsedResources)>, extent: [u32; 2], start: Instant, } @@ -237,10 +246,15 @@ impl Example { }; let texture = unsafe { device.create_texture(&texture_desc).unwrap() }; + let cmd_pool_desc = hal::CommandPoolDescriptor { + label: None, + queue: &queue, + }; + let mut cmd_pool = unsafe { device.create_command_pool(&cmd_pool_desc).unwrap() }; let init_cmd_desc = hal::CommandBufferDescriptor { label: Some("init"), }; - let mut init_cmd = unsafe { device.create_command_buffer(&init_cmd_desc).unwrap() }; + let mut init_cmd = unsafe { cmd_pool.allocate(&init_cmd_desc).unwrap() }; { let buffer_barrier = hal::BufferBarrier { buffer: &staging_buffer, @@ -397,11 +411,10 @@ impl Example { let fence = unsafe { let mut fence = device.create_fence().unwrap(); init_cmd.finish(); - queue - .submit(iter::once(init_cmd), Some((&mut fence, 1))) - .unwrap(); + queue.submit(&[&init_cmd], Some((&mut fence, 1))).unwrap(); device.wait(&fence, 1, !0).unwrap(); device.destroy_buffer(staging_buffer); + cmd_pool.free(init_cmd); fence }; @@ -421,7 +434,8 @@ impl Example { sampler, texture, view, - old_views: Vec::new(), + old_resources: Vec::new(), + cmd_pool, fence, fence_value: 1, extent: [window_size.0, window_size.1], @@ -494,8 +508,8 @@ impl Example { } let mut cmd_buf = unsafe { - self.device - .create_command_buffer(&hal::CommandBufferDescriptor { + self.cmd_pool + .allocate(&hal::CommandBufferDescriptor { label: Some("frame"), }) .unwrap() @@ -550,20 +564,33 @@ impl Example { self.fence_value += 1; let last_done = unsafe { self.device.get_fence_value(&self.fence).unwrap() }; - self.old_views.retain(|&(value, _)| value <= last_done); - self.old_views.push((self.fence_value, surface_tex_view)); + for i in (0..self.old_resources.len()).rev() { + if self.old_resources[i].0 <= last_done { + let (_, old) = self.old_resources.swap_remove(i); + unsafe { + self.device.destroy_texture_view(old.view); + self.cmd_pool.free(old.cmd_buf); + } + //TODO: clear the pool from time to time + } + } unsafe { cmd_buf.end_render_pass(); cmd_buf.finish(); self.queue - .submit( - iter::once(cmd_buf), - Some((&mut self.fence, self.fence_value)), - ) + .submit(&[&cmd_buf], Some((&mut self.fence, self.fence_value))) .unwrap(); self.queue.present(&mut self.surface, surface_tex).unwrap(); } + + self.old_resources.push(( + self.fence_value, + UsedResources { + view: surface_tex_view, + cmd_buf, + }, + )); } } diff --git a/wgpu-hal/src/empty.rs b/wgpu-hal/src/empty.rs index 615178b9e9..aeb20745d9 100644 --- a/wgpu-hal/src/empty.rs +++ b/wgpu-hal/src/empty.rs @@ -15,9 +15,10 @@ impl crate::Api for Api { type Instance = Context; type Surface = Context; type Adapter = Context; - type Queue = Context; type Device = Context; + type Queue = Context; + type CommandPool = Context; type CommandBuffer = Encoder; type Buffer = Resource; @@ -89,9 +90,9 @@ impl crate::Adapter for Context { } impl crate::Queue for Context { - unsafe fn submit( + unsafe fn submit( &mut self, - command_buffers: I, + command_buffers: &[&Encoder], signal_fence: Option<(&mut Resource, crate::FenceValue)>, ) -> DeviceResult<()> { Ok(()) @@ -105,6 +106,14 @@ impl crate::Queue for Context { } } +impl crate::CommandPool for Context { + unsafe fn allocate(&mut self, desc: &crate::CommandBufferDescriptor) -> DeviceResult { + Ok(Encoder) + } + unsafe fn free(&mut self, cmd_buf: Encoder) {} + unsafe fn clear(&mut self) {} +} + impl crate::Device for Context { unsafe fn create_buffer(&self, desc: &crate::BufferDescriptor) -> DeviceResult { Ok(Resource) @@ -140,13 +149,13 @@ impl crate::Device for Context { } unsafe fn destroy_sampler(&self, sampler: Resource) {} - unsafe fn create_command_buffer( + unsafe fn create_command_pool( &self, - desc: &crate::CommandBufferDescriptor, - ) -> DeviceResult { - Ok(Encoder) + desc: &crate::CommandPoolDescriptor, + ) -> DeviceResult { + Ok(Context) } - unsafe fn destroy_command_buffer(&self, cmd_buf: Encoder) {} + unsafe fn destroy_command_pool(&self, pool: Context) {} unsafe fn create_bind_group_layout( &self, diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index 085fb749db..15ce59a307 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -126,7 +126,9 @@ pub trait Api: Clone + Sized { type Surface: Surface; type Adapter: Adapter; type Device: Device; + type Queue: Queue; + type CommandPool: CommandPool; type CommandBuffer: CommandBuffer; type Buffer: fmt::Debug + Send + Sync + 'static; @@ -221,12 +223,11 @@ pub trait Device: Send + Sync { unsafe fn create_sampler(&self, desc: &SamplerDescriptor) -> Result; unsafe fn destroy_sampler(&self, sampler: A::Sampler); - //TODO: consider making DX12-style command pools - unsafe fn create_command_buffer( + unsafe fn create_command_pool( &self, - desc: &CommandBufferDescriptor, - ) -> Result; - unsafe fn destroy_command_buffer(&self, cmd_buf: A::CommandBuffer); + desc: &CommandPoolDescriptor, + ) -> Result; + unsafe fn destroy_command_pool(&self, pool: A::CommandPool); unsafe fn create_bind_group_layout( &self, @@ -281,13 +282,17 @@ pub trait Device: Send + Sync { } pub trait Queue: Send + Sync { - unsafe fn submit( + /// Submits the command buffers for execution on GPU. + /// + /// Valid usage: + /// - all of the command buffers were created from command pools + /// that are associated with this queue. + /// - all of the command buffers had `CommadBuffer::finish()` called. + unsafe fn submit( &mut self, - command_buffers: I, + command_buffers: &[&A::CommandBuffer], signal_fence: Option<(&mut A::Fence, FenceValue)>, - ) -> Result<(), DeviceError> - where - I: Iterator; + ) -> Result<(), DeviceError>; unsafe fn present( &mut self, surface: &mut A::Surface, @@ -295,6 +300,19 @@ pub trait Queue: Send + Sync { ) -> Result<(), SurfaceError>; } +pub trait CommandPool: Send + Sync { + unsafe fn allocate( + &mut self, + desc: &CommandBufferDescriptor, + ) -> Result; + unsafe fn free(&mut self, cmd_buf: A::CommandBuffer); + /// Reclaims all resources that are allocated for this pool. + /// + /// Valid usage: + /// - there are no allocated command buffers created from this pool. + unsafe fn clear(&mut self); +} + pub trait CommandBuffer: Send + Sync { unsafe fn finish(&mut self); @@ -797,6 +815,12 @@ pub struct BindGroupDescriptor<'a, A: Api> { } #[derive(Clone, Debug)] +pub struct CommandPoolDescriptor<'a, A: Api> { + pub label: Label<'a>, + pub queue: &'a A::Queue, +} + +#[derive(Debug)] pub struct CommandBufferDescriptor<'a> { pub label: Label<'a>, } diff --git a/wgpu-hal/src/metal/adapter.rs b/wgpu-hal/src/metal/adapter.rs index 8b8c6c7731..c6c38903f1 100644 --- a/wgpu-hal/src/metal/adapter.rs +++ b/wgpu-hal/src/metal/adapter.rs @@ -1,5 +1,6 @@ use mtl::{MTLFeatureSet, MTLGPUFamily, MTLLanguageVersion}; use objc::{class, msg_send, sel, sel_impl}; +use parking_lot::Mutex; use std::{sync::Arc, thread}; @@ -17,13 +18,18 @@ impl crate::Adapter for super::Adapter { &self, features: wgt::Features, ) -> Result, crate::DeviceError> { + let queue = self + .shared + .device + .lock() + .new_command_queue_with_max_command_buffer_count(5); Ok(crate::OpenDevice { device: super::Device { shared: Arc::clone(&self.shared), features, }, queue: super::Queue { - shared: Arc::clone(&self.shared), + raw: Arc::new(Mutex::new(queue)), }, }) } diff --git a/wgpu-hal/src/metal/device.rs b/wgpu-hal/src/metal/device.rs index 3ba11bb6a0..340259b7fe 100644 --- a/wgpu-hal/src/metal/device.rs +++ b/wgpu-hal/src/metal/device.rs @@ -343,35 +343,16 @@ impl crate::Device for super::Device { } unsafe fn destroy_sampler(&self, _sampler: super::Sampler) {} - unsafe fn create_command_buffer( + unsafe fn create_command_pool( &self, - desc: &crate::CommandBufferDescriptor, - ) -> DeviceResult { - let raw = self.shared.create_command_buffer(); - if let Some(label) = desc.label { - raw.set_label(label); - } - - Ok(super::CommandBuffer { - raw, - blit: None, - render: None, - compute: None, - disabilities: self.shared.disabilities.clone(), - max_buffers_per_stage: self.shared.private_caps.max_buffers_per_stage, - state: super::CommandState { - raw_primitive_type: mtl::MTLPrimitiveType::Point, - index: None, - raw_wg_size: mtl::MTLSize::new(0, 0, 0), - stage_infos: Default::default(), - storage_buffer_length_map: Default::default(), - }, - temp: super::Temp::default(), + desc: &crate::CommandPoolDescriptor, + ) -> Result { + Ok(super::CommandPool { + shared: Arc::clone(&self.shared), + raw_queue: Arc::clone(&desc.queue.raw), }) } - unsafe fn destroy_command_buffer(&self, mut cmd_buf: super::CommandBuffer) { - cmd_buf.leave_blit(); - } + unsafe fn destroy_command_pool(&self, _pool: super::CommandPool) {} unsafe fn create_bind_group_layout( &self, diff --git a/wgpu-hal/src/metal/mod.rs b/wgpu-hal/src/metal/mod.rs index 0eba53c44d..d7b4f4629e 100644 --- a/wgpu-hal/src/metal/mod.rs +++ b/wgpu-hal/src/metal/mod.rs @@ -24,9 +24,10 @@ impl crate::Api for Api { type Instance = Instance; type Surface = Surface; type Adapter = Adapter; - type Queue = Queue; type Device = Device; + type Queue = Queue; + type CommandPool = CommandPool; type CommandBuffer = CommandBuffer; type Buffer = Buffer; @@ -208,7 +209,6 @@ struct Settings { struct AdapterShared { device: Mutex, - queue: Mutex, disabilities: PrivateDisabilities, private_caps: PrivateCapabilities, settings: Settings, @@ -225,23 +225,10 @@ impl AdapterShared { Self { disabilities: PrivateDisabilities::new(&device), private_caps: PrivateCapabilities::new(&device), - queue: Mutex::new(device.new_command_queue()), device: Mutex::new(device), settings: Settings::default(), } } - - fn create_command_buffer(&self) -> mtl::CommandBuffer { - let queue = self.queue.lock(); - objc::rc::autoreleasepool(move || { - let cmd_buf_ref = if self.settings.retain_command_buffer_references { - queue.new_command_buffer() - } else { - queue.new_command_buffer_with_unretained_references() - }; - cmd_buf_ref.to_owned() - }) - } } pub struct Adapter { @@ -249,9 +236,12 @@ pub struct Adapter { } pub struct Queue { - shared: Arc, + raw: Arc>, } +unsafe impl Send for Queue {} +unsafe impl Sync for Queue {} + pub struct Device { shared: Arc, features: wgt::Features, @@ -287,35 +277,49 @@ unsafe impl Send for SurfaceTexture {} unsafe impl Sync for SurfaceTexture {} impl crate::Queue for Queue { - unsafe fn submit( + unsafe fn submit( &mut self, - command_buffers: I, + command_buffers: &[&CommandBuffer], signal_fence: Option<(&mut Fence, crate::FenceValue)>, - ) -> Result<(), crate::DeviceError> - where - I: Iterator, - { + ) -> Result<(), crate::DeviceError> { objc::rc::autoreleasepool(|| { + let extra_command_buffer = match signal_fence { + Some((fence, value)) => { + let completed_value = Arc::clone(&fence.completed_value); + let block = block::ConcreteBlock::new(move |_cmd_buf| { + completed_value.store(value, atomic::Ordering::Release); + }) + .copy(); + + let raw = match command_buffers.last() { + Some(&cmd_buf) => cmd_buf.raw.to_owned(), + None => { + let queue = self.raw.lock(); + queue + .new_command_buffer_with_unretained_references() + .to_owned() + } + }; + raw.set_label("_Signal"); + raw.add_completed_handler(&block); + + fence.update(); + fence.pending_command_buffers.push((value, raw.to_owned())); + // only return an extra one if it's extra + match command_buffers.last() { + Some(_) => None, + None => Some(raw), + } + } + None => None, + }; + for cmd_buffer in command_buffers { cmd_buffer.raw.commit(); } - //TODO: add the handler to the last command buffer in the list - // instead of committing an extra one - if let Some((fence, value)) = signal_fence { - let completed_value = Arc::clone(&fence.completed_value); - let block = block::ConcreteBlock::new(move |_cmd_buf| { - completed_value.store(value, atomic::Ordering::Release); - }) - .copy(); - - let raw = self.shared.create_command_buffer(); - raw.set_label("_Signal"); - raw.add_completed_handler(&block); + if let Some(raw) = extra_command_buffer { raw.commit(); - - fence.update(); - fence.pending_command_buffers.push((value, raw)); } }); Ok(()) @@ -325,7 +329,7 @@ impl crate::Queue for Queue { _surface: &mut Surface, texture: SurfaceTexture, ) -> Result<(), crate::SurfaceError> { - let queue = self.shared.queue.lock(); + let queue = &self.raw.lock(); objc::rc::autoreleasepool(|| { let command_buffer = queue.new_command_buffer(); command_buffer.set_label("_Present"); @@ -346,6 +350,49 @@ impl crate::Queue for Queue { } } +impl crate::CommandPool for CommandPool { + unsafe fn allocate( + &mut self, + desc: &crate::CommandBufferDescriptor, + ) -> Result { + let queue = &self.raw_queue.lock(); + let retain_references = self.shared.settings.retain_command_buffer_references; + let raw = objc::rc::autoreleasepool(move || { + let cmd_buf_ref = if retain_references { + queue.new_command_buffer() + } else { + queue.new_command_buffer_with_unretained_references() + }; + cmd_buf_ref.to_owned() + }); + + if let Some(label) = desc.label { + raw.set_label(label); + } + + Ok(CommandBuffer { + raw, + blit: None, + render: None, + compute: None, + disabilities: self.shared.disabilities.clone(), + max_buffers_per_stage: self.shared.private_caps.max_buffers_per_stage, + state: CommandState { + raw_primitive_type: mtl::MTLPrimitiveType::Point, + index: None, + raw_wg_size: mtl::MTLSize::new(0, 0, 0), + stage_infos: Default::default(), + storage_buffer_length_map: Default::default(), + }, + temp: Temp::default(), + }) + } + unsafe fn free(&mut self, mut cmd_buf: CommandBuffer) { + cmd_buf.leave_blit(); + } + unsafe fn clear(&mut self) {} +} + #[derive(Debug)] pub struct Buffer { raw: mtl::Buffer, @@ -639,6 +686,14 @@ impl Fence { } } +pub struct CommandPool { + shared: Arc, + raw_queue: Arc>, +} + +unsafe impl Send for CommandPool {} +unsafe impl Sync for CommandPool {} + struct IndexState { buffer_ptr: BufferPtr, offset: wgt::BufferAddress, diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 528ade1802..a9c6d1de36 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -341,7 +341,7 @@ impl super::Device { } } -use super::{DeviceResult, Encoder, Resource}; //temporary +use super::{DeviceResult, Resource}; //temporary impl crate::Device for super::Device { unsafe fn create_buffer( @@ -604,13 +604,13 @@ impl crate::Device for super::Device { self.shared.raw.destroy_sampler(sampler.raw, None); } - unsafe fn create_command_buffer( + unsafe fn create_command_pool( &self, - desc: &crate::CommandBufferDescriptor, - ) -> DeviceResult { - Ok(Encoder) + desc: &crate::CommandPoolDescriptor, + ) -> DeviceResult { + Ok(super::CommandPool {}) } - unsafe fn destroy_command_buffer(&self, cmd_buf: Encoder) {} + unsafe fn destroy_command_pool(&self, cmd_pool: super::CommandPool) {} unsafe fn create_bind_group_layout( &self, diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index 5790363f41..a9f4d28cbf 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -28,9 +28,10 @@ impl crate::Api for Api { type Instance = Instance; type Surface = Surface; type Adapter = Adapter; - type Queue = Queue; type Device = Device; + type Queue = Queue; + type CommandPool = CommandPool; type CommandBuffer = Encoder; type Buffer = Buffer; @@ -198,10 +199,15 @@ pub struct BindGroup { raw: gpu_descriptor::DescriptorSet, } +#[derive(Debug)] +pub struct CommandPool { + //TODO +} + impl crate::Queue for Queue { - unsafe fn submit( + unsafe fn submit( &mut self, - command_buffers: I, + command_buffers: &[&Encoder], signal_fence: Option<(&mut Resource, crate::FenceValue)>, ) -> DeviceResult<()> { Ok(()) @@ -215,6 +221,14 @@ impl crate::Queue for Queue { } } +impl crate::CommandPool for CommandPool { + unsafe fn allocate(&mut self, desc: &crate::CommandBufferDescriptor) -> DeviceResult { + Ok(Encoder) + } + unsafe fn free(&mut self, cmd_buf: Encoder) {} + unsafe fn clear(&mut self) {} +} + impl From for crate::DeviceError { fn from(result: vk::Result) -> Self { match result {