Implement pool for fences

This commit is contained in:
Nicolas Koch 2017-07-13 17:36:42 +02:00
parent b4b672a221
commit 99729ad9a7
5 changed files with 117 additions and 27 deletions

View File

@ -314,7 +314,7 @@ mod tests {
unsafe {
let (device, queue) = gfx_dev_and_queue!();
let fence = Fence::new(device.clone()).unwrap();
let fence = Fence::alloc(device.clone()).unwrap();
assert!(!fence.ready().unwrap());
let mut builder = SubmitCommandBufferBuilder::new();
@ -331,7 +331,7 @@ mod tests {
unsafe {
let (device, queue) = gfx_dev_and_queue!();
let fence = Fence::new(device.clone()).unwrap();
let fence = Fence::alloc(device.clone()).unwrap();
let mut builder = SubmitCommandBufferBuilder::new();
assert!(!builder.has_fence());
@ -345,8 +345,8 @@ mod tests {
unsafe {
let (device, _) = gfx_dev_and_queue!();
let fence1 = Fence::new(device.clone()).unwrap();
let fence2 = Fence::new(device.clone()).unwrap();
let fence1 = Fence::alloc(device.clone()).unwrap();
let fence2 = Fence::alloc(device.clone()).unwrap();
let mut builder1 = SubmitCommandBufferBuilder::new();
builder1.set_fence_signal(&fence1);

View File

@ -134,6 +134,7 @@ pub struct Device {
features: Features,
extensions: DeviceExtensions,
allocation_count: Mutex<u32>,
fence_pool: Mutex<Vec<vk::Fence>>,
}
// The `StandardCommandPool` type doesn't implement Send/Sync, so we have to manually reimplement
@ -307,6 +308,7 @@ impl Device {
},
extensions: (&extensions).into(),
allocation_count: Mutex::new(0),
fence_pool: Mutex::new(Vec::new()),
});
// Iterator for the produced queues.
@ -431,6 +433,10 @@ impl Device {
pub(crate) fn allocation_count(&self) -> &Mutex<u32> {
&self.allocation_count
}
pub(crate) fn fence_pool(&self) -> &Mutex<Vec<vk::Fence>> {
&self.fence_pool
}
}
impl fmt::Debug for Device {
@ -453,6 +459,9 @@ impl Drop for Device {
#[inline]
fn drop(&mut self) {
unsafe {
for &raw_fence in self.fence_pool.lock().unwrap().iter() {
self.vk.DestroyFence(self.device, raw_fence, ptr::null());
}
self.vk.DeviceWaitIdle(self.device);
self.vk.DestroyDevice(self.device, ptr::null());
}

View File

@ -68,7 +68,7 @@ use vk;
pub fn acquire_next_image(swapchain: Arc<Swapchain>, timeout: Option<Duration>)
-> Result<(usize, SwapchainAcquireFuture), AcquireError> {
let semaphore = Semaphore::new(swapchain.device.clone())?; // TODO: take from a pool
let fence = Fence::new(swapchain.device.clone())?; // TODO: take from a pool
let fence = Fence::from_pool(swapchain.device.clone())?;
// TODO: propagate `suboptimal` to the user
let AcquiredImage { id, suboptimal } = {

View File

@ -44,24 +44,76 @@ pub struct Fence<D = Arc<Device>>
// This variable exists so that we don't need to call `vkGetFenceStatus` or `vkWaitForFences`
// multiple times.
signaled: AtomicBool,
// Indicates whether this fence was taken from the fence pool.
// If true, will be put back into fence pool on drop.
from_pool: bool,
}
impl<D> Fence<D>
where D: SafeDeref<Target = Device>
{
/// Takes a fence from the vulkano-provided fence pool.
/// If the pool is empty, a new fence will be allocated.
/// Upon `drop`, the fence is put back into the pool.
///
/// For most applications, using the fence pool should be preferred,
/// in order to avoid creating new fences every frame.
pub fn from_pool(device: D) -> Result<Fence<D>, OomError> {
let raw_fence = device.fence_pool().lock().expect("Poisoned mutex").pop();
match raw_fence {
Some(raw_fence) => {
unsafe { Ok(Fence::from_raw(device, raw_fence, true)) }
},
None => {
// Pool is empty, alloc new fence
Fence::alloc_impl(device, false, true)
},
}
}
/// Unsafety: raw_fence must have been originally allocated from device.
unsafe fn from_raw(device: D, raw_fence: vk::Fence, from_pool: bool) -> Fence<D> {
{
// Make sure the fence isn't signaled
let vk = device.pointers();
vk.ResetFences(device.internal_object(), 1, &raw_fence);
}
Fence {
fence: raw_fence,
device: device,
signaled: AtomicBool::new(false),
from_pool: from_pool,
}
}
/// Builds a new fence.
#[deprecated(note = "use `Fence::alloc` instead")]
#[inline]
pub fn new(device: D) -> Result<Fence<D>, OomError> {
Fence::new_impl(device, false)
Fence::alloc(device)
}
/// See the docs of signaled().
/// Builds a new fence.
#[inline]
pub fn alloc(device: D) -> Result<Fence<D>, OomError> {
Fence::alloc_impl(device, false, false)
}
/// See the docs of `alloc_signaled()`.
#[deprecated(note = "use `Fence::alloc_signaled` instead")]
#[inline]
pub fn signaled(device: D) -> Result<Fence<D>, OomError> {
Fence::new_impl(device, true)
Fence::alloc_signaled(device)
}
fn new_impl(device: D, signaled: bool) -> Result<Fence<D>, OomError> {
/// Builds a new fence in signaled state.
#[inline]
pub fn alloc_signaled(device: D) -> Result<Fence<D>, OomError> {
Fence::alloc_impl(device, true, false)
}
fn alloc_impl(device: D, signaled: bool, from_pool: bool) -> Result<Fence<D>, OomError> {
let fence = unsafe {
let infos = vk::FenceCreateInfo {
sType: vk::STRUCTURE_TYPE_FENCE_CREATE_INFO,
@ -86,6 +138,7 @@ impl<D> Fence<D>
fence: fence,
device: device,
signaled: AtomicBool::new(signaled),
from_pool: from_pool,
})
}
@ -283,8 +336,17 @@ impl<D> Drop for Fence<D>
#[inline]
fn drop(&mut self) {
unsafe {
let vk = self.device.pointers();
vk.DestroyFence(self.device.internal_object(), self.fence, ptr::null());
if self.from_pool {
let raw_fence = self.fence;
self.device
.fence_pool()
.lock()
.expect("Poisoned mutex")
.push(raw_fence);
} else {
let vk = self.device.pointers();
vk.DestroyFence(self.device.internal_object(), self.fence, ptr::null());
}
}
}
}
@ -344,12 +406,13 @@ impl From<Error> for FenceWaitError {
mod tests {
use std::time::Duration;
use sync::Fence;
use VulkanObject;
#[test]
fn fence_create() {
let (device, _) = gfx_dev_and_queue!();
let fence = Fence::new(device.clone()).unwrap();
let fence = Fence::alloc(device.clone()).unwrap();
assert!(!fence.ready().unwrap());
}
@ -357,7 +420,7 @@ mod tests {
fn fence_create_signaled() {
let (device, _) = gfx_dev_and_queue!();
let fence = Fence::signaled(device.clone()).unwrap();
let fence = Fence::alloc_signaled(device.clone()).unwrap();
assert!(fence.ready().unwrap());
}
@ -365,7 +428,7 @@ mod tests {
fn fence_signaled_wait() {
let (device, _) = gfx_dev_and_queue!();
let fence = Fence::signaled(device.clone()).unwrap();
let fence = Fence::alloc_signaled(device.clone()).unwrap();
fence.wait(Some(Duration::new(0, 10))).unwrap();
}
@ -373,7 +436,7 @@ mod tests {
fn fence_reset() {
let (device, _) = gfx_dev_and_queue!();
let mut fence = Fence::signaled(device.clone()).unwrap();
let mut fence = Fence::alloc_signaled(device.clone()).unwrap();
fence.reset();
assert!(!fence.ready().unwrap());
}
@ -385,13 +448,13 @@ mod tests {
assert_should_panic!("Tried to wait for multiple fences that didn't belong \
to the same device",
{
let fence1 = Fence::signaled(device1.clone()).unwrap();
let fence2 = Fence::signaled(device2.clone()).unwrap();
{
let fence1 = Fence::alloc_signaled(device1.clone()).unwrap();
let fence2 = Fence::alloc_signaled(device2.clone()).unwrap();
let _ = Fence::multi_wait([&fence1, &fence2].iter().cloned(),
Some(Duration::new(0, 10)));
});
let _ = Fence::multi_wait([&fence1, &fence2].iter().cloned(),
Some(Duration::new(0, 10)));
});
}
#[test]
@ -403,11 +466,29 @@ mod tests {
assert_should_panic!("Tried to reset multiple fences that didn't belong \
to the same device",
{
let mut fence1 = Fence::signaled(device1.clone()).unwrap();
let mut fence2 = Fence::signaled(device2.clone()).unwrap();
{
let mut fence1 = Fence::alloc_signaled(device1.clone()).unwrap();
let mut fence2 = Fence::alloc_signaled(device2.clone()).unwrap();
let _ = Fence::multi_reset(once(&mut fence1).chain(once(&mut fence2)));
});
let _ = Fence::multi_reset(once(&mut fence1)
.chain(once(&mut fence2)));
});
}
#[test]
fn fence_pool() {
let (device, _) = gfx_dev_and_queue!();
assert_eq!(device.fence_pool().lock().unwrap().len(), 0);
let fence1_internal_obj = {
let fence = Fence::from_pool(device.clone()).unwrap();
assert_eq!(device.fence_pool().lock().unwrap().len(), 0);
fence.internal_object()
};
assert_eq!(device.fence_pool().lock().unwrap().len(), 1);
let fence2 = Fence::from_pool(device.clone()).unwrap();
assert_eq!(device.fence_pool().lock().unwrap().len(), 0);
assert_eq!(fence2.internal_object(), fence1_internal_obj);
}
}

View File

@ -37,7 +37,7 @@ pub fn then_signal_fence<F>(future: F, behavior: FenceSignalFutureBehavior) -> F
assert!(future.queue().is_some()); // TODO: document
let fence = Fence::new(device.clone()).unwrap();
let fence = Fence::from_pool(device.clone()).unwrap();
FenceSignalFuture {
device: device,
state: Mutex::new(FenceSignalFutureState::Pending(future, fence)),