use std::borrow::Cow;
use std::error;
use std::ffi::CStr;
use std::fmt;
use std::iter;
use std::iter::Empty as EmptyIter;
use std::marker::PhantomData;
use std::mem;
use std::ops::Range;
use std::ptr;
use std::sync::Arc;
use descriptor::pipeline_layout::EmptyPipelineDesc;
use descriptor::pipeline_layout::PipelineLayoutDesc;
use format::Format;
use pipeline::input_assembly::PrimitiveTopology;
use OomError;
use VulkanObject;
use check_errors;
use device::Device;
use vk;
#[derive(Debug)]
pub struct ShaderModule {
module: vk::ShaderModule,
device: Arc<Device>,
}
impl ShaderModule {
pub unsafe fn new(device: Arc<Device>, spirv: &[u8]) -> Result<Arc<ShaderModule>, OomError> {
debug_assert!((spirv.len() % 4) == 0);
let module = {
let infos = vk::ShaderModuleCreateInfo {
sType: vk::STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
pNext: ptr::null(),
flags: 0, codeSize: spirv.len(),
pCode: spirv.as_ptr() as *const _,
};
let vk = device.pointers();
let mut output = mem::uninitialized();
check_errors(vk.CreateShaderModule(device.internal_object(),
&infos,
ptr::null(),
&mut output))?;
output
};
Ok(Arc::new(ShaderModule {
module: module,
device: device,
}))
}
pub unsafe fn graphics_entry_point<'a, S, I, O, L>(&'a self, name: &'a CStr, input: I, output: O,
layout: L, ty: GraphicsShaderType)
-> GraphicsEntryPoint<'a, S, I, O, L>
{
GraphicsEntryPoint {
module: self,
name: name,
input: input,
output: output,
layout: layout,
ty: ty,
marker: PhantomData,
}
}
#[inline]
pub unsafe fn compute_entry_point<'a, S, L>(&'a self, name: &'a CStr, layout: L)
-> ComputeEntryPoint<'a, S, L> {
ComputeEntryPoint {
module: self,
name: name,
layout: layout,
marker: PhantomData,
}
}
}
unsafe impl VulkanObject for ShaderModule {
type Object = vk::ShaderModule;
#[inline]
fn internal_object(&self) -> vk::ShaderModule {
self.module
}
}
impl Drop for ShaderModule {
#[inline]
fn drop(&mut self) {
unsafe {
let vk = self.device.pointers();
vk.DestroyShaderModule(self.device.internal_object(), self.module, ptr::null());
}
}
}
pub unsafe trait GraphicsEntryPointAbstract: EntryPointAbstract {
type InputDefinition: ShaderInterfaceDef;
type OutputDefinition: ShaderInterfaceDef;
fn input(&self) -> &Self::InputDefinition;
fn output(&self) -> &Self::OutputDefinition;
fn ty(&self) -> GraphicsShaderType;
}
#[derive(Debug, Copy, Clone)]
pub struct GraphicsEntryPoint<'a, S, I, O, L> {
module: &'a ShaderModule,
name: &'a CStr,
input: I,
layout: L,
output: O,
ty: GraphicsShaderType,
marker: PhantomData<S>,
}
unsafe impl<'a, S, I, O, L> EntryPointAbstract for GraphicsEntryPoint<'a, S, I, O, L>
where L: PipelineLayoutDesc,
I: ShaderInterfaceDef,
O: ShaderInterfaceDef,
S: SpecializationConstants,
{
type PipelineLayout = L;
type SpecializationConstants = S;
#[inline]
fn module(&self) -> &ShaderModule {
self.module
}
#[inline]
fn name(&self) -> &CStr {
self.name
}
#[inline]
fn layout(&self) -> &L {
&self.layout
}
}
unsafe impl<'a, S, I, O, L> GraphicsEntryPointAbstract for GraphicsEntryPoint<'a, S, I, O, L>
where L: PipelineLayoutDesc,
I: ShaderInterfaceDef,
O: ShaderInterfaceDef,
S: SpecializationConstants,
{
type InputDefinition = I;
type OutputDefinition = O;
#[inline]
fn input(&self) -> &I {
&self.input
}
#[inline]
fn output(&self) -> &O {
&self.output
}
#[inline]
fn ty(&self) -> GraphicsShaderType {
self.ty
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum GraphicsShaderType {
Vertex,
TessellationControl,
TessellationEvaluation,
Geometry(GeometryShaderExecutionMode),
Fragment,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum GeometryShaderExecutionMode {
Points,
Lines,
LinesWithAdjacency,
Triangles,
TrianglesWithAdjacency,
}
impl GeometryShaderExecutionMode {
#[inline]
pub fn matches(&self, input: PrimitiveTopology) -> bool {
match (*self, input) {
(GeometryShaderExecutionMode::Points, PrimitiveTopology::PointList) => true,
(GeometryShaderExecutionMode::Lines, PrimitiveTopology::LineList) => true,
(GeometryShaderExecutionMode::Lines, PrimitiveTopology::LineStrip) => true,
(GeometryShaderExecutionMode::LinesWithAdjacency,
PrimitiveTopology::LineListWithAdjacency) => true,
(GeometryShaderExecutionMode::LinesWithAdjacency,
PrimitiveTopology::LineStripWithAdjacency) => true,
(GeometryShaderExecutionMode::Triangles, PrimitiveTopology::TriangleList) => true,
(GeometryShaderExecutionMode::Triangles, PrimitiveTopology::TriangleStrip) => true,
(GeometryShaderExecutionMode::Triangles, PrimitiveTopology::TriangleFan) => true,
(GeometryShaderExecutionMode::TrianglesWithAdjacency,
PrimitiveTopology::TriangleListWithAdjacency) => true,
(GeometryShaderExecutionMode::TrianglesWithAdjacency,
PrimitiveTopology::TriangleStripWithAdjacency) => true,
_ => false,
}
}
}
pub unsafe trait EntryPointAbstract {
type PipelineLayout: PipelineLayoutDesc;
type SpecializationConstants: SpecializationConstants;
fn module(&self) -> &ShaderModule;
fn name(&self) -> &CStr;
fn layout(&self) -> &Self::PipelineLayout;
}
#[derive(Debug, Copy, Clone)]
pub struct ComputeEntryPoint<'a, S, L> {
module: &'a ShaderModule,
name: &'a CStr,
layout: L,
marker: PhantomData<S>,
}
unsafe impl<'a, S, L> EntryPointAbstract for ComputeEntryPoint<'a, S, L>
where L: PipelineLayoutDesc,
S: SpecializationConstants,
{
type PipelineLayout = L;
type SpecializationConstants = S;
#[inline]
fn module(&self) -> &ShaderModule {
self.module
}
#[inline]
fn name(&self) -> &CStr {
self.name
}
#[inline]
fn layout(&self) -> &L {
&self.layout
}
}
#[derive(Debug, Copy, Clone)]
pub enum EmptyEntryPointDummy {
}
unsafe impl EntryPointAbstract for EmptyEntryPointDummy {
type PipelineLayout = EmptyPipelineDesc;
type SpecializationConstants = ();
#[inline]
fn module(&self) -> &ShaderModule {
unreachable!()
}
#[inline]
fn name(&self) -> &CStr {
unreachable!()
}
#[inline]
fn layout(&self) -> &EmptyPipelineDesc {
unreachable!()
}
}
unsafe impl GraphicsEntryPointAbstract for EmptyEntryPointDummy {
type InputDefinition = EmptyShaderInterfaceDef;
type OutputDefinition = EmptyShaderInterfaceDef;
#[inline]
fn input(&self) -> &EmptyShaderInterfaceDef {
unreachable!()
}
#[inline]
fn output(&self) -> &EmptyShaderInterfaceDef {
unreachable!()
}
#[inline]
fn ty(&self) -> GraphicsShaderType {
unreachable!()
}
}
pub unsafe trait ShaderInterfaceDef {
type Iter: ExactSizeIterator<Item = ShaderInterfaceDefEntry>;
fn elements(&self) -> Self::Iter;
}
#[derive(Debug, Clone)]
pub struct ShaderInterfaceDefEntry {
pub location: Range<u32>,
pub format: Format,
pub name: Option<Cow<'static, str>>,
}
#[derive(Debug, Copy, Clone)]
pub struct EmptyShaderInterfaceDef;
unsafe impl ShaderInterfaceDef for EmptyShaderInterfaceDef {
type Iter = EmptyIter<ShaderInterfaceDefEntry>;
#[inline]
fn elements(&self) -> Self::Iter {
iter::empty()
}
}
pub unsafe trait ShaderInterfaceDefMatch<I>: ShaderInterfaceDef
where I: ShaderInterfaceDef
{
fn matches(&self, other: &I) -> Result<(), ShaderInterfaceMismatchError>;
}
unsafe impl<T, I> ShaderInterfaceDefMatch<I> for T
where T: ShaderInterfaceDef,
I: ShaderInterfaceDef
{
fn matches(&self, other: &I) -> Result<(), ShaderInterfaceMismatchError> {
if self.elements().len() != other.elements().len() {
return Err(ShaderInterfaceMismatchError::ElementsCountMismatch);
}
for a in self.elements() {
for loc in a.location.clone() {
let b = match other
.elements()
.find(|e| loc >= e.location.start && loc < e.location.end) {
None => return Err(ShaderInterfaceMismatchError::MissingElement {
location: loc,
}),
Some(b) => b,
};
if a.format != b.format {
return Err(ShaderInterfaceMismatchError::FormatMismatch);
}
}
}
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ShaderInterfaceMismatchError {
ElementsCountMismatch,
MissingElement { location: u32 },
FormatMismatch,
}
impl error::Error for ShaderInterfaceMismatchError {
#[inline]
fn description(&self) -> &str {
match *self {
ShaderInterfaceMismatchError::ElementsCountMismatch =>
"the number of elements mismatches",
ShaderInterfaceMismatchError::MissingElement { .. } => "an element is missing",
ShaderInterfaceMismatchError::FormatMismatch =>
"the format of an element does not match",
}
}
}
impl fmt::Display for ShaderInterfaceMismatchError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self))
}
}
pub unsafe trait SpecializationConstants {
fn descriptors() -> &'static [SpecializationMapEntry];
}
unsafe impl SpecializationConstants for () {
#[inline]
fn descriptors() -> &'static [SpecializationMapEntry] {
&[]
}
}
#[repr(C)]
pub struct SpecializationMapEntry {
pub constant_id: u32,
pub offset: u32,
pub size: usize,
}