Early buffer destruction logic

This commit is contained in:
Dzmitry Malyshau 2020-10-08 22:28:52 -04:00
parent 0a7d81351c
commit c4c8e3b224
5 changed files with 135 additions and 50 deletions

View File

@ -150,7 +150,7 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
self.device_create_texture::<B>(device, &desc, id).unwrap();
}
A::DestroyTexture(id) => {
self.texture_drop::<B>(id);
self.texture_drop::<B>(id, true);
}
A::CreateTextureView {
id,

View File

@ -5,7 +5,7 @@
#[cfg(feature = "trace")]
use crate::device::trace;
use crate::{
device::DeviceError,
device::{queue::TempResource, DeviceError},
hub::{GfxBackend, GlobalIdentityHandlerFactory, Hub, Token},
id, resource,
track::TrackerSet,
@ -241,10 +241,16 @@ impl<B: hal::Backend> LifetimeTracker<B> {
index: SubmissionIndex,
fence: B::Fence,
new_suspects: &SuspectedResources,
temp_buffers: impl Iterator<Item = (B::Buffer, MemoryBlock<B>)>,
temp_resources: impl Iterator<Item = (TempResource<B>, MemoryBlock<B>)>,
) {
let mut last_resources = NonReferencedResources::new();
last_resources.buffers.extend(temp_buffers);
for (res, memory) in temp_resources {
match res {
TempResource::Buffer(raw) => last_resources.buffers.push((raw, memory)),
//TempResource::Image(raw) => last_resources.images.push((raw, memory)),
}
}
self.suspected_resources.buffers.extend(
self.future_suspected_buffers
.drain(..)
@ -256,6 +262,7 @@ impl<B: hal::Backend> LifetimeTracker<B> {
.map(|stored| stored.value),
);
self.suspected_resources.extend(new_suspects);
self.active.alloc().init(ActiveSubmission {
index,
fence,
@ -336,6 +343,23 @@ impl<B: hal::Backend> LifetimeTracker<B> {
descriptor_allocator_mutex.lock().cleanup(device);
}
}
pub fn schedule_resource_destruction(
&mut self,
temp_resource: TempResource<B>,
memory: MemoryBlock<B>,
last_submit_index: SubmissionIndex,
) {
let resources = self
.active
.iter_mut()
.find(|a| a.index == last_submit_index)
.map_or(&mut self.free_resources, |a| &mut a.last_resources);
match temp_resource {
TempResource::Buffer(raw) => resources.buffers.push((raw, memory)),
//TempResource::Image(raw) => resources.images.push((raw, memory)),
}
}
}
impl<B: GfxBackend> LifetimeTracker<B> {

View File

@ -1219,7 +1219,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let hub = B::hub(self);
let mut token = Token::root();
let (device_guard, mut token) = hub.devices.read(&mut token);
//TODO: lock pending writes separately, keep the device read-only
let (mut device_guard, mut token) = hub.devices.write(&mut token);
tracing::info!("Buffer {:?} is destroyed", buffer_id);
let (mut buffer_guard, _) = hub.buffers.write(&mut token);
@ -1227,18 +1228,35 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.get_mut(buffer_id)
.map_err(|_| resource::DestroyError::Invalid)?;
let device = &device_guard[buffer.device_id.value];
let device = &mut device_guard[buffer.device_id.value];
#[cfg(feature = "trace")]
if let Some(ref trace) = device.trace {
trace.lock().add(trace::Action::FreeBuffer(buffer_id));
}
let _ = device; //TODO: schedule buffer destruction
let (raw, memory) = buffer
.raw
.take()
.ok_or(resource::DestroyError::AlreadyDestroyed)?;
let temp = queue::TempResource::Buffer(raw);
if device.pending_writes.dst_buffers.contains(&buffer_id) {
device.pending_writes.temp_resources.push((temp, memory));
} else {
let last_submit_index = buffer.life_guard.submission_index.load(Ordering::Acquire);
drop(buffer_guard);
device.lock_life(&mut token).schedule_resource_destruction(
temp,
memory,
last_submit_index,
);
}
Ok(())
}
pub fn buffer_drop<B: GfxBackend>(&self, buffer_id: id::BufferId, now: bool) {
pub fn buffer_drop<B: GfxBackend>(&self, buffer_id: id::BufferId, wait: bool) {
span!(_guard, INFO, "Buffer::drop");
let hub = B::hub(self);
@ -1263,25 +1281,26 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (device_guard, mut token) = hub.devices.read(&mut token);
let device = &device_guard[device_id];
if now {
let mut life_lock = device_guard[device_id].lock_life(&mut token);
if device.pending_writes.dst_buffers.contains(&buffer_id) {
life_lock.future_suspected_buffers.push(Stored {
value: id::Valid(buffer_id),
ref_count,
});
} else {
drop(ref_count);
device
.lock_life(&mut token)
life_lock
.suspected_resources
.buffers
.push(id::Valid(buffer_id));
}
if wait {
match device.wait_for_submit(last_submit_index, &mut token) {
Ok(()) => (),
Err(e) => tracing::error!("Failed to wait for buffer {:?}: {:?}", buffer_id, e),
}
} else {
device
.lock_life(&mut token)
.future_suspected_buffers
.push(Stored {
value: id::Valid(buffer_id),
ref_count,
});
}
}
@ -1338,19 +1357,21 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
.register_error(id_in, &mut Token::root())
}
pub fn texture_drop<B: GfxBackend>(&self, texture_id: id::TextureId) {
pub fn texture_drop<B: GfxBackend>(&self, texture_id: id::TextureId, wait: bool) {
span!(_guard, INFO, "Texture::drop");
let hub = B::hub(self);
let mut token = Token::root();
let (ref_count, device_id) = {
let (ref_count, last_submit_index, device_id) = {
let (mut texture_guard, _) = hub.textures.write(&mut token);
match texture_guard.get_mut(texture_id) {
Ok(texture) => (
texture.life_guard.ref_count.take().unwrap(),
texture.device_id.value,
),
Ok(texture) => {
let ref_count = texture.life_guard.ref_count.take().unwrap();
let last_submit_index =
texture.life_guard.submission_index.load(Ordering::Acquire);
(ref_count, last_submit_index, texture.device_id.value)
}
Err(InvalidId) => {
hub.textures
.unregister_locked(texture_id, &mut *texture_guard);
@ -1360,13 +1381,28 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
};
let (device_guard, mut token) = hub.devices.read(&mut token);
device_guard[device_id]
.lock_life(&mut token)
.future_suspected_textures
.push(Stored {
let device = &device_guard[device_id];
let mut life_lock = device_guard[device_id].lock_life(&mut token);
if device.pending_writes.dst_textures.contains(&texture_id) {
life_lock.future_suspected_textures.push(Stored {
value: id::Valid(texture_id),
ref_count,
});
} else {
drop(ref_count);
life_lock
.suspected_resources
.textures
.push(id::Valid(texture_id));
}
if wait {
match device.wait_for_submit(last_submit_index, &mut token) {
Ok(()) => (),
Err(e) => tracing::error!("Failed to wait for texture {:?}: {:?}", texture_id, e),
}
}
}
pub fn texture_create_view<B: GfxBackend>(
@ -3797,7 +3833,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
device
.pending_writes
.consume_temp(stage_buffer, stage_memory);
.consume_temp(queue::TempResource::Buffer(stage_buffer), stage_memory);
}
resource::BufferMapState::Idle => {
return Err(resource::BufferAccessError::NotMapped);

View File

@ -14,7 +14,7 @@ use crate::{
hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Token},
id,
resource::{BufferAccessError, BufferMapState, BufferUse, TextureUse},
span,
span, FastHashSet,
};
use gfx_memory::{Block, Heaps, MemoryBlock};
@ -29,17 +29,27 @@ struct StagingData<B: hal::Backend> {
cmdbuf: B::CommandBuffer,
}
#[derive(Debug, Default)]
#[derive(Debug)]
pub enum TempResource<B: hal::Backend> {
Buffer(B::Buffer),
//Image(B::Image),
}
#[derive(Debug)]
pub(crate) struct PendingWrites<B: hal::Backend> {
pub command_buffer: Option<B::CommandBuffer>,
pub temp_buffers: Vec<(B::Buffer, MemoryBlock<B>)>,
pub temp_resources: Vec<(TempResource<B>, MemoryBlock<B>)>,
pub dst_buffers: FastHashSet<id::BufferId>,
pub dst_textures: FastHashSet<id::TextureId>,
}
impl<B: hal::Backend> PendingWrites<B> {
pub fn new() -> Self {
Self {
command_buffer: None,
temp_buffers: Vec::new(),
temp_resources: Vec::new(),
dst_buffers: FastHashSet::default(),
dst_textures: FastHashSet::default(),
}
}
@ -52,22 +62,38 @@ impl<B: hal::Backend> PendingWrites<B> {
if let Some(raw) = self.command_buffer {
cmd_allocator.discard_internal(raw);
}
for (buffer, memory) in self.temp_buffers {
for (resource, memory) in self.temp_resources {
mem_allocator.free(device, memory);
unsafe {
device.destroy_buffer(buffer);
match resource {
TempResource::Buffer(buffer) => unsafe {
device.destroy_buffer(buffer);
},
/*TempResource::Image(image) => unsafe {
device.destroy_image(image);
},*/
}
}
}
pub fn consume_temp(&mut self, buffer: B::Buffer, memory: MemoryBlock<B>) {
self.temp_buffers.push((buffer, memory));
pub fn consume_temp(&mut self, resource: TempResource<B>, memory: MemoryBlock<B>) {
self.temp_resources.push((resource, memory));
}
fn consume(&mut self, stage: StagingData<B>) {
self.temp_buffers.push((stage.buffer, stage.memory));
self.temp_resources
.push((TempResource::Buffer(stage.buffer), stage.memory));
self.command_buffer = Some(stage.cmdbuf);
}
#[must_use]
fn finish(&mut self) -> Option<B::CommandBuffer> {
self.dst_buffers.clear();
self.dst_textures.clear();
self.command_buffer.take().map(|mut cmd_buf| unsafe {
cmd_buf.finish();
cmd_buf
})
}
}
impl<B: hal::Backend> super::Device<B> {
@ -260,6 +286,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
device.pending_writes.consume(stage);
device.pending_writes.dst_buffers.insert(buffer_id);
Ok(())
}
@ -418,6 +445,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
device.pending_writes.consume(stage);
device
.pending_writes
.dst_textures
.insert(destination.texture);
Ok(())
}
@ -437,15 +468,7 @@ 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
.command_buffer
.take()
.map(|mut comb_raw| unsafe {
comb_raw.finish();
comb_raw
});
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;
@ -615,7 +638,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
submit_index,
fence,
&device.temp_suspected,
device.pending_writes.temp_buffers.drain(..),
device.pending_writes.temp_resources.drain(..),
);
// finally, return the command buffers to the allocator

View File

@ -232,6 +232,8 @@ macro_rules! span {
/// Fast hash map used internally.
type FastHashMap<K, V> =
std::collections::HashMap<K, V, std::hash::BuildHasherDefault<fxhash::FxHasher>>;
/// Fast hash set used internally.
type FastHashSet<K> = std::collections::HashSet<K, std::hash::BuildHasherDefault<fxhash::FxHasher>>;
#[test]
fn test_default_limits() {