use smallvec::SmallVec;
use std::error;
use std::fmt;
use std::marker::PhantomData;
use std::ptr;
use command_buffer::sys::UnsafeCommandBuffer;
use device::Queue;
use sync::Fence;
use sync::PipelineStages;
use sync::Semaphore;
use Error;
use OomError;
use SynchronizedVulkanObject;
use VulkanObject;
use check_errors;
use vk;
#[derive(Debug)]
pub struct SubmitCommandBufferBuilder<'a> {
wait_semaphores: SmallVec<[vk::Semaphore; 16]>,
destination_stages: SmallVec<[vk::PipelineStageFlags; 8]>,
signal_semaphores: SmallVec<[vk::Semaphore; 16]>,
command_buffers: SmallVec<[vk::CommandBuffer; 4]>,
fence: vk::Fence,
marker: PhantomData<&'a ()>,
}
impl<'a> SubmitCommandBufferBuilder<'a> {
#[inline]
pub fn new() -> SubmitCommandBufferBuilder<'a> {
SubmitCommandBufferBuilder {
wait_semaphores: SmallVec::new(),
destination_stages: SmallVec::new(),
signal_semaphores: SmallVec::new(),
command_buffers: SmallVec::new(),
fence: 0,
marker: PhantomData,
}
}
#[inline]
pub fn has_fence(&self) -> bool {
self.fence != 0
}
#[inline]
pub unsafe fn set_fence_signal(&mut self, fence: &'a Fence) {
self.fence = fence.internal_object();
}
#[inline]
pub unsafe fn add_wait_semaphore(&mut self, semaphore: &'a Semaphore, stages: PipelineStages) {
debug_assert!(stages.into_vulkan_bits() != 0);
self.wait_semaphores.push(semaphore.internal_object());
self.destination_stages.push(stages.into_vulkan_bits());
}
#[inline]
pub unsafe fn add_command_buffer<P>(&mut self, command_buffer: &'a UnsafeCommandBuffer<P>) {
self.command_buffers.push(command_buffer.internal_object());
}
#[inline]
pub fn num_signal_semaphores(&self) -> usize {
self.signal_semaphores.len()
}
#[inline]
pub unsafe fn add_signal_semaphore(&mut self, semaphore: &'a Semaphore) {
self.signal_semaphores.push(semaphore.internal_object());
}
pub fn submit(self, queue: &Queue) -> Result<(), SubmitCommandBufferError> {
unsafe {
let vk = queue.device().pointers();
let queue = queue.internal_object_guard();
debug_assert_eq!(self.wait_semaphores.len(), self.destination_stages.len());
let batch = vk::SubmitInfo {
sType: vk::STRUCTURE_TYPE_SUBMIT_INFO,
pNext: ptr::null(),
waitSemaphoreCount: self.wait_semaphores.len() as u32,
pWaitSemaphores: self.wait_semaphores.as_ptr(),
pWaitDstStageMask: self.destination_stages.as_ptr(),
commandBufferCount: self.command_buffers.len() as u32,
pCommandBuffers: self.command_buffers.as_ptr(),
signalSemaphoreCount: self.signal_semaphores.len() as u32,
pSignalSemaphores: self.signal_semaphores.as_ptr(),
};
check_errors(vk.QueueSubmit(*queue, 1, &batch, self.fence))?;
Ok(())
}
}
pub fn merge(mut self, other: Self) -> Self {
assert!(self.fence == 0 || other.fence == 0,
"Can't merge two queue submits that both have a fence");
self.wait_semaphores.extend(other.wait_semaphores);
self.destination_stages.extend(other.destination_stages); self.signal_semaphores.extend(other.signal_semaphores);
self.command_buffers.extend(other.command_buffers);
if self.fence == 0 {
self.fence = other.fence;
}
self
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum SubmitCommandBufferError {
OomError(OomError),
DeviceLost,
}
impl error::Error for SubmitCommandBufferError {
#[inline]
fn description(&self) -> &str {
match *self {
SubmitCommandBufferError::OomError(_) => "not enough memory",
SubmitCommandBufferError::DeviceLost => "the connection to the device has been lost",
}
}
#[inline]
fn cause(&self) -> Option<&error::Error> {
match *self {
SubmitCommandBufferError::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for SubmitCommandBufferError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self))
}
}
impl From<Error> for SubmitCommandBufferError {
#[inline]
fn from(err: Error) -> SubmitCommandBufferError {
match err {
err @ Error::OutOfHostMemory => SubmitCommandBufferError::OomError(OomError::from(err)),
err @ Error::OutOfDeviceMemory =>
SubmitCommandBufferError::OomError(OomError::from(err)),
Error::DeviceLost => SubmitCommandBufferError::DeviceLost,
_ => panic!("unexpected error: {:?}", err),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
use sync::Fence;
#[test]
fn empty_submit() {
let (device, queue) = gfx_dev_and_queue!();
let builder = SubmitCommandBufferBuilder::new();
builder.submit(&queue).unwrap();
}
#[test]
fn signal_fence() {
unsafe {
let (device, queue) = gfx_dev_and_queue!();
let fence = Fence::alloc(device.clone()).unwrap();
assert!(!fence.ready().unwrap());
let mut builder = SubmitCommandBufferBuilder::new();
builder.set_fence_signal(&fence);
builder.submit(&queue).unwrap();
fence.wait(Some(Duration::from_secs(5))).unwrap();
assert!(fence.ready().unwrap());
}
}
#[test]
fn has_fence() {
unsafe {
let (device, queue) = gfx_dev_and_queue!();
let fence = Fence::alloc(device.clone()).unwrap();
let mut builder = SubmitCommandBufferBuilder::new();
assert!(!builder.has_fence());
builder.set_fence_signal(&fence);
assert!(builder.has_fence());
}
}
#[test]
fn merge_both_have_fences() {
unsafe {
let (device, _) = gfx_dev_and_queue!();
let fence1 = Fence::alloc(device.clone()).unwrap();
let fence2 = Fence::alloc(device.clone()).unwrap();
let mut builder1 = SubmitCommandBufferBuilder::new();
builder1.set_fence_signal(&fence1);
let mut builder2 = SubmitCommandBufferBuilder::new();
builder2.set_fence_signal(&fence2);
assert_should_panic!("Can't merge two queue submits that both have a fence", {
let _ = builder1.merge(builder2);
});
}
}
}