mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
Experimental command pool API
This commit is contained in:
@@ -144,7 +144,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
// 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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
// 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
|
||||
|
||||
@@ -270,7 +270,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
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 {
|
||||
|
||||
@@ -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<A: hal::Api> {
|
||||
pool: A::CommandPool,
|
||||
list: Vec<A::CommandBuffer>,
|
||||
is_open: bool,
|
||||
label: String,
|
||||
}
|
||||
|
||||
impl<A: hal::Api> CommandEncoder<A> {
|
||||
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<A: hal::Api> {
|
||||
pub(crate) pool: A::CommandPool,
|
||||
pub(crate) list: Vec<A::CommandBuffer>,
|
||||
pub(crate) trackers: TrackerSet,
|
||||
}
|
||||
|
||||
pub struct CommandBuffer<A: hal::Api> {
|
||||
pub(crate) raw: Vec<A::CommandBuffer>,
|
||||
encoder: CommandEncoder<A>,
|
||||
status: CommandEncoderStatus,
|
||||
recorded_thread_id: thread::ThreadId,
|
||||
pub(crate) device_id: Stored<id::DeviceId>,
|
||||
pub(crate) trackers: TrackerSet,
|
||||
pub(crate) used_swap_chains: SmallVec<[Stored<id::SwapChainId>; 1]>,
|
||||
@@ -56,24 +86,26 @@ pub struct CommandBuffer<A: hal::Api> {
|
||||
support_fill_buffer_texture: bool,
|
||||
#[cfg(feature = "trace")]
|
||||
pub(crate) commands: Option<Vec<crate::device::trace::Command>>,
|
||||
#[cfg(debug_assertions)]
|
||||
pub(crate) label: String,
|
||||
}
|
||||
|
||||
impl<A: HalApi> CommandBuffer<A> {
|
||||
pub(crate) fn new(
|
||||
raw: A::CommandBuffer,
|
||||
pool: A::CommandPool,
|
||||
device_id: Stored<id::DeviceId>,
|
||||
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<A: HalApi> CommandBuffer<A> {
|
||||
} else {
|
||||
None
|
||||
},
|
||||
#[cfg(debug_assertions)]
|
||||
label: crate::LabelHelpers::borrow_or_default(label).to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_encoder_mut(
|
||||
storage: &mut Storage<Self, id::CommandEncoderId>,
|
||||
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<A: HalApi> CommandBuffer<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: hal::Api> CommandBuffer<A> {
|
||||
fn get_encoder_mut(
|
||||
storage: &mut Storage<Self, id::CommandEncoderId>,
|
||||
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<A> {
|
||||
BackedCommands {
|
||||
pool: self.encoder.pool,
|
||||
list: self.encoder.list,
|
||||
trackers: self.trackers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: hal::Api> crate::hub::Resource for CommandBuffer<A> {
|
||||
const TYPE: &'static str = "CommandBuffer";
|
||||
|
||||
@@ -148,10 +188,7 @@ impl<A: hal::Api> crate::hub::Resource for CommandBuffer<A> {
|
||||
}
|
||||
|
||||
fn label(&self) -> &str {
|
||||
#[cfg(debug_assertions)]
|
||||
return &self.label;
|
||||
#[cfg(not(debug_assertions))]
|
||||
return "";
|
||||
&self.encoder.label
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +276,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
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();
|
||||
|
||||
@@ -288,7 +288,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
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 {
|
||||
|
||||
@@ -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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -431,7 +431,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
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(
|
||||
|
||||
@@ -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<A: hal::Api> {
|
||||
index: SubmissionIndex,
|
||||
last_resources: NonReferencedResources<A>,
|
||||
mapped: Vec<id::Valid<id::BufferId>>,
|
||||
pool_executions: Vec<PoolExecution<A>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
@@ -221,6 +225,7 @@ impl<A: hal::Api> LifetimeTracker<A> {
|
||||
index: SubmissionIndex,
|
||||
new_suspects: &SuspectedResources,
|
||||
temp_resources: impl Iterator<Item = TempResource<A>>,
|
||||
pool_executions: Vec<PoolExecution<A>>,
|
||||
) {
|
||||
let mut last_resources = NonReferencedResources::new();
|
||||
for res in temp_resources {
|
||||
@@ -246,6 +251,7 @@ impl<A: hal::Api> LifetimeTracker<A> {
|
||||
index,
|
||||
last_resources,
|
||||
mapped: Vec::new(),
|
||||
pool_executions,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -254,7 +260,11 @@ impl<A: hal::Api> LifetimeTracker<A> {
|
||||
}
|
||||
|
||||
/// 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<super::PoolAllocator<A>>,
|
||||
) {
|
||||
profiling::scope!("triage_submissions");
|
||||
|
||||
//TODO: enable when `is_sorted_by_key` is stable
|
||||
@@ -269,6 +279,10 @@ impl<A: hal::Api> LifetimeTracker<A> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<I: IntoIterator<Item = BufferMapPendingCallback>>(callback
|
||||
}
|
||||
}
|
||||
|
||||
struct PoolAllocator<A: hal::Api> {
|
||||
free_pools: Vec<A::CommandPool>,
|
||||
}
|
||||
|
||||
impl<A: hal::Api> PoolAllocator<A> {
|
||||
fn acquire_pool(
|
||||
&mut self,
|
||||
device: &A::Device,
|
||||
queue: &A::Queue,
|
||||
) -> Result<A::CommandPool, hal::DeviceError> {
|
||||
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<A: hal::Api> {
|
||||
//desc_allocator: Mutex<descriptor::DescriptorAllocator<A>>,
|
||||
//Note: The submission index here corresponds to the last submission that is done.
|
||||
pub(crate) life_guard: LifeGuard,
|
||||
pool_allocator: Mutex<PoolAllocator<A>>,
|
||||
pub(crate) active_submission_index: SubmissionIndex,
|
||||
fence: A::Fence,
|
||||
/// Has to be locked temporarily only (locked last)
|
||||
@@ -240,11 +265,20 @@ impl<A: HalApi> Device<A> {
|
||||
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("<device>"),
|
||||
pool_allocator: Mutex::new(palloc),
|
||||
active_submission_index: 0,
|
||||
fence,
|
||||
trackers: Mutex::new(TrackerSet::new(A::VARIANT)),
|
||||
@@ -268,7 +302,7 @@ impl<A: HalApi> Device<A> {
|
||||
limits: desc.limits.clone(),
|
||||
features: desc.features,
|
||||
downlevel,
|
||||
pending_writes: queue::PendingWrites::new(),
|
||||
pending_writes,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -329,7 +363,7 @@ impl<A: HalApi> Device<A> {
|
||||
}
|
||||
};
|
||||
|
||||
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<A: HalApi> Device<A> {
|
||||
.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<A: hal::Api> Device<A> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn destroy_command_buffer(&self, cmd_buf: command::CommandBuffer<A>) {
|
||||
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<A: hal::Api> Device<A> {
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
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,
|
||||
);
|
||||
|
||||
|
||||
@@ -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<A: hal::Api> {
|
||||
buffer: A::Buffer,
|
||||
cmdbuf: A::CommandBuffer,
|
||||
@@ -52,30 +60,52 @@ pub enum TempResource<A: hal::Api> {
|
||||
Texture(A::Texture),
|
||||
}
|
||||
|
||||
/// A queue execution for a particular command pool.
|
||||
pub(super) struct PoolExecution<A: hal::Api> {
|
||||
cmd_pool: A::CommandPool,
|
||||
cmd_buffers: Vec<A::CommandBuffer>,
|
||||
}
|
||||
|
||||
impl<A: hal::Api> PoolExecution<A> {
|
||||
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<A: hal::Api> {
|
||||
pub command_pool: A::CommandPool,
|
||||
pub command_buffer: Option<A::CommandBuffer>,
|
||||
pub temp_resources: Vec<TempResource<A>>,
|
||||
pub dst_buffers: FastHashSet<id::BufferId>,
|
||||
pub dst_textures: FastHashSet<id::TextureId>,
|
||||
pub executing_command_buffers: Vec<A::CommandBuffer>,
|
||||
}
|
||||
|
||||
impl<A: hal::Api> PendingWrites<A> {
|
||||
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<A: hal::Api> PendingWrites<A> {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn finish(&mut self) -> Option<A::CommandBuffer> {
|
||||
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<super::PoolAllocator<A>>,
|
||||
device: &A::Device,
|
||||
queue: &A::Queue,
|
||||
) -> Option<PoolExecution<A>> {
|
||||
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<A: hal::Api> super::Device<A> {
|
||||
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<StagingData<A>, DeviceError> {
|
||||
@@ -176,7 +226,7 @@ impl<A: hal::Api> super::Device<A> {
|
||||
|
||||
let cmdbuf = match self.pending_writes.command_buffer.take() {
|
||||
Some(cmdbuf) => cmdbuf,
|
||||
None => PendingWrites::<A>::create_cmd_buf(&self.raw),
|
||||
None => self.pending_writes.create_cmd_buf(),
|
||||
};
|
||||
Ok(StagingData { buffer, cmdbuf })
|
||||
}
|
||||
@@ -190,7 +240,7 @@ impl<A: hal::Api> super::Device<A> {
|
||||
.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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
// 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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
));
|
||||
}
|
||||
if !cmdbuf.is_finished() {
|
||||
device.destroy_command_buffer(cmdbuf);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -695,33 +749,36 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
}
|
||||
}
|
||||
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
}
|
||||
}
|
||||
|
||||
//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::<Vec<_>>();
|
||||
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<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
@@ -626,11 +626,7 @@ impl<A: HalApi, F: GlobalIdentityHandlerFactory> Hub<A, F> {
|
||||
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(..) {
|
||||
|
||||
@@ -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<A: hal::Api> {
|
||||
view: A::TextureView,
|
||||
cmd_buf: A::CommandBuffer,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Example<A: hal::Api> {
|
||||
instance: A::Instance,
|
||||
@@ -45,7 +53,8 @@ struct Example<A: hal::Api> {
|
||||
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<A>)>,
|
||||
extent: [u32; 2],
|
||||
start: Instant,
|
||||
}
|
||||
@@ -237,10 +246,15 @@ impl<A: hal::Api> Example<A> {
|
||||
};
|
||||
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<A: hal::Api> Example<A> {
|
||||
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<A: hal::Api> Example<A> {
|
||||
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<A: hal::Api> Example<A> {
|
||||
}
|
||||
|
||||
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<A: hal::Api> Example<A> {
|
||||
|
||||
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,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Api> for Context {
|
||||
}
|
||||
|
||||
impl crate::Queue<Api> for Context {
|
||||
unsafe fn submit<I>(
|
||||
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<Api> for Context {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::CommandPool<Api> for Context {
|
||||
unsafe fn allocate(&mut self, desc: &crate::CommandBufferDescriptor) -> DeviceResult<Encoder> {
|
||||
Ok(Encoder)
|
||||
}
|
||||
unsafe fn free(&mut self, cmd_buf: Encoder) {}
|
||||
unsafe fn clear(&mut self) {}
|
||||
}
|
||||
|
||||
impl crate::Device<Api> for Context {
|
||||
unsafe fn create_buffer(&self, desc: &crate::BufferDescriptor) -> DeviceResult<Resource> {
|
||||
Ok(Resource)
|
||||
@@ -140,13 +149,13 @@ impl crate::Device<Api> for Context {
|
||||
}
|
||||
unsafe fn destroy_sampler(&self, sampler: Resource) {}
|
||||
|
||||
unsafe fn create_command_buffer(
|
||||
unsafe fn create_command_pool(
|
||||
&self,
|
||||
desc: &crate::CommandBufferDescriptor,
|
||||
) -> DeviceResult<Encoder> {
|
||||
Ok(Encoder)
|
||||
desc: &crate::CommandPoolDescriptor<Api>,
|
||||
) -> DeviceResult<Context> {
|
||||
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,
|
||||
|
||||
@@ -126,7 +126,9 @@ pub trait Api: Clone + Sized {
|
||||
type Surface: Surface<Self>;
|
||||
type Adapter: Adapter<Self>;
|
||||
type Device: Device<Self>;
|
||||
|
||||
type Queue: Queue<Self>;
|
||||
type CommandPool: CommandPool<Self>;
|
||||
type CommandBuffer: CommandBuffer<Self>;
|
||||
|
||||
type Buffer: fmt::Debug + Send + Sync + 'static;
|
||||
@@ -221,12 +223,11 @@ pub trait Device<A: Api>: Send + Sync {
|
||||
unsafe fn create_sampler(&self, desc: &SamplerDescriptor) -> Result<A::Sampler, DeviceError>;
|
||||
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<A::CommandBuffer, DeviceError>;
|
||||
unsafe fn destroy_command_buffer(&self, cmd_buf: A::CommandBuffer);
|
||||
desc: &CommandPoolDescriptor<A>,
|
||||
) -> Result<A::CommandPool, DeviceError>;
|
||||
unsafe fn destroy_command_pool(&self, pool: A::CommandPool);
|
||||
|
||||
unsafe fn create_bind_group_layout(
|
||||
&self,
|
||||
@@ -281,13 +282,17 @@ pub trait Device<A: Api>: Send + Sync {
|
||||
}
|
||||
|
||||
pub trait Queue<A: Api>: Send + Sync {
|
||||
unsafe fn submit<I>(
|
||||
/// 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<Item = A::CommandBuffer>;
|
||||
) -> Result<(), DeviceError>;
|
||||
unsafe fn present(
|
||||
&mut self,
|
||||
surface: &mut A::Surface,
|
||||
@@ -295,6 +300,19 @@ pub trait Queue<A: Api>: Send + Sync {
|
||||
) -> Result<(), SurfaceError>;
|
||||
}
|
||||
|
||||
pub trait CommandPool<A: Api>: Send + Sync {
|
||||
unsafe fn allocate(
|
||||
&mut self,
|
||||
desc: &CommandBufferDescriptor,
|
||||
) -> Result<A::CommandBuffer, DeviceError>;
|
||||
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<A: Api>: 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>,
|
||||
}
|
||||
|
||||
@@ -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<super::Api> for super::Adapter {
|
||||
&self,
|
||||
features: wgt::Features,
|
||||
) -> Result<crate::OpenDevice<super::Api>, 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)),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -343,35 +343,16 @@ impl crate::Device<super::Api> 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<super::CommandBuffer> {
|
||||
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<super::Api>,
|
||||
) -> Result<super::CommandPool, crate::DeviceError> {
|
||||
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,
|
||||
|
||||
@@ -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<mtl::Device>,
|
||||
queue: Mutex<mtl::CommandQueue>,
|
||||
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<AdapterShared>,
|
||||
raw: Arc<Mutex<mtl::CommandQueue>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Queue {}
|
||||
unsafe impl Sync for Queue {}
|
||||
|
||||
pub struct Device {
|
||||
shared: Arc<AdapterShared>,
|
||||
features: wgt::Features,
|
||||
@@ -287,35 +277,49 @@ unsafe impl Send for SurfaceTexture {}
|
||||
unsafe impl Sync for SurfaceTexture {}
|
||||
|
||||
impl crate::Queue<Api> for Queue {
|
||||
unsafe fn submit<I>(
|
||||
unsafe fn submit(
|
||||
&mut self,
|
||||
command_buffers: I,
|
||||
command_buffers: &[&CommandBuffer],
|
||||
signal_fence: Option<(&mut Fence, crate::FenceValue)>,
|
||||
) -> Result<(), crate::DeviceError>
|
||||
where
|
||||
I: Iterator<Item = CommandBuffer>,
|
||||
{
|
||||
) -> 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<Api> 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<Api> for Queue {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::CommandPool<Api> for CommandPool {
|
||||
unsafe fn allocate(
|
||||
&mut self,
|
||||
desc: &crate::CommandBufferDescriptor,
|
||||
) -> Result<CommandBuffer, crate::DeviceError> {
|
||||
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<AdapterShared>,
|
||||
raw_queue: Arc<Mutex<mtl::CommandQueue>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for CommandPool {}
|
||||
unsafe impl Sync for CommandPool {}
|
||||
|
||||
struct IndexState {
|
||||
buffer_ptr: BufferPtr,
|
||||
offset: wgt::BufferAddress,
|
||||
|
||||
@@ -341,7 +341,7 @@ impl super::Device {
|
||||
}
|
||||
}
|
||||
|
||||
use super::{DeviceResult, Encoder, Resource}; //temporary
|
||||
use super::{DeviceResult, Resource}; //temporary
|
||||
|
||||
impl crate::Device<super::Api> for super::Device {
|
||||
unsafe fn create_buffer(
|
||||
@@ -604,13 +604,13 @@ impl crate::Device<super::Api> 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<Encoder> {
|
||||
Ok(Encoder)
|
||||
desc: &crate::CommandPoolDescriptor<super::Api>,
|
||||
) -> DeviceResult<super::CommandPool> {
|
||||
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,
|
||||
|
||||
@@ -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<vk::DescriptorSet>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CommandPool {
|
||||
//TODO
|
||||
}
|
||||
|
||||
impl crate::Queue<Api> for Queue {
|
||||
unsafe fn submit<I>(
|
||||
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<Api> for Queue {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::CommandPool<Api> for CommandPool {
|
||||
unsafe fn allocate(&mut self, desc: &crate::CommandBufferDescriptor) -> DeviceResult<Encoder> {
|
||||
Ok(Encoder)
|
||||
}
|
||||
unsafe fn free(&mut self, cmd_buf: Encoder) {}
|
||||
unsafe fn clear(&mut self) {}
|
||||
}
|
||||
|
||||
impl From<vk::Result> for crate::DeviceError {
|
||||
fn from(result: vk::Result) -> Self {
|
||||
match result {
|
||||
|
||||
Reference in New Issue
Block a user