use std::error;
use std::fmt;
use std::mem;
use std::sync::Arc;
use buffer::BufferAccess;
use buffer::TypedBufferAccess;
use command_buffer::commands_raw::CmdBindDescriptorSets;
use command_buffer::commands_raw::CmdBindDescriptorSetsError;
use command_buffer::commands_raw::CmdBindPipeline;
use command_buffer::commands_raw::CmdPushConstants;
use command_buffer::commands_raw::CmdPushConstantsError;
use command_buffer::CommandAddError;
use command_buffer::DispatchIndirectCommand;
use command_buffer::RawCommandBufferPrototype;
use command_buffer::CommandsList;
use command_buffer::CommandsListSink;
use descriptor::PipelineLayoutAbstract;
use descriptor::descriptor_set::collection::TrackedDescriptorSetsCollection;
use device::DeviceOwned;
use pipeline::ComputePipeline;
use sync::AccessFlagBits;
use sync::PipelineStages;
use VulkanObject;
use VulkanPointers;
use vk;
pub struct CmdDispatchIndirect<L, B, Pl, S, Pc>
where L: CommandsList, Pl: PipelineLayoutAbstract, S: TrackedDescriptorSetsCollection
{
previous: CmdPushConstants<
CmdBindDescriptorSets<
CmdBindPipeline<L, Arc<ComputePipeline<Pl>>>,
S, Arc<ComputePipeline<Pl>>
>,
Pc, Arc<ComputePipeline<Pl>>
>,
raw_buffer: vk::Buffer,
raw_offset: vk::DeviceSize,
buffer: B,
}
impl<L, B, Pl, S, Pc> CmdDispatchIndirect<L, B, Pl, S, Pc>
where L: CommandsList, Pl: PipelineLayoutAbstract, S: TrackedDescriptorSetsCollection
{
pub unsafe fn new(previous: L, pipeline: Arc<ComputePipeline<Pl>>, sets: S, push_constants: Pc,
buffer: B)
-> Result<CmdDispatchIndirect<L, B, Pl, S, Pc>, CmdDispatchIndirectError>
where B: TypedBufferAccess<Content = DispatchIndirectCommand>
{
let previous = CmdBindPipeline::bind_compute_pipeline(previous, pipeline.clone());
let device = previous.device().clone();
let previous = CmdBindDescriptorSets::new(previous, false, pipeline.clone(), sets)?;
let previous = CmdPushConstants::new(previous, pipeline.clone(), push_constants)?;
let (raw_buffer, raw_offset) = {
let inner = buffer.inner();
if !inner.buffer.usage_indirect_buffer() {
return Err(CmdDispatchIndirectError::MissingBufferUsage);
}
if inner.offset % 4 != 0 {
return Err(CmdDispatchIndirectError::WrongAlignment);
}
(inner.buffer.internal_object(), inner.offset as vk::DeviceSize)
};
Ok(CmdDispatchIndirect {
previous: previous,
raw_buffer: raw_buffer,
raw_offset: raw_offset,
buffer: buffer,
})
}
}
unsafe impl<L, B, Pl, S, Pc> CommandsList for CmdDispatchIndirect<L, B, Pl, S, Pc>
where L: CommandsList, B: BufferAccess,
Pl: PipelineLayoutAbstract, S: TrackedDescriptorSetsCollection
{
#[inline]
fn append<'a>(&'a self, builder: &mut CommandsListSink<'a>) {
self.previous.append(builder);
{
let stages = PipelineStages { compute_shader: true, .. PipelineStages::none() };
let access = AccessFlagBits { indirect_command_read: true, .. AccessFlagBits::none() };
builder.add_buffer_transition(&self.buffer, 0,
mem::size_of::<DispatchIndirectCommand>(), false,
stages, access);
}
builder.add_command(Box::new(move |raw: &mut RawCommandBufferPrototype| {
unsafe {
let vk = raw.device.pointers();
let cmd = raw.command_buffer.clone().take().unwrap();
vk.CmdDispatchIndirect(cmd, self.raw_buffer, self.raw_offset);
}
}));
}
}
#[derive(Debug, Copy, Clone)]
pub enum CmdDispatchIndirectError {
MissingBufferUsage,
WrongAlignment,
BindDescriptorSetsError(CmdBindDescriptorSetsError),
PushConstantsError(CmdPushConstantsError),
}
impl From<CmdBindDescriptorSetsError> for CmdDispatchIndirectError {
#[inline]
fn from(err: CmdBindDescriptorSetsError) -> CmdDispatchIndirectError {
CmdDispatchIndirectError::BindDescriptorSetsError(err)
}
}
impl From<CmdPushConstantsError> for CmdDispatchIndirectError {
#[inline]
fn from(err: CmdPushConstantsError) -> CmdDispatchIndirectError {
CmdDispatchIndirectError::PushConstantsError(err)
}
}
impl error::Error for CmdDispatchIndirectError {
#[inline]
fn description(&self) -> &str {
match *self {
CmdDispatchIndirectError::MissingBufferUsage => {
"the buffer must have the indirect usage."
},
CmdDispatchIndirectError::WrongAlignment => {
"the buffer must be 4-bytes-aligned"
},
CmdDispatchIndirectError::BindDescriptorSetsError(_) => {
"error while binding descriptor sets"
},
CmdDispatchIndirectError::PushConstantsError(_) => {
"error while setting push constants"
},
}
}
#[inline]
fn cause(&self) -> Option<&error::Error> {
match *self {
CmdDispatchIndirectError::MissingBufferUsage => None,
CmdDispatchIndirectError::WrongAlignment => None,
CmdDispatchIndirectError::BindDescriptorSetsError(ref err) => Some(err),
CmdDispatchIndirectError::PushConstantsError(ref err) => Some(err),
}
}
}
impl fmt::Display for CmdDispatchIndirectError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self))
}
}