use super::descriptor::ShaderStageFlags;
use super::device::DeviceInner;
use super::image::Format;
use super::pipeline::PipelineLayout;
use super::render_pass::RenderPass;
use super::shader::ShaderModule;
use super::{Device, Error, Result, check};
use crate::raw::bindings::*;
use std::ffi::CString;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GraphicsShaderStage {
Vertex,
Fragment,
}
impl GraphicsShaderStage {
fn bit(self) -> u32 {
match self {
Self::Vertex => 0x1, Self::Fragment => 0x10, }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InputRate(pub VkVertexInputRate);
impl InputRate {
pub const VERTEX: Self = Self(VkVertexInputRate::VERTEX_INPUT_RATE_VERTEX);
pub const INSTANCE: Self = Self(VkVertexInputRate::VERTEX_INPUT_RATE_INSTANCE);
}
impl Default for InputRate {
fn default() -> Self {
Self::VERTEX
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CompareOp(pub VkCompareOp);
impl CompareOp {
pub const NEVER: Self = Self(VkCompareOp::COMPARE_OP_NEVER);
pub const LESS: Self = Self(VkCompareOp::COMPARE_OP_LESS);
pub const EQUAL: Self = Self(VkCompareOp::COMPARE_OP_EQUAL);
pub const LESS_OR_EQUAL: Self = Self(VkCompareOp::COMPARE_OP_LESS_OR_EQUAL);
pub const GREATER: Self = Self(VkCompareOp::COMPARE_OP_GREATER);
pub const NOT_EQUAL: Self = Self(VkCompareOp::COMPARE_OP_NOT_EQUAL);
pub const GREATER_OR_EQUAL: Self = Self(VkCompareOp::COMPARE_OP_GREATER_OR_EQUAL);
pub const ALWAYS: Self = Self(VkCompareOp::COMPARE_OP_ALWAYS);
}
#[derive(Debug, Clone, Copy)]
pub struct VertexInputBinding {
pub binding: u32,
pub stride: u32,
pub input_rate: InputRate,
}
#[derive(Debug, Clone, Copy)]
pub struct VertexInputAttribute {
pub location: u32,
pub binding: u32,
pub format: Format,
pub offset: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PrimitiveTopology(pub VkPrimitiveTopology);
impl PrimitiveTopology {
pub const POINT_LIST: Self = Self(VkPrimitiveTopology::PRIMITIVE_TOPOLOGY_POINT_LIST);
pub const LINE_LIST: Self = Self(VkPrimitiveTopology::PRIMITIVE_TOPOLOGY_LINE_LIST);
pub const LINE_STRIP: Self = Self(VkPrimitiveTopology::PRIMITIVE_TOPOLOGY_LINE_STRIP);
pub const TRIANGLE_LIST: Self = Self(VkPrimitiveTopology::PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
pub const TRIANGLE_STRIP: Self = Self(VkPrimitiveTopology::PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
pub const TRIANGLE_FAN: Self = Self(VkPrimitiveTopology::PRIMITIVE_TOPOLOGY_TRIANGLE_FAN);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PolygonMode(pub VkPolygonMode);
impl PolygonMode {
pub const FILL: Self = Self(VkPolygonMode::POLYGON_MODE_FILL);
pub const LINE: Self = Self(VkPolygonMode::POLYGON_MODE_LINE);
pub const POINT: Self = Self(VkPolygonMode::POLYGON_MODE_POINT);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CullMode(pub u32);
impl CullMode {
pub const NONE: Self = Self(0);
pub const FRONT: Self = Self(0x1);
pub const BACK: Self = Self(0x2);
pub const FRONT_AND_BACK: Self = Self(0x3);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FrontFace(pub VkFrontFace);
impl FrontFace {
pub const CLOCKWISE: Self = Self(VkFrontFace::FRONT_FACE_CLOCKWISE);
pub const COUNTER_CLOCKWISE: Self = Self(VkFrontFace::FRONT_FACE_COUNTER_CLOCKWISE);
}
pub struct GraphicsPipelineBuilder<'a> {
layout: &'a PipelineLayout,
render_pass: &'a RenderPass,
subpass: u32,
vertex_shader: Option<(&'a ShaderModule, &'a str)>,
fragment_shader: Option<(&'a ShaderModule, &'a str)>,
vertex_bindings: &'a [VertexInputBinding],
vertex_attributes: &'a [VertexInputAttribute],
topology: PrimitiveTopology,
polygon_mode: PolygonMode,
cull_mode: CullMode,
front_face: FrontFace,
viewport_width: u32,
viewport_height: u32,
blend_enable: bool,
depth_test: bool,
depth_write: bool,
depth_compare_op: CompareOp,
depth_bias_enable: bool,
depth_bias_constant: f32,
depth_bias_slope: f32,
depth_bias_clamp: f32,
color_attachment_count: u32,
dynamic_viewport_scissor: bool,
}
impl<'a> GraphicsPipelineBuilder<'a> {
pub fn new(layout: &'a PipelineLayout, render_pass: &'a RenderPass) -> Self {
Self {
layout,
render_pass,
subpass: 0,
vertex_shader: None,
fragment_shader: None,
vertex_bindings: &[],
vertex_attributes: &[],
topology: PrimitiveTopology::TRIANGLE_LIST,
polygon_mode: PolygonMode::FILL,
cull_mode: CullMode::BACK,
front_face: FrontFace::COUNTER_CLOCKWISE,
viewport_width: 1,
viewport_height: 1,
blend_enable: false,
depth_test: false,
depth_write: false,
depth_compare_op: CompareOp::LESS_OR_EQUAL,
depth_bias_enable: false,
depth_bias_constant: 0.0,
depth_bias_slope: 0.0,
depth_bias_clamp: 0.0,
color_attachment_count: 1,
dynamic_viewport_scissor: false,
}
}
pub fn stage(
mut self,
stage: GraphicsShaderStage,
shader: &'a ShaderModule,
entry_point: &'a str,
) -> Self {
match stage {
GraphicsShaderStage::Vertex => self.vertex_shader = Some((shader, entry_point)),
GraphicsShaderStage::Fragment => self.fragment_shader = Some((shader, entry_point)),
}
self
}
pub fn vertex_input(
mut self,
bindings: &'a [VertexInputBinding],
attributes: &'a [VertexInputAttribute],
) -> Self {
self.vertex_bindings = bindings;
self.vertex_attributes = attributes;
self
}
pub fn viewport_extent(mut self, width: u32, height: u32) -> Self {
self.viewport_width = width;
self.viewport_height = height;
self
}
pub fn topology(mut self, topology: PrimitiveTopology) -> Self {
self.topology = topology;
self
}
pub fn polygon_mode(mut self, mode: PolygonMode) -> Self {
self.polygon_mode = mode;
self
}
pub fn cull_mode(mut self, mode: CullMode) -> Self {
self.cull_mode = mode;
self
}
pub fn front_face(mut self, face: FrontFace) -> Self {
self.front_face = face;
self
}
pub fn alpha_blending(mut self, enable: bool) -> Self {
self.blend_enable = enable;
self
}
pub fn depth_test(mut self, test: bool, write: bool) -> Self {
self.depth_test = test;
self.depth_write = write;
self
}
pub fn depth_compare_op(mut self, op: CompareOp) -> Self {
self.depth_compare_op = op;
self
}
pub fn depth_bias(mut self, constant: f32, slope: f32, clamp: f32) -> Self {
self.depth_bias_enable = true;
self.depth_bias_constant = constant;
self.depth_bias_slope = slope;
self.depth_bias_clamp = clamp;
self
}
pub fn color_attachment_count(mut self, count: u32) -> Self {
self.color_attachment_count = count;
self
}
pub fn dynamic_viewport_scissor(mut self) -> Self {
self.dynamic_viewport_scissor = true;
self
}
pub fn build(self, device: &Device) -> Result<GraphicsPipeline> {
let create = device
.inner
.dispatch
.vkCreateGraphicsPipelines
.ok_or(Error::MissingFunction("vkCreateGraphicsPipelines"))?;
let vert = self
.vertex_shader
.ok_or(Error::Vk(VkResult::ERROR_INITIALIZATION_FAILED))?;
let frag = self
.fragment_shader
.ok_or(Error::Vk(VkResult::ERROR_INITIALIZATION_FAILED))?;
let vert_entry = CString::new(vert.1)?;
let frag_entry = CString::new(frag.1)?;
let stages = [
VkPipelineShaderStageCreateInfo {
sType: VkStructureType::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
stage: GraphicsShaderStage::Vertex.bit(),
module: vert.0.handle,
pName: vert_entry.as_ptr(),
..Default::default()
},
VkPipelineShaderStageCreateInfo {
sType: VkStructureType::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
stage: GraphicsShaderStage::Fragment.bit(),
module: frag.0.handle,
pName: frag_entry.as_ptr(),
..Default::default()
},
];
let raw_bindings: Vec<VkVertexInputBindingDescription> = self
.vertex_bindings
.iter()
.map(|b| VkVertexInputBindingDescription {
binding: b.binding,
stride: b.stride,
inputRate: b.input_rate.0,
})
.collect();
let raw_attributes: Vec<VkVertexInputAttributeDescription> = self
.vertex_attributes
.iter()
.map(|a| VkVertexInputAttributeDescription {
location: a.location,
binding: a.binding,
format: a.format.0,
offset: a.offset,
})
.collect();
let vertex_input = VkPipelineVertexInputStateCreateInfo {
sType: VkStructureType::STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
vertexBindingDescriptionCount: raw_bindings.len() as u32,
pVertexBindingDescriptions: if raw_bindings.is_empty() {
std::ptr::null()
} else {
raw_bindings.as_ptr()
},
vertexAttributeDescriptionCount: raw_attributes.len() as u32,
pVertexAttributeDescriptions: if raw_attributes.is_empty() {
std::ptr::null()
} else {
raw_attributes.as_ptr()
},
..Default::default()
};
let input_assembly = VkPipelineInputAssemblyStateCreateInfo {
sType: VkStructureType::STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
topology: self.topology.0,
primitiveRestartEnable: 0,
..Default::default()
};
let viewport = VkViewport {
x: 0.0,
y: 0.0,
width: self.viewport_width as f32,
height: self.viewport_height as f32,
minDepth: 0.0,
maxDepth: 1.0,
};
let scissor = VkRect2D {
offset: VkOffset2D { x: 0, y: 0 },
extent: VkExtent2D {
width: self.viewport_width,
height: self.viewport_height,
},
};
let viewport_state = VkPipelineViewportStateCreateInfo {
sType: VkStructureType::STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
viewportCount: 1,
pViewports: if self.dynamic_viewport_scissor {
std::ptr::null()
} else {
&viewport
},
scissorCount: 1,
pScissors: if self.dynamic_viewport_scissor {
std::ptr::null()
} else {
&scissor
},
..Default::default()
};
let raster_state = VkPipelineRasterizationStateCreateInfo {
sType: VkStructureType::STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
depthClampEnable: 0,
rasterizerDiscardEnable: 0,
polygonMode: self.polygon_mode.0,
cullMode: self.cull_mode.0,
frontFace: self.front_face.0,
depthBiasEnable: if self.depth_bias_enable { 1 } else { 0 },
depthBiasConstantFactor: self.depth_bias_constant,
depthBiasClamp: self.depth_bias_clamp,
depthBiasSlopeFactor: self.depth_bias_slope,
lineWidth: 1.0,
..Default::default()
};
let multisample_state = VkPipelineMultisampleStateCreateInfo {
sType: VkStructureType::STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
rasterizationSamples: SAMPLE_COUNT_1_BIT,
sampleShadingEnable: 0,
minSampleShading: 1.0,
pSampleMask: std::ptr::null(),
alphaToCoverageEnable: 0,
alphaToOneEnable: 0,
..Default::default()
};
let depth_stencil_state = VkPipelineDepthStencilStateCreateInfo {
sType: VkStructureType::STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
depthTestEnable: if self.depth_test { 1 } else { 0 },
depthWriteEnable: if self.depth_write { 1 } else { 0 },
depthCompareOp: self.depth_compare_op.0,
depthBoundsTestEnable: 0,
stencilTestEnable: 0,
front: VkStencilOpState::default(),
back: VkStencilOpState::default(),
minDepthBounds: 0.0,
maxDepthBounds: 1.0,
..Default::default()
};
let single_blend = VkPipelineColorBlendAttachmentState {
blendEnable: if self.blend_enable { 1 } else { 0 },
srcColorBlendFactor: VkBlendFactor::BLEND_FACTOR_SRC_ALPHA,
dstColorBlendFactor: VkBlendFactor::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
colorBlendOp: VkBlendOp::BLEND_OP_ADD,
srcAlphaBlendFactor: VkBlendFactor::BLEND_FACTOR_ONE,
dstAlphaBlendFactor: VkBlendFactor::BLEND_FACTOR_ZERO,
alphaBlendOp: VkBlendOp::BLEND_OP_ADD,
colorWriteMask: 0xF,
};
let color_blend_attachments: Vec<VkPipelineColorBlendAttachmentState> =
vec![single_blend; self.color_attachment_count as usize];
let color_blend_state = VkPipelineColorBlendStateCreateInfo {
sType: VkStructureType::STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
logicOpEnable: 0,
logicOp: VkLogicOp::LOGIC_OP_COPY,
attachmentCount: color_blend_attachments.len() as u32,
pAttachments: color_blend_attachments.as_ptr(),
blendConstants: [0.0; 4],
..Default::default()
};
let dynamic_states = [
VkDynamicState::DYNAMIC_STATE_VIEWPORT,
VkDynamicState::DYNAMIC_STATE_SCISSOR,
];
let dynamic_state_info = VkPipelineDynamicStateCreateInfo {
sType: VkStructureType::STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
dynamicStateCount: dynamic_states.len() as u32,
pDynamicStates: dynamic_states.as_ptr(),
..Default::default()
};
let info = VkGraphicsPipelineCreateInfo {
sType: VkStructureType::STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
stageCount: stages.len() as u32,
pStages: stages.as_ptr(),
pVertexInputState: &vertex_input,
pInputAssemblyState: &input_assembly,
pTessellationState: std::ptr::null(),
pViewportState: &viewport_state,
pRasterizationState: &raster_state,
pMultisampleState: &multisample_state,
pDepthStencilState: &depth_stencil_state,
pColorBlendState: &color_blend_state,
pDynamicState: if self.dynamic_viewport_scissor {
&dynamic_state_info
} else {
std::ptr::null()
},
layout: self.layout.handle,
renderPass: self.render_pass.handle,
subpass: self.subpass,
basePipelineHandle: 0,
basePipelineIndex: -1,
..Default::default()
};
let mut handle: VkPipeline = 0;
check(unsafe {
create(
device.inner.handle,
0, 1,
&info,
std::ptr::null(),
&mut handle,
)
})?;
let _ = (
stages,
raw_bindings,
raw_attributes,
vertex_input,
input_assembly,
viewport,
scissor,
viewport_state,
raster_state,
multisample_state,
depth_stencil_state,
color_blend_attachments,
color_blend_state,
vert_entry,
frag_entry,
);
Ok(GraphicsPipeline {
handle,
device: Arc::clone(&device.inner),
})
}
}
pub struct GraphicsPipeline {
pub(crate) handle: VkPipeline,
pub(crate) device: Arc<DeviceInner>,
}
impl GraphicsPipeline {
pub fn raw(&self) -> VkPipeline {
self.handle
}
}
impl Drop for GraphicsPipeline {
fn drop(&mut self) {
if let Some(destroy) = self.device.dispatch.vkDestroyPipeline {
unsafe { destroy(self.device.handle, self.handle, std::ptr::null()) };
}
}
}
#[doc(hidden)]
pub fn _shader_stage_flags() -> ShaderStageFlags {
ShaderStageFlags::ALL
}