Add reference counting to objects

This commit is contained in:
Dzmitry Malyshau 2018-11-02 16:03:24 -04:00
parent c1eb437b25
commit 39186c9c7b
8 changed files with 252 additions and 55 deletions

View File

@ -1,5 +1,5 @@
use super::CommandBuffer;
use {DeviceId, Stored};
use {DeviceId, LifeGuard, Stored};
use track::{Tracker};
use hal::command::RawCommandBuffer;
@ -61,8 +61,8 @@ impl<B: hal::Backend> CommandAllocator<B> {
}
}
pub fn allocate(
&self, device_id: DeviceId, device: &B::Device
pub(crate) fn allocate(
&self, device_id: Stored<DeviceId>, device: &B::Device
) -> CommandBuffer<B> {
let thread_id = thread::current().id();
let mut inner = self.inner.lock().unwrap();
@ -90,7 +90,8 @@ impl<B: hal::Backend> CommandAllocator<B> {
raw: vec![init],
fence,
recorded_thread_id: thread_id,
device_id: Stored(device_id),
device_id,
life_guard: LifeGuard::new(),
buffer_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> {
pub fn new(raw: B::CommandBuffer, cmb_id: CommandBufferId) -> Self {
pub(crate) fn new(raw: B::CommandBuffer, cmb_id: Stored<CommandBufferId>) -> Self {
ComputePass {
raw,
cmb_id: Stored(cmb_id),
cmb_id,
}
}
}
@ -34,10 +34,10 @@ pub extern "C" fn wgpu_compute_pass_end_pass(
HUB.command_buffers
.lock()
.get_mut(pass.cmb_id.0)
.get_mut(pass.cmb_id.value)
.raw
.push(pass.raw);
pass.cmb_id.0
pass.cmb_id.value
}
pub extern "C" fn wgpu_compute_pass_set_bind_group(

View File

@ -2,7 +2,7 @@ mod allocator;
mod compute;
mod render;
pub use self::allocator::CommandAllocator;
pub(crate) use self::allocator::CommandAllocator;
pub use self::compute::*;
pub use self::render::*;
@ -10,7 +10,7 @@ use hal::{self, Device};
use hal::command::RawCommandBuffer;
use {
B, Color, Origin3d, Stored, BufferUsageFlags, TextureUsageFlags,
B, Color, LifeGuard, Origin3d, Stored, BufferUsageFlags, TextureUsageFlags, WeaklyStored,
BufferId, CommandBufferId, ComputePassId, DeviceId, RenderPassId, TextureId, TextureViewId,
};
use conv;
@ -83,6 +83,7 @@ pub struct CommandBuffer<B: hal::Backend> {
fence: B::Fence,
recorded_thread_id: ThreadId,
device_id: Stored<DeviceId>,
life_guard: LifeGuard,
pub(crate) buffer_tracker: BufferTracker,
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 cmb = cmb_guard.get_mut(command_buffer_id);
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 mut current_comb = device.com_allocator.extend(cmb);
@ -160,7 +161,7 @@ pub extern "C" fn wgpu_command_buffer_begin_render_pass(
} else {
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::DEPTH | hal::format::Aspects::STENCIL,
@ -185,7 +186,7 @@ pub extern "C" fn wgpu_command_buffer_begin_render_pass(
} else {
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);
hal::pass::Attachment {
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 {
attachments: desc.color_attachments
.iter()
.map(|at| Stored(at.attachment))
.chain(desc.depth_stencil_attachment.as_ref().map(|at| Stored(at.attachment)))
.map(|at| WeaklyStored(at.attachment))
.chain(desc.depth_stencil_attachment.as_ref().map(|at| WeaklyStored(at.attachment)))
.collect(),
};
let framebuffer = match framebuffer_cache.entry(fb_key) {
@ -246,7 +247,7 @@ pub extern "C" fn wgpu_command_buffer_begin_render_pass(
.key()
.attachments
.iter()
.map(|&Stored(id)| &view_guard.get(id).raw);
.map(|&WeaklyStored(id)| &view_guard.get(id).raw);
device.raw
.create_framebuffer(&render_pass, attachments, extent.unwrap())
@ -289,7 +290,10 @@ pub extern "C" fn wgpu_command_buffer_begin_render_pass(
.lock()
.register(RenderPass::new(
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 raw = cmb.raw.pop().unwrap();
let stored = Stored {
value: command_buffer_id,
ref_count: cmb.life_guard.ref_count.clone(),
};
HUB.compute_passes
.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> {
pub fn new(
pub(crate) fn new(
raw: B::CommandBuffer,
cmb_id: CommandBufferId,
cmb_id: Stored<CommandBufferId>,
) -> Self {
RenderPass {
raw,
cmb_id: Stored(cmb_id),
cmb_id,
buffer_tracker: BufferTracker::new(),
texture_tracker: TextureTracker::new(),
}
@ -40,7 +40,7 @@ pub extern "C" fn wgpu_render_pass_end_pass(
pass.raw.end_render_pass();
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() {
CommandBuffer::insert_barriers(
@ -52,5 +52,5 @@ pub extern "C" fn wgpu_render_pass_end_pass(
}
cmb.raw.push(pass.raw);
pass.cmb_id.0
pass.cmb_id.value
}

View File

@ -2,8 +2,9 @@ use {back, binding_model, command, conv, pipeline, resource};
use registry::{HUB, Items, Registry};
use track::{BufferTracker, TextureTracker};
use {
CommandBuffer, Stored, TextureUsageFlags,
BindGroupLayoutId, BlendStateId, CommandBufferId, DepthStencilStateId,
CommandBuffer, LifeGuard, RefCount, Stored, SubmissionIndex, WeaklyStored,
TextureUsageFlags,
BindGroupLayoutId, BlendStateId, BufferId, CommandBufferId, DepthStencilStateId,
DeviceId, PipelineLayoutId, QueueId, RenderPipelineId, ShaderModuleId,
TextureId, TextureViewId,
};
@ -16,6 +17,7 @@ use rendy_memory::{allocator, Config, Heaps};
use std::{ffi, slice};
use std::collections::hash_map::{Entry, HashMap};
use std::sync::Mutex;
use std::sync::atomic::Ordering;
#[derive(Hash, PartialEq)]
@ -26,21 +28,67 @@ impl Eq for RenderPassKey {}
#[derive(Hash, PartialEq)]
pub(crate) struct FramebufferKey {
pub attachments: Vec<Stored<TextureViewId>>,
pub attachments: Vec<WeaklyStored<TextureViewId>>,
}
impl Eq for FramebufferKey {}
enum Resource {
Buffer(BufferId),
Texture(TextureId),
}
struct ActiveFrame<B: hal::Backend> {
submission_index: SubmissionIndex,
fence: B::Fence,
resources: Vec<Resource>,
}
struct DestroyedResources<B: hal::Backend> {
/// Resources that are destroyed by the user but still referenced by
/// other objects or command buffers.
referenced: Vec<(Resource, 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>,
}
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: Resource, life_guard: &LifeGuard) {
if life_guard.ref_count.load() == 1 {
let sub_index = life_guard.submission_index.load(Ordering::Acquire);
match self.active.iter_mut().find(|af| af.submission_index == sub_index) {
Some(frame) => {
frame.resources.push(resource);
}
None => {
self.free.push(resource);
}
}
} else {
self.referenced.push((resource, life_guard.ref_count.clone()));
}
}
}
pub struct Device<B: hal::Backend> {
pub(crate) raw: B::Device,
queue_group: hal::QueueGroup<B, hal::General>,
mem_allocator: Heaps<B::Memory>,
pub(crate) com_allocator: command::CommandAllocator<B>,
life_guard: LifeGuard,
buffer_tracker: Mutex<BufferTracker>,
texture_tracker: Mutex<TextureTracker>,
mem_props: hal::MemoryProperties,
pub(crate) render_passes: Mutex<HashMap<RenderPassKey, B::RenderPass>>,
pub(crate) framebuffers: Mutex<HashMap<FramebufferKey, B::Framebuffer>>,
last_submission_index: SubmissionIndex,
destroyed: Mutex<DestroyedResources<B>>,
}
impl<B: hal::Backend> Device<B> {
@ -75,11 +123,18 @@ impl<B: hal::Backend> Device<B> {
mem_allocator,
com_allocator: command::CommandAllocator::new(queue_group.family()),
queue_group,
life_guard: LifeGuard::new(),
buffer_tracker: Mutex::new(BufferTracker::new()),
texture_tracker: Mutex::new(TextureTracker::new()),
mem_props,
render_passes: 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 +191,28 @@ pub extern "C" fn wgpu_device_create_texture(
layers: 0 .. 1, //TODO
};
let life_guard = LifeGuard::new();
let ref_count = life_guard.ref_count.clone();
let id = HUB.textures
.lock()
.register(resource::Texture {
raw: bound_image,
device_id: Stored(device_id),
device_id: Stored {
value: device_id,
ref_count: device.life_guard.ref_count.clone(),
},
kind,
format: desc.format,
full_range,
life_guard,
});
let query = device.texture_tracker
.lock()
.unwrap()
.query(id, TextureUsageFlags::WRITE_ALL);
.query(
&Stored { value: id, ref_count },
TextureUsageFlags::WRITE_ALL,
);
assert!(query.initialized);
id
@ -164,7 +228,7 @@ pub extern "C" fn wgpu_texture_create_texture_view(
let raw = HUB.devices
.lock()
.get(texture.device_id.0)
.get(texture.device_id.value)
.raw
.create_image_view(
&texture.raw,
@ -183,10 +247,14 @@ pub extern "C" fn wgpu_texture_create_texture_view(
.lock()
.register(resource::TextureView {
raw,
texture_id: Stored(texture_id),
texture_id: Stored {
value: texture_id,
ref_count: texture.life_guard.ref_count.clone(),
},
format: texture.format,
extent: texture.kind.extent(),
samples: texture.kind.num_samples(),
life_guard: LifeGuard::new(),
})
}
@ -205,7 +273,7 @@ pub extern "C" fn wgpu_texture_create_default_texture_view(
let raw = HUB.devices
.lock()
.get(texture.device_id.0)
.get(texture.device_id.value)
.raw
.create_image_view(
&texture.raw,
@ -220,13 +288,38 @@ pub extern "C" fn wgpu_texture_create_default_texture_view(
.lock()
.register(resource::TextureView {
raw,
texture_id: Stored(texture_id),
texture_id: Stored {
value: texture_id,
ref_count: texture.life_guard.ref_count.clone(),
},
format: texture.format,
extent: texture.kind.extent(),
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(Resource::Texture(texture_id), &texture.life_guard);
}
#[no_mangle]
pub extern "C" fn wgpu_texture_view_destroy(
_texture_view_id: TextureViewId,
) {
unimplemented!()
}
#[no_mangle]
pub extern "C" fn wgpu_device_create_bind_group_layout(
@ -338,7 +431,11 @@ pub extern "C" fn wgpu_device_create_command_buffer(
let device_guard = HUB.devices.lock();
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(
hal::command::CommandBufferFlags::ONE_TIME_SUBMIT,
hal::command::CommandBufferInheritanceInfo::default(),

View File

@ -43,12 +43,78 @@ pub use self::resource::*;
use back::Backend as B;
use registry::Id;
#[derive(Debug, Hash, PartialEq, Eq)]
struct Stored<T>(T);
#[cfg(not(feature = "remote"))]
unsafe impl<T> Sync for Stored<T> {}
#[cfg(not(feature = "remote"))]
use std::ptr;
use std::sync::atomic::{AtomicUsize, Ordering};
//#[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> 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)]
#[derive(Clone, Copy, Debug)]

View File

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

View File

@ -1,4 +1,4 @@
use {Stored, BufferId, TextureId};
use {RefCount, Stored, WeaklyStored, BufferId, TextureId};
use resource::{BufferUsageFlags, TextureUsageFlags};
use std::collections::hash_map::{Entry, HashMap};
@ -42,10 +42,18 @@ impl GenericUsage for TextureUsageFlags {
}
}
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`?
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 TextureTracker = Tracker<TextureId, TextureUsageFlags>;
@ -54,16 +62,22 @@ impl<
I: Clone + Hash + Eq,
U: Copy + GenericUsage + BitOr<Output = U> + PartialEq,
> Tracker<I, U> {
pub fn new() -> Self {
pub(crate) fn new() -> Self {
Tracker {
map: HashMap::new(),
}
}
pub fn query(&mut self, id: I, default: U) -> Query<U> {
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) => {
e.insert(default .. default);
e.insert(Track {
ref_count: stored.ref_count.clone(),
init: default,
last: default,
});
Query {
usage: default,
initialized: true,
@ -71,28 +85,34 @@ impl<
}
Entry::Occupied(e) => {
Query {
usage: e.get().end,
usage: e.get().last,
initialized: false,
}
}
}
}
pub fn transit(&mut self, id: I, usage: U, permit: TrackPermit) -> Result<Tracktion<U>, U> {
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) => {
e.insert(usage .. usage);
e.insert(Track {
ref_count: ref_count.clone(),
init: usage,
last: usage,
});
Ok(Tracktion::Init)
}
Entry::Occupied(mut e) => {
let old = e.get().end;
let old = e.get().last;
if usage == old {
Ok(Tracktion::Keep)
} 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 })
} else if permit.contains(TrackPermit::REPLACE) {
e.get_mut().end = usage;
e.get_mut().last = usage;
Ok(Tracktion::Replace { old })
} else {
Err(old)
@ -101,13 +121,15 @@ impl<
}
}
pub fn consume<'a>(&'a mut self, other: &'a Self) -> impl 'a + Iterator<Item = (I, Range<U>)> {
pub fn consume<'a>(
&'a mut self, other: &'a Self
) -> impl 'a + Iterator<Item = (I, Range<U>)> {
other.map
.iter()
.flat_map(move |(id, new)| match self.transit(id.0.clone(), new.end, TrackPermit::REPLACE) {
.flat_map(move |(id, new)| match self.transit(id.0.clone(), &new.ref_count, new.last, TrackPermit::REPLACE) {
Ok(Tracktion::Init) |
Ok(Tracktion::Keep) => None,
Ok(Tracktion::Replace { old }) => Some((id.0.clone(), old .. new.end)),
Ok(Tracktion::Replace { old }) => Some((id.0.clone(), old .. new.last)),
Ok(Tracktion::Extend { .. }) |
Err(_) => panic!("Unable to consume a resource transition!"),
})