27: Resource lifetime tracking r=grovesNL a=kvark

Fixes #26

This change ends up being much more serious and intrusive, but hopefully manage-able. The idea is that the native layer would have explicit deletion calls exposed, but would still guarantee that resources live for as long as they are used by either CPU or GPU. The device, in order to guarantee that, keeps track of resources associated with fences and walks through them for clean up.

Maintaining the actual ref-counts per object up-to-date is one of the most challenging tasks. I choose to use the existing `Stored` structure to host it, so that we are forced to clone the refcounts, and it's difficult to screw up. I still had to provide the "old" semantics under the new name of `WeaklyStored` though, and we should be careful about it.

TODO:
  - [ ] review and test
  - [x] fix transition API once more, so that command buffers are stitched properly and resources are tracked by the device
  - [ ] update the Rust layer, using RAII
  - [ ] update the examples

Co-authored-by: Dzmitry Malyshau <dmalyshau@mozilla.com>
This commit is contained in:
bors[bot] 2018-11-04 22:53:04 +00:00
commit cf2e62becb
9 changed files with 353 additions and 61 deletions

View File

@ -387,3 +387,7 @@ WGPUTextureViewId wgpu_texture_create_default_texture_view(WGPUTextureId texture
WGPUTextureViewId wgpu_texture_create_texture_view(WGPUTextureId texture_id, WGPUTextureViewId wgpu_texture_create_texture_view(WGPUTextureId texture_id,
const WGPUTextureViewDescriptor *desc); const WGPUTextureViewDescriptor *desc);
void wgpu_texture_destroy(WGPUDeviceId texture_id);
void wgpu_texture_view_destroy(WGPUTextureViewId _texture_view_id);

View File

@ -1,5 +1,5 @@
use super::CommandBuffer; use super::CommandBuffer;
use {DeviceId, Stored}; use {DeviceId, LifeGuard, Stored};
use track::{Tracker}; use track::{Tracker};
use hal::command::RawCommandBuffer; use hal::command::RawCommandBuffer;
@ -61,8 +61,8 @@ impl<B: hal::Backend> CommandAllocator<B> {
} }
} }
pub fn allocate( pub(crate) fn allocate(
&self, device_id: DeviceId, device: &B::Device &self, device_id: Stored<DeviceId>, device: &B::Device
) -> CommandBuffer<B> { ) -> CommandBuffer<B> {
let thread_id = thread::current().id(); let thread_id = thread::current().id();
let mut inner = self.inner.lock().unwrap(); let mut inner = self.inner.lock().unwrap();
@ -90,7 +90,8 @@ impl<B: hal::Backend> CommandAllocator<B> {
raw: vec![init], raw: vec![init],
fence, fence,
recorded_thread_id: thread_id, recorded_thread_id: thread_id,
device_id: Stored(device_id), device_id,
life_guard: LifeGuard::new(),
buffer_tracker: Tracker::new(), buffer_tracker: Tracker::new(),
texture_tracker: Tracker::new(), texture_tracker: Tracker::new(),
} }

View File

@ -16,10 +16,10 @@ pub struct ComputePass<B: hal::Backend> {
} }
impl<B: hal::Backend> ComputePass<B> { impl<B: hal::Backend> ComputePass<B> {
pub fn new(raw: B::CommandBuffer, cmb_id: CommandBufferId) -> Self { pub(crate) fn new(raw: B::CommandBuffer, cmb_id: Stored<CommandBufferId>) -> Self {
ComputePass { ComputePass {
raw, raw,
cmb_id: Stored(cmb_id), cmb_id,
} }
} }
} }
@ -34,10 +34,10 @@ pub extern "C" fn wgpu_compute_pass_end_pass(
HUB.command_buffers HUB.command_buffers
.lock() .lock()
.get_mut(pass.cmb_id.0) .get_mut(pass.cmb_id.value)
.raw .raw
.push(pass.raw); .push(pass.raw);
pass.cmb_id.0 pass.cmb_id.value
} }
pub extern "C" fn wgpu_compute_pass_set_bind_group( pub extern "C" fn wgpu_compute_pass_set_bind_group(

View File

@ -2,7 +2,7 @@ mod allocator;
mod compute; mod compute;
mod render; mod render;
pub use self::allocator::CommandAllocator; pub(crate) use self::allocator::CommandAllocator;
pub use self::compute::*; pub use self::compute::*;
pub use self::render::*; pub use self::render::*;
@ -10,7 +10,7 @@ use hal::{self, Device};
use hal::command::RawCommandBuffer; use hal::command::RawCommandBuffer;
use { use {
B, Color, Origin3d, Stored, BufferUsageFlags, TextureUsageFlags, B, Color, LifeGuard, Origin3d, Stored, BufferUsageFlags, TextureUsageFlags, WeaklyStored,
BufferId, CommandBufferId, ComputePassId, DeviceId, RenderPassId, TextureId, TextureViewId, BufferId, CommandBufferId, ComputePassId, DeviceId, RenderPassId, TextureId, TextureViewId,
}; };
use conv; use conv;
@ -83,6 +83,7 @@ pub struct CommandBuffer<B: hal::Backend> {
fence: B::Fence, fence: B::Fence,
recorded_thread_id: ThreadId, recorded_thread_id: ThreadId,
device_id: Stored<DeviceId>, device_id: Stored<DeviceId>,
life_guard: LifeGuard,
pub(crate) buffer_tracker: BufferTracker, pub(crate) buffer_tracker: BufferTracker,
pub(crate) texture_tracker: TextureTracker, pub(crate) texture_tracker: TextureTracker,
} }
@ -139,7 +140,7 @@ pub extern "C" fn wgpu_command_buffer_begin_render_pass(
let mut cmb_guard = HUB.command_buffers.lock(); let mut cmb_guard = HUB.command_buffers.lock();
let cmb = cmb_guard.get_mut(command_buffer_id); let cmb = cmb_guard.get_mut(command_buffer_id);
let device_guard = HUB.devices.lock(); let device_guard = HUB.devices.lock();
let device = device_guard.get(cmb.device_id.0); let device = device_guard.get(cmb.device_id.value);
let view_guard = HUB.texture_views.lock(); let view_guard = HUB.texture_views.lock();
let mut current_comb = device.com_allocator.extend(cmb); let mut current_comb = device.com_allocator.extend(cmb);
@ -160,7 +161,7 @@ pub extern "C" fn wgpu_command_buffer_begin_render_pass(
} else { } else {
extent = Some(view.extent); extent = Some(view.extent);
} }
let query = tracker.query(view.texture_id.0, TextureUsageFlags::empty()); let query = tracker.query(&view.texture_id, TextureUsageFlags::empty());
let (_, layout) = conv::map_texture_state( let (_, layout) = conv::map_texture_state(
query.usage, query.usage,
hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL, hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL,
@ -185,7 +186,7 @@ pub extern "C" fn wgpu_command_buffer_begin_render_pass(
} else { } else {
extent = Some(view.extent); extent = Some(view.extent);
} }
let query = tracker.query(view.texture_id.0, TextureUsageFlags::empty()); let query = tracker.query(&view.texture_id, TextureUsageFlags::empty());
let (_, layout) = conv::map_texture_state(query.usage, hal::format::Aspects::COLOR); let (_, layout) = conv::map_texture_state(query.usage, hal::format::Aspects::COLOR);
hal::pass::Attachment { hal::pass::Attachment {
format: Some(conv::map_texture_format(view.format)), format: Some(conv::map_texture_format(view.format)),
@ -234,8 +235,8 @@ pub extern "C" fn wgpu_command_buffer_begin_render_pass(
let fb_key = FramebufferKey { let fb_key = FramebufferKey {
attachments: desc.color_attachments attachments: desc.color_attachments
.iter() .iter()
.map(|at| Stored(at.attachment)) .map(|at| WeaklyStored(at.attachment))
.chain(desc.depth_stencil_attachment.as_ref().map(|at| Stored(at.attachment))) .chain(desc.depth_stencil_attachment.as_ref().map(|at| WeaklyStored(at.attachment)))
.collect(), .collect(),
}; };
let framebuffer = match framebuffer_cache.entry(fb_key) { let framebuffer = match framebuffer_cache.entry(fb_key) {
@ -246,7 +247,7 @@ pub extern "C" fn wgpu_command_buffer_begin_render_pass(
.key() .key()
.attachments .attachments
.iter() .iter()
.map(|&Stored(id)| &view_guard.get(id).raw); .map(|&WeaklyStored(id)| &view_guard.get(id).raw);
device.raw device.raw
.create_framebuffer(&render_pass, attachments, extent.unwrap()) .create_framebuffer(&render_pass, attachments, extent.unwrap())
@ -289,7 +290,10 @@ pub extern "C" fn wgpu_command_buffer_begin_render_pass(
.lock() .lock()
.register(RenderPass::new( .register(RenderPass::new(
current_comb, current_comb,
command_buffer_id, Stored {
value: command_buffer_id,
ref_count: cmb.life_guard.ref_count.clone(),
},
)) ))
} }
@ -301,8 +305,12 @@ pub extern "C" fn wgpu_command_buffer_begin_compute_pass(
let cmb = cmb_guard.get_mut(command_buffer_id); let cmb = cmb_guard.get_mut(command_buffer_id);
let raw = cmb.raw.pop().unwrap(); let raw = cmb.raw.pop().unwrap();
let stored = Stored {
value: command_buffer_id,
ref_count: cmb.life_guard.ref_count.clone(),
};
HUB.compute_passes HUB.compute_passes
.lock() .lock()
.register(ComputePass::new(raw, command_buffer_id)) .register(ComputePass::new(raw, stored))
} }

View File

@ -17,13 +17,13 @@ pub struct RenderPass<B: hal::Backend> {
} }
impl<B: hal::Backend> RenderPass<B> { impl<B: hal::Backend> RenderPass<B> {
pub fn new( pub(crate) fn new(
raw: B::CommandBuffer, raw: B::CommandBuffer,
cmb_id: CommandBufferId, cmb_id: Stored<CommandBufferId>,
) -> Self { ) -> Self {
RenderPass { RenderPass {
raw, raw,
cmb_id: Stored(cmb_id), cmb_id,
buffer_tracker: BufferTracker::new(), buffer_tracker: BufferTracker::new(),
texture_tracker: TextureTracker::new(), texture_tracker: TextureTracker::new(),
} }
@ -40,7 +40,7 @@ pub extern "C" fn wgpu_render_pass_end_pass(
pass.raw.end_render_pass(); pass.raw.end_render_pass();
let mut cmb_guard = HUB.command_buffers.lock(); let mut cmb_guard = HUB.command_buffers.lock();
let cmb = cmb_guard.get_mut(pass.cmb_id.0); let cmb = cmb_guard.get_mut(pass.cmb_id.value);
if let Some(ref mut last) = cmb.raw.last_mut() { if let Some(ref mut last) = cmb.raw.last_mut() {
CommandBuffer::insert_barriers( CommandBuffer::insert_barriers(
@ -52,5 +52,5 @@ pub extern "C" fn wgpu_render_pass_end_pass(
} }
cmb.raw.push(pass.raw); cmb.raw.push(pass.raw);
pass.cmb_id.0 pass.cmb_id.value
} }

View File

@ -1,9 +1,10 @@
use {back, binding_model, command, conv, pipeline, resource}; use {back, binding_model, command, conv, pipeline, resource};
use registry::{HUB, Items, Registry}; use registry::{HUB, Items, ItemsGuard, Registry};
use track::{BufferTracker, TextureTracker}; use track::{BufferTracker, TextureTracker};
use { use {
CommandBuffer, Stored, TextureUsageFlags, CommandBuffer, LifeGuard, RefCount, Stored, SubmissionIndex, WeaklyStored,
BindGroupLayoutId, BlendStateId, CommandBufferId, DepthStencilStateId, TextureUsageFlags,
BindGroupLayoutId, BlendStateId, BufferId, CommandBufferId, DepthStencilStateId,
DeviceId, PipelineLayoutId, QueueId, RenderPipelineId, ShaderModuleId, DeviceId, PipelineLayoutId, QueueId, RenderPipelineId, ShaderModuleId,
TextureId, TextureViewId, TextureId, TextureViewId,
}; };
@ -16,6 +17,7 @@ use rendy_memory::{allocator, Config, Heaps};
use std::{ffi, slice}; use std::{ffi, slice};
use std::collections::hash_map::{Entry, HashMap}; use std::collections::hash_map::{Entry, HashMap};
use std::sync::Mutex; use std::sync::Mutex;
use std::sync::atomic::Ordering;
#[derive(Hash, PartialEq)] #[derive(Hash, PartialEq)]
@ -26,21 +28,115 @@ impl Eq for RenderPassKey {}
#[derive(Hash, PartialEq)] #[derive(Hash, PartialEq)]
pub(crate) struct FramebufferKey { pub(crate) struct FramebufferKey {
pub attachments: Vec<Stored<TextureViewId>>, pub attachments: Vec<WeaklyStored<TextureViewId>>,
} }
impl Eq for FramebufferKey {} impl Eq for FramebufferKey {}
enum ResourceId {
Buffer(BufferId),
Texture(TextureId),
}
enum Resource<B: hal::Backend> {
Buffer(resource::Buffer<B>),
Texture(resource::Texture<B>),
}
struct ActiveFrame<B: hal::Backend> {
submission_index: SubmissionIndex,
fence: B::Fence,
resources: Vec<Resource<B>>,
}
struct DestroyedResources<B: hal::Backend> {
/// Resources that are destroyed by the user but still referenced by
/// other objects or command buffers.
referenced: Vec<(ResourceId, RefCount)>,
/// Resources that are not referenced any more but still used by GPU.
/// Grouped by frames associated with a fence and a submission index.
active: Vec<ActiveFrame<B>>,
/// Resources that are neither referenced or used, just pending
/// actual deletion.
free: Vec<Resource<B>>,
}
unsafe impl<B: hal::Backend> Send for DestroyedResources<B> {}
unsafe impl<B: hal::Backend> Sync for DestroyedResources<B> {}
impl<B: hal::Backend> DestroyedResources<B> {
fn add(&mut self, resource_id: ResourceId, life_guard: &LifeGuard) {
self.referenced.push((resource_id, life_guard.ref_count.clone()));
}
fn triage_referenced(
&mut self,
buffer_guard: &mut ItemsGuard<resource::Buffer<B>>,
texture_guard: &mut ItemsGuard<resource::Texture<B>>,
) {
for i in (0 .. self.referenced.len()).rev() {
// one in resource itself, and one here in this list
let num_refs = self.referenced[i].1.load();
if num_refs <= 2 {
assert_eq!(num_refs, 2);
let resource_id = self.referenced.swap_remove(i).0;
let (submit_index, resource) = match resource_id {
ResourceId::Buffer(id) => {
let buf = buffer_guard.take(id);
let si = buf.life_guard.submission_index.load(Ordering::Acquire);
(si, Resource::Buffer(buf))
}
ResourceId::Texture(id) => {
let tex = texture_guard.take(id);
let si = tex.life_guard.submission_index.load(Ordering::Acquire);
(si, Resource::Texture(tex))
}
};
match self.active
.iter_mut()
.find(|af| af.submission_index == submit_index)
{
Some(af) => af.resources.push(resource),
None => self.free.push(resource),
}
}
}
}
fn cleanup(&mut self, raw: &B::Device) {
for i in (0 .. self.active.len()).rev() {
if raw.get_fence_status(&self.active[i].fence) {
let af = self.active.swap_remove(i);
self.free.extend(af.resources);
raw.destroy_fence(af.fence);
}
}
for resource in self.free.drain(..) {
match resource {
Resource::Buffer(buf) => {
raw.destroy_buffer(buf.raw);
}
Resource::Texture(tex) => {
raw.destroy_image(tex.raw);
}
}
}
}
}
pub struct Device<B: hal::Backend> { pub struct Device<B: hal::Backend> {
pub(crate) raw: B::Device, pub(crate) raw: B::Device,
queue_group: hal::QueueGroup<B, hal::General>, queue_group: hal::QueueGroup<B, hal::General>,
mem_allocator: Heaps<B::Memory>, mem_allocator: Heaps<B::Memory>,
pub(crate) com_allocator: command::CommandAllocator<B>, pub(crate) com_allocator: command::CommandAllocator<B>,
life_guard: LifeGuard,
buffer_tracker: Mutex<BufferTracker>, buffer_tracker: Mutex<BufferTracker>,
texture_tracker: Mutex<TextureTracker>, texture_tracker: Mutex<TextureTracker>,
mem_props: hal::MemoryProperties, mem_props: hal::MemoryProperties,
pub(crate) render_passes: Mutex<HashMap<RenderPassKey, B::RenderPass>>, pub(crate) render_passes: Mutex<HashMap<RenderPassKey, B::RenderPass>>,
pub(crate) framebuffers: Mutex<HashMap<FramebufferKey, B::Framebuffer>>, pub(crate) framebuffers: Mutex<HashMap<FramebufferKey, B::Framebuffer>>,
last_submission_index: SubmissionIndex,
destroyed: Mutex<DestroyedResources<B>>,
} }
impl<B: hal::Backend> Device<B> { impl<B: hal::Backend> Device<B> {
@ -75,11 +171,18 @@ impl<B: hal::Backend> Device<B> {
mem_allocator, mem_allocator,
com_allocator: command::CommandAllocator::new(queue_group.family()), com_allocator: command::CommandAllocator::new(queue_group.family()),
queue_group, queue_group,
life_guard: LifeGuard::new(),
buffer_tracker: Mutex::new(BufferTracker::new()), buffer_tracker: Mutex::new(BufferTracker::new()),
texture_tracker: Mutex::new(TextureTracker::new()), texture_tracker: Mutex::new(TextureTracker::new()),
mem_props, mem_props,
render_passes: Mutex::new(HashMap::new()), render_passes: Mutex::new(HashMap::new()),
framebuffers: Mutex::new(HashMap::new()), framebuffers: Mutex::new(HashMap::new()),
last_submission_index: 0,
destroyed: Mutex::new(DestroyedResources {
referenced: Vec::new(),
active: Vec::new(),
free: Vec::new(),
}),
} }
} }
} }
@ -136,19 +239,28 @@ pub extern "C" fn wgpu_device_create_texture(
layers: 0 .. 1, //TODO layers: 0 .. 1, //TODO
}; };
let life_guard = LifeGuard::new();
let ref_count = life_guard.ref_count.clone();
let id = HUB.textures let id = HUB.textures
.lock() .lock()
.register(resource::Texture { .register(resource::Texture {
raw: bound_image, raw: bound_image,
device_id: Stored(device_id), device_id: Stored {
value: device_id,
ref_count: device.life_guard.ref_count.clone(),
},
kind, kind,
format: desc.format, format: desc.format,
full_range, full_range,
life_guard,
}); });
let query = device.texture_tracker let query = device.texture_tracker
.lock() .lock()
.unwrap() .unwrap()
.query(id, TextureUsageFlags::WRITE_ALL); .query(
&Stored { value: id, ref_count },
TextureUsageFlags::WRITE_ALL,
);
assert!(query.initialized); assert!(query.initialized);
id id
@ -164,7 +276,7 @@ pub extern "C" fn wgpu_texture_create_texture_view(
let raw = HUB.devices let raw = HUB.devices
.lock() .lock()
.get(texture.device_id.0) .get(texture.device_id.value)
.raw .raw
.create_image_view( .create_image_view(
&texture.raw, &texture.raw,
@ -183,10 +295,14 @@ pub extern "C" fn wgpu_texture_create_texture_view(
.lock() .lock()
.register(resource::TextureView { .register(resource::TextureView {
raw, raw,
texture_id: Stored(texture_id), texture_id: Stored {
value: texture_id,
ref_count: texture.life_guard.ref_count.clone(),
},
format: texture.format, format: texture.format,
extent: texture.kind.extent(), extent: texture.kind.extent(),
samples: texture.kind.num_samples(), samples: texture.kind.num_samples(),
life_guard: LifeGuard::new(),
}) })
} }
@ -205,7 +321,7 @@ pub extern "C" fn wgpu_texture_create_default_texture_view(
let raw = HUB.devices let raw = HUB.devices
.lock() .lock()
.get(texture.device_id.0) .get(texture.device_id.value)
.raw .raw
.create_image_view( .create_image_view(
&texture.raw, &texture.raw,
@ -220,13 +336,38 @@ pub extern "C" fn wgpu_texture_create_default_texture_view(
.lock() .lock()
.register(resource::TextureView { .register(resource::TextureView {
raw, raw,
texture_id: Stored(texture_id), texture_id: Stored {
value: texture_id,
ref_count: texture.life_guard.ref_count.clone(),
},
format: texture.format, format: texture.format,
extent: texture.kind.extent(), extent: texture.kind.extent(),
samples: texture.kind.num_samples(), samples: texture.kind.num_samples(),
life_guard: LifeGuard::new(),
}) })
} }
#[no_mangle]
pub extern "C" fn wgpu_texture_destroy(
texture_id: DeviceId,
) {
let texture_guard = HUB.textures.lock();
let texture = texture_guard.get(texture_id);
let device_guard = HUB.devices.lock();
device_guard
.get(texture.device_id.value)
.destroyed
.lock()
.unwrap()
.add(ResourceId::Texture(texture_id), &texture.life_guard);
}
#[no_mangle]
pub extern "C" fn wgpu_texture_view_destroy(
_texture_view_id: TextureViewId,
) {
unimplemented!()
}
#[no_mangle] #[no_mangle]
pub extern "C" fn wgpu_device_create_bind_group_layout( pub extern "C" fn wgpu_device_create_bind_group_layout(
@ -338,7 +479,11 @@ pub extern "C" fn wgpu_device_create_command_buffer(
let device_guard = HUB.devices.lock(); let device_guard = HUB.devices.lock();
let device = device_guard.get(device_id); let device = device_guard.get(device_id);
let mut cmd_buf = device.com_allocator.allocate(device_id, &device.raw); let dev_stored = Stored {
value: device_id,
ref_count: device.life_guard.ref_count.clone(),
};
let mut cmd_buf = device.com_allocator.allocate(dev_stored, &device.raw);
cmd_buf.raw.last_mut().unwrap().begin( cmd_buf.raw.last_mut().unwrap().begin(
hal::command::CommandBufferFlags::ONE_TIME_SUBMIT, hal::command::CommandBufferFlags::ONE_TIME_SUBMIT,
hal::command::CommandBufferInheritanceInfo::default(), hal::command::CommandBufferInheritanceInfo::default(),
@ -367,6 +512,10 @@ pub extern "C" fn wgpu_queue_submit(
slice::from_raw_parts(command_buffer_ptr, command_buffer_count) slice::from_raw_parts(command_buffer_ptr, command_buffer_count)
}; };
let mut buffer_guard = HUB.buffers.lock();
let mut texture_guard = HUB.textures.lock();
let old_submit_index = device.life_guard.submission_index.fetch_add(1, Ordering::Relaxed);
//TODO: if multiple command buffers are submitted, we can re-use the last //TODO: if multiple command buffers are submitted, we can re-use the last
// native command buffer of the previous chain instead of always creating // native command buffer of the previous chain instead of always creating
// a temporary one, since the chains are not finished. // a temporary one, since the chains are not finished.
@ -374,11 +523,21 @@ pub extern "C" fn wgpu_queue_submit(
// finish all the command buffers first // finish all the command buffers first
for &cmb_id in command_buffer_ids { for &cmb_id in command_buffer_ids {
let comb = command_buffer_guard.get_mut(cmb_id); let comb = command_buffer_guard.get_mut(cmb_id);
// update submission IDs
for id in comb.buffer_tracker.used() {
buffer_guard.get(id).life_guard.submission_index.store(old_submit_index, Ordering::Release);
}
for id in comb.texture_tracker.used() {
texture_guard.get(id).life_guard.submission_index.store(old_submit_index, Ordering::Release);
}
// execute resource transitions
let mut transit = device.com_allocator.extend(comb); let mut transit = device.com_allocator.extend(comb);
transit.begin( transit.begin(
hal::command::CommandBufferFlags::ONE_TIME_SUBMIT, hal::command::CommandBufferFlags::ONE_TIME_SUBMIT,
hal::command::CommandBufferInheritanceInfo::default(), hal::command::CommandBufferInheritanceInfo::default(),
); );
//TODO: fix the consume
CommandBuffer::insert_barriers( CommandBuffer::insert_barriers(
&mut transit, &mut transit,
buffer_tracker.consume(&comb.buffer_tracker), buffer_tracker.consume(&comb.buffer_tracker),
@ -393,6 +552,7 @@ pub extern "C" fn wgpu_queue_submit(
} }
// now prepare the GPU submission // now prepare the GPU submission
let fence = device.raw.create_fence(false);
{ {
let submission = hal::queue::RawSubmission { let submission = hal::queue::RawSubmission {
cmd_buffers: command_buffer_ids cmd_buffers: command_buffer_ids
@ -406,10 +566,21 @@ pub extern "C" fn wgpu_queue_submit(
unsafe { unsafe {
device.queue_group.queues[0] device.queue_group.queues[0]
.as_raw_mut() .as_raw_mut()
.submit_raw(submission, None); .submit_raw(submission, Some(&fence));
} }
} }
if let Ok(mut destroyed) = device.destroyed.lock() {
destroyed.triage_referenced(&mut buffer_guard, &mut texture_guard);
destroyed.cleanup(&device.raw);
destroyed.active.push(ActiveFrame {
submission_index: old_submit_index + 1,
fence,
resources: Vec::new(),
});
}
// finally, return the command buffers to the allocator // finally, return the command buffers to the allocator
for &cmb_id in command_buffer_ids { for &cmb_id in command_buffer_ids {
let cmd_buf = command_buffer_guard.take(cmb_id); let cmd_buf = command_buffer_guard.take(cmb_id);

View File

@ -43,12 +43,78 @@ pub use self::resource::*;
use back::Backend as B; use back::Backend as B;
use registry::Id; use registry::Id;
#[derive(Debug, Hash, PartialEq, Eq)] use std::ptr;
struct Stored<T>(T); use std::sync::atomic::{AtomicUsize, Ordering};
#[cfg(not(feature = "remote"))]
unsafe impl<T> Sync for Stored<T> {}
#[cfg(not(feature = "remote"))] //#[cfg(not(feature = "remote"))]
//unsafe impl<T> Sync for Stored<T> {}
//#[cfg(not(feature = "remote"))]
//unsafe impl<T> Send for Stored<T> {}
type SubmissionIndex = usize;
#[derive(Debug)]
struct RefCount(ptr::NonNull<AtomicUsize>);
impl RefCount {
const MAX: usize = 1 << 24;
fn load(&self) -> usize {
unsafe { self.0.as_ref() }.load(Ordering::Acquire)
}
}
impl Clone for RefCount {
fn clone(&self) -> Self {
let old_size = unsafe { self.0.as_ref() }.fetch_add(1, Ordering::Relaxed);
assert!(old_size < Self::MAX);
RefCount(self.0)
}
}
impl Drop for RefCount {
fn drop(&mut self) {
if unsafe { self.0.as_ref() }.fetch_sub(1, Ordering::Relaxed) == 1 {
let _ = unsafe { Box::from_raw(self.0.as_ptr()) };
}
}
}
struct LifeGuard {
ref_count: RefCount,
submission_index: AtomicUsize,
}
//TODO: reconsider this
unsafe impl Send for LifeGuard {}
unsafe impl Sync for LifeGuard {}
impl LifeGuard {
fn new() -> Self {
let bx = Box::new(AtomicUsize::new(1));
LifeGuard {
ref_count: RefCount(ptr::NonNull::new(Box::into_raw(bx)).unwrap()),
submission_index: AtomicUsize::new(0),
}
}
}
#[derive(Debug)]
struct Stored<T> {
value: T,
ref_count: RefCount,
}
unsafe impl<T> Send for Stored<T> {} unsafe impl<T> Send for Stored<T> {}
unsafe impl<T> Sync for Stored<T> {}
#[derive(Debug, Hash, PartialEq, Eq)]
struct WeaklyStored<T>(T);
unsafe impl<T> Send for WeaklyStored<T> {}
unsafe impl<T> Sync for WeaklyStored<T> {}
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]

View File

@ -1,5 +1,5 @@
use { use {
Extent3d, Stored, Extent3d, LifeGuard, Stored,
DeviceId, TextureId, DeviceId, TextureId,
}; };
@ -32,6 +32,7 @@ pub(crate) struct Buffer<B: hal::Backend> {
//pub raw: B::UnboundBuffer, //pub raw: B::UnboundBuffer,
pub raw: B::Buffer, pub raw: B::Buffer,
pub memory_properties: hal::memory::Properties, pub memory_properties: hal::memory::Properties,
pub life_guard: LifeGuard,
// TODO: mapping, unmap() // TODO: mapping, unmap()
} }
@ -82,6 +83,7 @@ pub(crate) struct Texture<B: hal::Backend> {
pub kind: hal::image::Kind, pub kind: hal::image::Kind,
pub format: TextureFormat, pub format: TextureFormat,
pub full_range: hal::image::SubresourceRange, pub full_range: hal::image::SubresourceRange,
pub life_guard: LifeGuard,
} }
@ -122,6 +124,7 @@ pub(crate) struct TextureView<B: hal::Backend> {
pub format: TextureFormat, pub format: TextureFormat,
pub extent: hal::image::Extent, pub extent: hal::image::Extent,
pub samples: hal::image::NumSamples, pub samples: hal::image::NumSamples,
pub life_guard: LifeGuard,
} }

View File

@ -1,8 +1,9 @@
use {Stored, BufferId, TextureId}; use {RefCount, Stored, WeaklyStored, BufferId, TextureId};
use resource::{BufferUsageFlags, TextureUsageFlags}; use resource::{BufferUsageFlags, TextureUsageFlags};
use std::collections::hash_map::{Entry, HashMap}; use std::collections::hash_map::{Entry, HashMap};
use std::hash::Hash; use std::hash::Hash;
use std::mem;
use std::ops::{BitOr, Range}; use std::ops::{BitOr, Range};
@ -42,10 +43,19 @@ impl GenericUsage for TextureUsageFlags {
} }
} }
#[derive(Clone)]
struct Track<U> {
ref_count: RefCount,
init: U,
last: U,
}
unsafe impl<U> Send for Track<U> {}
unsafe impl<U> Sync for Track<U> {}
//TODO: consider having `I` as an associated type of `U`? //TODO: consider having `I` as an associated type of `U`?
pub struct Tracker<I, U> { pub struct Tracker<I, U> {
map: HashMap<Stored<I>, Range<U>>, map: HashMap<WeaklyStored<I>, Track<U>>,
} }
pub type BufferTracker = Tracker<BufferId, BufferUsageFlags>; pub type BufferTracker = Tracker<BufferId, BufferUsageFlags>;
pub type TextureTracker = Tracker<TextureId, TextureUsageFlags>; pub type TextureTracker = Tracker<TextureId, TextureUsageFlags>;
@ -54,16 +64,23 @@ impl<
I: Clone + Hash + Eq, I: Clone + Hash + Eq,
U: Copy + GenericUsage + BitOr<Output = U> + PartialEq, U: Copy + GenericUsage + BitOr<Output = U> + PartialEq,
> Tracker<I, U> { > Tracker<I, U> {
pub fn new() -> Self { pub(crate) fn new() -> Self {
Tracker { Tracker {
map: HashMap::new(), map: HashMap::new(),
} }
} }
pub fn query(&mut self, id: I, default: U) -> Query<U> { /// Get the last usage on a resource.
match self.map.entry(Stored(id)) { pub(crate) fn query(
&mut self, stored: &Stored<I>, default: U
) -> Query<U> {
match self.map.entry(WeaklyStored(stored.value.clone())) {
Entry::Vacant(e) => { Entry::Vacant(e) => {
e.insert(default .. default); e.insert(Track {
ref_count: stored.ref_count.clone(),
init: default,
last: default,
});
Query { Query {
usage: default, usage: default,
initialized: true, initialized: true,
@ -71,28 +88,35 @@ impl<
} }
Entry::Occupied(e) => { Entry::Occupied(e) => {
Query { Query {
usage: e.get().end, usage: e.get().last,
initialized: false, initialized: false,
} }
} }
} }
} }
pub fn transit(&mut self, id: I, usage: U, permit: TrackPermit) -> Result<Tracktion<U>, U> { /// Transit a specified resource into a different usage.
match self.map.entry(Stored(id)) { pub(crate) fn transit(
&mut self, id: I, ref_count: &RefCount, usage: U, permit: TrackPermit
) -> Result<Tracktion<U>, U> {
match self.map.entry(WeaklyStored(id)) {
Entry::Vacant(e) => { Entry::Vacant(e) => {
e.insert(usage .. usage); e.insert(Track {
ref_count: ref_count.clone(),
init: usage,
last: usage,
});
Ok(Tracktion::Init) Ok(Tracktion::Init)
} }
Entry::Occupied(mut e) => { Entry::Occupied(mut e) => {
let old = e.get().end; let old = e.get().last;
if usage == old { if usage == old {
Ok(Tracktion::Keep) Ok(Tracktion::Keep)
} else if permit.contains(TrackPermit::EXTEND) && !(old | usage).is_exclusive() { } else if permit.contains(TrackPermit::EXTEND) && !(old | usage).is_exclusive() {
e.get_mut().end = old | usage; e.get_mut().last = old | usage;
Ok(Tracktion::Extend { old }) Ok(Tracktion::Extend { old })
} else if permit.contains(TrackPermit::REPLACE) { } else if permit.contains(TrackPermit::REPLACE) {
e.get_mut().end = usage; e.get_mut().last = usage;
Ok(Tracktion::Replace { old }) Ok(Tracktion::Replace { old })
} else { } else {
Err(old) Err(old)
@ -101,15 +125,30 @@ impl<
} }
} }
pub fn consume<'a>(&'a mut self, other: &'a Self) -> impl 'a + Iterator<Item = (I, Range<U>)> { /// Consume another tacker, adding it's transitions to `self`.
pub fn consume<'a>(
&'a mut self, other: &'a Self
) -> impl 'a + Iterator<Item = (I, Range<U>)> {
other.map other.map
.iter() .iter()
.flat_map(move |(id, new)| match self.transit(id.0.clone(), new.end, TrackPermit::REPLACE) { .flat_map(move |(id, new)| match self.map.entry(WeaklyStored(id.0.clone())) {
Ok(Tracktion::Init) | Entry::Vacant(e) => {
Ok(Tracktion::Keep) => None, e.insert(new.clone());
Ok(Tracktion::Replace { old }) => Some((id.0.clone(), old .. new.end)), None
Ok(Tracktion::Extend { .. }) | }
Err(_) => panic!("Unable to consume a resource transition!"), Entry::Occupied(mut e) => {
let old = mem::replace(&mut e.get_mut().last, new.last);
if old == new.init {
None
} else {
Some((id.0.clone(), old .. new.last))
}
}
}) })
} }
/// Return an iterator over used resources keys.
pub fn used<'a>(&'a self) -> impl 'a + Iterator<Item = I> {
self.map.keys().map(|&WeaklyStored(ref id)| id.clone())
}
} }