use smallvec::SmallVec;
use std::error;
use std::fmt;
use std::mem;
use std::ptr;
use std::sync::Arc;
use Error;
use OomError;
use VulkanObject;
use check_errors;
use vk;
use descriptor::descriptor::DescriptorDesc;
use descriptor::descriptor::ShaderStages;
use descriptor::descriptor_set::UnsafeDescriptorSetLayout;
use descriptor::pipeline_layout::PipelineLayoutAbstract;
use descriptor::pipeline_layout::PipelineLayoutDesc;
use descriptor::pipeline_layout::PipelineLayoutDescPcRange;
use descriptor::pipeline_layout::PipelineLayoutLimitsError;
use device::Device;
use device::DeviceOwned;
pub struct PipelineLayout<L> {
device: Arc<Device>,
layout: vk::PipelineLayout,
layouts: SmallVec<[Arc<UnsafeDescriptorSetLayout>; 16]>,
desc: L,
}
impl<L> PipelineLayout<L>
where L: PipelineLayoutDesc
{
#[inline]
pub fn new(device: Arc<Device>, desc: L)
-> Result<PipelineLayout<L>, PipelineLayoutCreationError> {
let vk = device.pointers();
desc.check_against_limits(&device)?;
let layouts = {
let mut layouts: SmallVec<[_; 16]> = SmallVec::new();
for num in 0 .. desc.num_sets() {
layouts.push(match desc.provided_set_layout(num) {
Some(l) => {
assert_eq!(l.device().internal_object(),
device.internal_object());
l
},
None => {
let sets_iter = 0 ..
desc.num_bindings_in_set(num).unwrap_or(0);
let desc_iter = sets_iter.map(|d| desc.descriptor(num, d));
Arc::new(UnsafeDescriptorSetLayout::new(device.clone(),
desc_iter)?)
},
});
}
layouts
};
let layouts_ids = layouts
.iter()
.map(|l| l.internal_object())
.collect::<SmallVec<[_; 16]>>();
let push_constants = {
let mut out: SmallVec<[_; 8]> = SmallVec::new();
for pc_id in 0 .. desc.num_push_constants_ranges() {
let PipelineLayoutDescPcRange {
offset,
size,
stages,
} = {
match desc.push_constants_range(pc_id) {
Some(o) => o,
None => continue,
}
};
if stages == ShaderStages::none() || size == 0 || (size % 4) != 0 {
return Err(PipelineLayoutCreationError::InvalidPushConstant);
}
out.push(vk::PushConstantRange {
stageFlags: stages.into_vulkan_bits(),
offset: offset as u32,
size: size as u32,
});
}
out
};
debug_assert!({
let mut stages = 0;
let mut outcome = true;
for pc in push_constants.iter() {
if (stages & pc.stageFlags) != 0 {
outcome = false;
break;
}
stages &= pc.stageFlags;
}
outcome
});
let layout = unsafe {
let infos = vk::PipelineLayoutCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
pNext: ptr::null(),
flags: 0, setLayoutCount: layouts_ids.len() as u32,
pSetLayouts: layouts_ids.as_ptr(),
pushConstantRangeCount: push_constants.len() as u32,
pPushConstantRanges: push_constants.as_ptr(),
};
let mut output = mem::uninitialized();
check_errors(vk.CreatePipelineLayout(device.internal_object(),
&infos,
ptr::null(),
&mut output))?;
output
};
Ok(PipelineLayout {
device: device.clone(),
layout: layout,
layouts: layouts,
desc: desc,
})
}
}
impl<L> PipelineLayout<L>
where L: PipelineLayoutDesc
{
#[inline]
pub fn desc(&self) -> &L {
&self.desc
}
}
unsafe impl<D> PipelineLayoutAbstract for PipelineLayout<D>
where D: PipelineLayoutDesc
{
#[inline]
fn sys(&self) -> PipelineLayoutSys {
PipelineLayoutSys(&self.layout)
}
#[inline]
fn descriptor_set_layout(&self, index: usize) -> Option<&Arc<UnsafeDescriptorSetLayout>> {
self.layouts.get(index)
}
}
unsafe impl<D> PipelineLayoutDesc for PipelineLayout<D>
where D: PipelineLayoutDesc
{
#[inline]
fn num_sets(&self) -> usize {
self.desc.num_sets()
}
#[inline]
fn num_bindings_in_set(&self, set: usize) -> Option<usize> {
self.desc.num_bindings_in_set(set)
}
#[inline]
fn descriptor(&self, set: usize, binding: usize) -> Option<DescriptorDesc> {
self.desc.descriptor(set, binding)
}
#[inline]
fn num_push_constants_ranges(&self) -> usize {
self.desc.num_push_constants_ranges()
}
#[inline]
fn push_constants_range(&self, num: usize) -> Option<PipelineLayoutDescPcRange> {
self.desc.push_constants_range(num)
}
}
unsafe impl<D> DeviceOwned for PipelineLayout<D> {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl<D> fmt::Debug for PipelineLayout<D>
where D: fmt::Debug
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt.debug_struct("PipelineLayout")
.field("raw", &self.layout)
.field("device", &self.device)
.field("desc", &self.desc)
.finish()
}
}
impl<L> Drop for PipelineLayout<L> {
#[inline]
fn drop(&mut self) {
unsafe {
let vk = self.device.pointers();
vk.DestroyPipelineLayout(self.device.internal_object(), self.layout, ptr::null());
}
}
}
#[derive(Copy, Clone)]
pub struct PipelineLayoutSys<'a>(&'a vk::PipelineLayout);
unsafe impl<'a> VulkanObject for PipelineLayoutSys<'a> {
type Object = vk::PipelineLayout;
#[inline]
fn internal_object(&self) -> vk::PipelineLayout {
*self.0
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PipelineLayoutCreationError {
OomError(OomError),
LimitsError(PipelineLayoutLimitsError),
InvalidPushConstant,
}
impl error::Error for PipelineLayoutCreationError {
#[inline]
fn description(&self) -> &str {
match *self {
PipelineLayoutCreationError::OomError(_) => {
"not enough memory available"
},
PipelineLayoutCreationError::LimitsError(_) => {
"the pipeline layout description doesn't fulfill the limit requirements"
},
PipelineLayoutCreationError::InvalidPushConstant => {
"one of the push constants range didn't obey the rules"
},
}
}
#[inline]
fn cause(&self) -> Option<&error::Error> {
match *self {
PipelineLayoutCreationError::OomError(ref err) => Some(err),
PipelineLayoutCreationError::LimitsError(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for PipelineLayoutCreationError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self))
}
}
impl From<OomError> for PipelineLayoutCreationError {
#[inline]
fn from(err: OomError) -> PipelineLayoutCreationError {
PipelineLayoutCreationError::OomError(err)
}
}
impl From<PipelineLayoutLimitsError> for PipelineLayoutCreationError {
#[inline]
fn from(err: PipelineLayoutLimitsError) -> PipelineLayoutCreationError {
PipelineLayoutCreationError::LimitsError(err)
}
}
impl From<Error> for PipelineLayoutCreationError {
#[inline]
fn from(err: Error) -> PipelineLayoutCreationError {
match err {
err @ Error::OutOfHostMemory => {
PipelineLayoutCreationError::OomError(OomError::from(err))
},
err @ Error::OutOfDeviceMemory => {
PipelineLayoutCreationError::OomError(OomError::from(err))
},
_ => panic!("unexpected error: {:?}", err),
}
}
}