#![allow(deprecated)]
use smallvec::SmallVec;
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::u32;
use descriptor::pipeline_layout::PipelineLayoutAbstract;
use device::Device;
use framebuffer::RenderPassAbstract;
use framebuffer::Subpass;
use pipeline::blend::AttachmentBlend;
use pipeline::blend::AttachmentsBlend;
use pipeline::blend::Blend;
use pipeline::blend::LogicOp;
use pipeline::depth_stencil::Compare;
use pipeline::depth_stencil::DepthBounds;
use pipeline::depth_stencil::DepthStencil;
use pipeline::graphics_pipeline::GraphicsPipeline;
use pipeline::graphics_pipeline::GraphicsPipelineCreationError;
use pipeline::graphics_pipeline::Inner as GraphicsPipelineInner;
use pipeline::input_assembly::PrimitiveTopology;
use pipeline::raster::CullMode;
use pipeline::raster::DepthBiasControl;
use pipeline::raster::FrontFace;
use pipeline::raster::PolygonMode;
use pipeline::raster::Rasterization;
use pipeline::shader::EmptyEntryPointDummy;
use pipeline::shader::GraphicsEntryPointAbstract;
use pipeline::shader::GraphicsShaderType;
use pipeline::shader::ShaderInterfaceDefMatch;
use pipeline::shader::SpecializationConstants;
use pipeline::vertex::BufferlessDefinition;
use pipeline::vertex::SingleBufferDefinition;
use pipeline::vertex::VertexDefinition;
use pipeline::viewport::Scissor;
use pipeline::viewport::Viewport;
use pipeline::viewport::ViewportsState;
use VulkanObject;
use check_errors;
use descriptor::pipeline_layout::PipelineLayoutDesc;
use descriptor::pipeline_layout::PipelineLayoutDescTweaks;
use descriptor::pipeline_layout::PipelineLayoutSuperset;
use framebuffer::RenderPassSubpassInterface;
use vk;
pub struct GraphicsPipelineBuilder<Vdef, Vs, Vss, Tcs, Tcss, Tes, Tess, Gs, Gss, Fs, Fss, Rp> {
vertex_input: Vdef,
vertex_shader: Option<(Vs, Vss)>,
input_assembly: vk::PipelineInputAssemblyStateCreateInfo,
input_assembly_topology: PrimitiveTopology,
tessellation: Option<TessInfo<Tcs, Tcss, Tes, Tess>>,
geometry_shader: Option<(Gs, Gss)>,
viewport: Option<ViewportsState>,
raster: Rasterization,
multisample: vk::PipelineMultisampleStateCreateInfo,
fragment_shader: Option<(Fs, Fss)>,
depth_stencil: DepthStencil,
blend: Blend,
render_pass: Option<Subpass<Rp>>,
}
#[derive(Copy, Clone)]
struct TessInfo<Tcs, Tcss, Tes, Tess> {
tessellation_control_shader: (Tcs, Tcss),
tessellation_evaluation_shader: (Tes, Tess),
}
impl
GraphicsPipelineBuilder<BufferlessDefinition,
EmptyEntryPointDummy,
(),
EmptyEntryPointDummy,
(),
EmptyEntryPointDummy,
(),
EmptyEntryPointDummy,
(),
EmptyEntryPointDummy,
(),
()> {
pub(super) fn new() -> Self {
unsafe {
GraphicsPipelineBuilder {
vertex_input: BufferlessDefinition,
vertex_shader: None,
input_assembly: vk::PipelineInputAssemblyStateCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
topology: PrimitiveTopology::TriangleList.into(),
..mem::zeroed()
},
input_assembly_topology: PrimitiveTopology::TriangleList,
tessellation: None,
geometry_shader: None,
viewport: None,
raster: Default::default(),
multisample: vk::PipelineMultisampleStateCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
..mem::zeroed()
},
fragment_shader: None,
depth_stencil: DepthStencil::disabled(),
blend: Blend::pass_through(),
render_pass: None,
}
}
}
}
impl<Vdef, Vs, Vss, Tcs, Tcss, Tes, Tess, Gs, Gss, Fs, Fss, Rp>
GraphicsPipelineBuilder<Vdef, Vs, Vss, Tcs, Tcss, Tes, Tess, Gs, Gss, Fs, Fss, Rp>
where Vdef: VertexDefinition<Vs::InputDefinition>,
Vs: GraphicsEntryPointAbstract,
Fs: GraphicsEntryPointAbstract,
Gs: GraphicsEntryPointAbstract,
Tcs: GraphicsEntryPointAbstract,
Tes: GraphicsEntryPointAbstract,
Vss: SpecializationConstants,
Tcss: SpecializationConstants,
Tess: SpecializationConstants,
Gss: SpecializationConstants,
Fss: SpecializationConstants,
Vs::PipelineLayout: Clone + 'static + Send + Sync,
Fs::PipelineLayout: Clone + 'static + Send + Sync,
Tcs::PipelineLayout: Clone + 'static + Send + Sync,
Tes::PipelineLayout: Clone + 'static + Send + Sync,
Gs::PipelineLayout: Clone + 'static + Send + Sync,
Tcs::InputDefinition: ShaderInterfaceDefMatch<Vs::OutputDefinition>,
Tes::InputDefinition: ShaderInterfaceDefMatch<Tcs::OutputDefinition>,
Gs::InputDefinition: ShaderInterfaceDefMatch<Tes::OutputDefinition>
+ ShaderInterfaceDefMatch<Vs::OutputDefinition>,
Fs::InputDefinition: ShaderInterfaceDefMatch<Gs::OutputDefinition>
+ ShaderInterfaceDefMatch<Tes::OutputDefinition>
+ ShaderInterfaceDefMatch<Vs::OutputDefinition>,
Rp: RenderPassAbstract + RenderPassSubpassInterface<Fs::OutputDefinition>
{
pub fn build(self, device: Arc<Device>)
-> Result<GraphicsPipeline<Vdef, Box<dyn PipelineLayoutAbstract + Send + Sync>, Rp>,
GraphicsPipelineCreationError> {
self.with_auto_layout(device, &[])
}
pub fn with_auto_layout(self, device: Arc<Device>, dynamic_buffers: &[(usize, usize)])
-> Result<GraphicsPipeline<Vdef, Box<dyn PipelineLayoutAbstract + Send + Sync>, Rp>,
GraphicsPipelineCreationError>
{
let pipeline_layout;
if let Some(ref tess) = self.tessellation {
if let Some(ref gs) = self.geometry_shader {
if let Err(err) = tess.tessellation_control_shader
.0
.input()
.matches(self.vertex_shader.as_ref().unwrap().0.output())
{
return Err(GraphicsPipelineCreationError::VertexTessControlStagesMismatch(err));
}
if let Err(err) = tess.tessellation_evaluation_shader
.0
.input()
.matches(tess.tessellation_control_shader.0.output())
{
return Err(GraphicsPipelineCreationError::TessControlTessEvalStagesMismatch(err));
}
if let Err(err) = gs.0
.input()
.matches(tess.tessellation_evaluation_shader.0.output())
{
return Err(GraphicsPipelineCreationError::TessEvalGeometryStagesMismatch(err));
}
if let Err(err) = self.fragment_shader
.as_ref()
.unwrap()
.0
.input()
.matches(gs.0.output())
{
return Err(GraphicsPipelineCreationError::GeometryFragmentStagesMismatch(err));
}
pipeline_layout = Box::new(
PipelineLayoutDescTweaks::new(self.vertex_shader.as_ref().unwrap().0.layout().clone()
.union(self.fragment_shader.as_ref().unwrap().0.layout().clone())
.union(self.tessellation.as_ref().unwrap().tessellation_control_shader.0.layout().clone())
.union(self.tessellation.as_ref().unwrap().tessellation_evaluation_shader.0.layout().clone())
.union(self.geometry_shader.as_ref().unwrap().0.layout().clone()),
dynamic_buffers.into_iter().cloned()
).build(device.clone()).unwrap()) as Box<_>;
} else {
if let Err(err) = tess.tessellation_control_shader
.0
.input()
.matches(self.vertex_shader.as_ref().unwrap().0.output())
{
return Err(GraphicsPipelineCreationError::VertexTessControlStagesMismatch(err));
}
if let Err(err) = tess.tessellation_evaluation_shader
.0
.input()
.matches(tess.tessellation_control_shader.0.output())
{
return Err(GraphicsPipelineCreationError::TessControlTessEvalStagesMismatch(err));
}
if let Err(err) = self.fragment_shader
.as_ref()
.unwrap()
.0
.input()
.matches(tess.tessellation_evaluation_shader.0.output())
{
return Err(GraphicsPipelineCreationError::TessEvalFragmentStagesMismatch(err));
}
pipeline_layout = Box::new(
PipelineLayoutDescTweaks::new(self.vertex_shader.as_ref().unwrap().0.layout().clone()
.union(self.fragment_shader.as_ref().unwrap().0.layout().clone())
.union(self.tessellation.as_ref().unwrap().tessellation_control_shader.0.layout().clone())
.union(self.tessellation.as_ref().unwrap().tessellation_evaluation_shader.0.layout().clone()),
dynamic_buffers.into_iter().cloned()
).build(device.clone()).unwrap()) as Box<_>;
}
} else {
if let Some(ref geometry_shader) = self.geometry_shader {
if let Err(err) = geometry_shader
.0
.input()
.matches(self.vertex_shader.as_ref().unwrap().0.output())
{
return Err(GraphicsPipelineCreationError::VertexGeometryStagesMismatch(err));
}
if let Err(err) = self.fragment_shader
.as_ref()
.unwrap()
.0
.input()
.matches(geometry_shader.0.output())
{
return Err(GraphicsPipelineCreationError::GeometryFragmentStagesMismatch(err));
}
pipeline_layout = Box::new(
PipelineLayoutDescTweaks::new(self.vertex_shader.as_ref().unwrap().0.layout().clone()
.union(self.fragment_shader.as_ref().unwrap().0.layout().clone())
.union(self.geometry_shader.as_ref().unwrap().0.layout().clone()),
dynamic_buffers.into_iter().cloned()
).build(device.clone()).unwrap()) as Box<_>;
} else {
if let Err(err) = self.fragment_shader
.as_ref()
.unwrap()
.0
.input()
.matches(self.vertex_shader.as_ref().unwrap().0.output())
{
return Err(GraphicsPipelineCreationError::VertexFragmentStagesMismatch(err));
}
pipeline_layout =
Box::new(
PipelineLayoutDescTweaks::new(self.vertex_shader
.as_ref()
.unwrap()
.0
.layout()
.clone()
.union(self.fragment_shader.as_ref().unwrap().0.layout().clone()),
dynamic_buffers.into_iter().cloned()
).build(device.clone()).unwrap()) as Box<_>;
}
}
self.with_pipeline_layout(device, pipeline_layout)
}
pub fn with_pipeline_layout<Pl>(mut self, device: Arc<Device>, pipeline_layout: Pl)
-> Result<GraphicsPipeline<Vdef, Pl, Rp>,
GraphicsPipelineCreationError>
where Pl: PipelineLayoutAbstract
{
let vk = device.pointers();
PipelineLayoutSuperset::ensure_superset_of(&pipeline_layout,
self.vertex_shader
.as_ref()
.unwrap()
.0
.layout())?;
PipelineLayoutSuperset::ensure_superset_of(&pipeline_layout,
self.fragment_shader
.as_ref()
.unwrap()
.0
.layout())?;
if let Some(ref geometry_shader) = self.geometry_shader {
PipelineLayoutSuperset::ensure_superset_of(&pipeline_layout,
geometry_shader.0.layout())?;
}
if let Some(ref tess) = self.tessellation {
PipelineLayoutSuperset::ensure_superset_of(&pipeline_layout,
tess.tessellation_control_shader
.0
.layout())?;
PipelineLayoutSuperset::ensure_superset_of(&pipeline_layout,
tess.tessellation_evaluation_shader
.0
.layout())?;
}
if !RenderPassSubpassInterface::is_compatible_with(&self.render_pass
.as_ref()
.unwrap()
.render_pass(),
self.render_pass
.as_ref()
.unwrap()
.index(),
self.fragment_shader
.as_ref()
.unwrap()
.0
.output())
{
return Err(GraphicsPipelineCreationError::FragmentShaderRenderPassIncompatible);
}
let mut dynamic_states: SmallVec<[vk::DynamicState; 8]> = SmallVec::new();
let vertex_shader_specialization = {
let spec_descriptors = Vss::descriptors();
let constants = &self.vertex_shader.as_ref().unwrap().1;
vk::SpecializationInfo {
mapEntryCount: spec_descriptors.len() as u32,
pMapEntries: spec_descriptors.as_ptr() as *const _,
dataSize: mem::size_of_val(constants),
pData: constants as *const Vss as *const _,
}
};
let tess_shader_specialization = if let Some(ref tess) = self.tessellation {
let tcs_spec = {
let spec_descriptors = Tcss::descriptors();
let constants = &tess.tessellation_control_shader.1;
vk::SpecializationInfo {
mapEntryCount: spec_descriptors.len() as u32,
pMapEntries: spec_descriptors.as_ptr() as *const _,
dataSize: mem::size_of_val(constants),
pData: constants as *const Tcss as *const _,
}
};
let tes_spec = {
let spec_descriptors = Tess::descriptors();
let constants = &tess.tessellation_evaluation_shader.1;
vk::SpecializationInfo {
mapEntryCount: spec_descriptors.len() as u32,
pMapEntries: spec_descriptors.as_ptr() as *const _,
dataSize: mem::size_of_val(constants),
pData: constants as *const Tess as *const _,
}
};
Some((tcs_spec, tes_spec))
} else {
None
};
let geometry_shader_specialization = if let Some(ref gs) = self.geometry_shader {
let spec_descriptors = Gss::descriptors();
let constants = &gs.1;
Some(vk::SpecializationInfo {
mapEntryCount: spec_descriptors.len() as u32,
pMapEntries: spec_descriptors.as_ptr() as *const _,
dataSize: mem::size_of_val(constants),
pData: constants as *const Gss as *const _,
})
} else {
None
};
let fragment_shader_specialization = {
let spec_descriptors = Fss::descriptors();
let constants = &self.fragment_shader.as_ref().unwrap().1;
vk::SpecializationInfo {
mapEntryCount: spec_descriptors.len() as u32,
pMapEntries: spec_descriptors.as_ptr() as *const _,
dataSize: mem::size_of_val(constants),
pData: constants as *const Fss as *const _,
}
};
let stages = {
let mut stages = SmallVec::<[_; 5]>::new();
match self.vertex_shader.as_ref().unwrap().0.ty() {
GraphicsShaderType::Vertex => {},
_ => return Err(GraphicsPipelineCreationError::WrongShaderType),
};
stages.push(vk::PipelineShaderStageCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
stage: vk::SHADER_STAGE_VERTEX_BIT,
module: self.vertex_shader
.as_ref()
.unwrap()
.0
.module()
.internal_object(),
pName: self.vertex_shader.as_ref().unwrap().0.name().as_ptr(),
pSpecializationInfo: &vertex_shader_specialization as *const _,
});
match self.fragment_shader.as_ref().unwrap().0.ty() {
GraphicsShaderType::Fragment => {},
_ => return Err(GraphicsPipelineCreationError::WrongShaderType),
};
stages.push(vk::PipelineShaderStageCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
stage: vk::SHADER_STAGE_FRAGMENT_BIT,
module: self.fragment_shader
.as_ref()
.unwrap()
.0
.module()
.internal_object(),
pName: self.fragment_shader.as_ref().unwrap().0.name().as_ptr(),
pSpecializationInfo: &fragment_shader_specialization as *const _,
});
if let Some(ref gs) = self.geometry_shader {
if !device.enabled_features().geometry_shader {
return Err(GraphicsPipelineCreationError::GeometryShaderFeatureNotEnabled);
}
stages.push(vk::PipelineShaderStageCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
stage: vk::SHADER_STAGE_GEOMETRY_BIT,
module: gs.0.module().internal_object(),
pName: gs.0.name().as_ptr(),
pSpecializationInfo: geometry_shader_specialization
.as_ref()
.unwrap() as
*const _,
});
}
if let Some(ref tess) = self.tessellation {
if !device.enabled_features().tessellation_shader {
return Err(GraphicsPipelineCreationError::TessellationShaderFeatureNotEnabled);
}
match tess.tessellation_control_shader.0.ty() {
GraphicsShaderType::TessellationControl => {},
_ => return Err(GraphicsPipelineCreationError::WrongShaderType),
};
match tess.tessellation_evaluation_shader.0.ty() {
GraphicsShaderType::TessellationEvaluation => {},
_ => return Err(GraphicsPipelineCreationError::WrongShaderType),
};
stages.push(vk::PipelineShaderStageCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
stage: vk::SHADER_STAGE_TESSELLATION_CONTROL_BIT,
module: tess.tessellation_control_shader
.0
.module()
.internal_object(),
pName: tess.tessellation_control_shader.0.name().as_ptr(),
pSpecializationInfo: &tess_shader_specialization
.as_ref()
.unwrap()
.0 as
*const _,
});
stages.push(vk::PipelineShaderStageCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
stage: vk::SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
module: tess.tessellation_evaluation_shader
.0
.module()
.internal_object(),
pName: tess.tessellation_evaluation_shader.0.name().as_ptr(),
pSpecializationInfo: &tess_shader_specialization
.as_ref()
.unwrap()
.1 as
*const _,
});
}
stages
};
let (binding_descriptions, attribute_descriptions) = {
let (buffers_iter, attribs_iter) =
self.vertex_input
.definition(self.vertex_shader.as_ref().unwrap().0.input())?;
let mut binding_descriptions = SmallVec::<[_; 8]>::new();
for (num, stride, rate) in buffers_iter {
if stride >
device
.physical_device()
.limits()
.max_vertex_input_binding_stride() as usize
{
return Err(GraphicsPipelineCreationError::MaxVertexInputBindingStrideExceeded {
binding: num as usize,
max: device.physical_device().limits().max_vertex_input_binding_stride() as usize,
obtained: stride,
});
}
binding_descriptions.push(vk::VertexInputBindingDescription {
binding: num as u32,
stride: stride as u32,
inputRate: rate as u32,
});
}
let mut attribute_descriptions = SmallVec::<[_; 8]>::new();
for (loc, binding, info) in attribs_iter {
if info.offset >
device
.physical_device()
.limits()
.max_vertex_input_attribute_offset() as usize
{
return Err(GraphicsPipelineCreationError::MaxVertexInputAttributeOffsetExceeded {
max: device.physical_device().limits().max_vertex_input_attribute_offset() as usize,
obtained: info.offset,
});
}
debug_assert!(binding_descriptions
.iter()
.find(|b| b.binding == binding)
.is_some());
attribute_descriptions.push(vk::VertexInputAttributeDescription {
location: loc as u32,
binding: binding as u32,
format: info.format as u32,
offset: info.offset as u32,
});
}
(binding_descriptions, attribute_descriptions)
};
if binding_descriptions.len() >
device
.physical_device()
.limits()
.max_vertex_input_bindings() as usize
{
return Err(GraphicsPipelineCreationError::MaxVertexInputBindingsExceeded {
max: device
.physical_device()
.limits()
.max_vertex_input_bindings() as
usize,
obtained: binding_descriptions.len(),
});
}
if attribute_descriptions.len() >
device
.physical_device()
.limits()
.max_vertex_input_attributes() as usize
{
return Err(GraphicsPipelineCreationError::MaxVertexInputAttributesExceeded {
max: device
.physical_device()
.limits()
.max_vertex_input_attributes() as
usize,
obtained: attribute_descriptions.len(),
});
}
let vertex_input_state = vk::PipelineVertexInputStateCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
vertexBindingDescriptionCount: binding_descriptions.len() as u32,
pVertexBindingDescriptions: binding_descriptions.as_ptr(),
vertexAttributeDescriptionCount: attribute_descriptions.len() as u32,
pVertexAttributeDescriptions: attribute_descriptions.as_ptr(),
};
if self.input_assembly.primitiveRestartEnable != vk::FALSE &&
!self.input_assembly_topology.supports_primitive_restart()
{
return Err(GraphicsPipelineCreationError::PrimitiveDoesntSupportPrimitiveRestart {
primitive: self.input_assembly_topology,
});
}
if let Some(ref gs) = self.geometry_shader {
match gs.0.ty() {
GraphicsShaderType::Geometry(primitives) => {
if !primitives.matches(self.input_assembly_topology) {
return Err(GraphicsPipelineCreationError::TopologyNotMatchingGeometryShader);
}
},
_ => return Err(GraphicsPipelineCreationError::WrongShaderType),
}
}
let tessellation = match self.input_assembly_topology {
PrimitiveTopology::PatchList { vertices_per_patch } => {
if self.tessellation.is_none() {
return Err(GraphicsPipelineCreationError::InvalidPrimitiveTopology);
}
if vertices_per_patch >
device
.physical_device()
.limits()
.max_tessellation_patch_size()
{
return Err(GraphicsPipelineCreationError::MaxTessellationPatchSizeExceeded);
}
Some(vk::PipelineTessellationStateCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
patchControlPoints: vertices_per_patch,
})
},
_ => {
if self.tessellation.is_some() {
return Err(GraphicsPipelineCreationError::InvalidPrimitiveTopology);
}
None
},
};
let (vp_vp, vp_sc, vp_num) = match *self.viewport.as_ref().unwrap() {
ViewportsState::Fixed { ref data } => (data.iter()
.map(|e| e.0.clone().into_vulkan_viewport())
.collect::<SmallVec<[vk::Viewport; 4]>>(),
data.iter()
.map(|e| e.1.clone().into_vulkan_rect())
.collect::<SmallVec<[vk::Rect2D; 4]>>(),
data.len() as u32),
ViewportsState::DynamicViewports { ref scissors } => {
let num = scissors.len() as u32;
let scissors = scissors
.iter()
.map(|e| e.clone().into_vulkan_rect())
.collect::<SmallVec<[vk::Rect2D; 4]>>();
dynamic_states.push(vk::DYNAMIC_STATE_VIEWPORT);
(SmallVec::new(), scissors, num)
},
ViewportsState::DynamicScissors { ref viewports } => {
let num = viewports.len() as u32;
let viewports = viewports
.iter()
.map(|e| e.clone().into_vulkan_viewport())
.collect::<SmallVec<[vk::Viewport; 4]>>();
dynamic_states.push(vk::DYNAMIC_STATE_SCISSOR);
(viewports, SmallVec::new(), num)
},
ViewportsState::Dynamic { num } => {
dynamic_states.push(vk::DYNAMIC_STATE_VIEWPORT);
dynamic_states.push(vk::DYNAMIC_STATE_SCISSOR);
(SmallVec::new(), SmallVec::new(), num)
},
};
if vp_num > 1 && !device.enabled_features().multi_viewport {
return Err(GraphicsPipelineCreationError::MultiViewportFeatureNotEnabled);
}
if vp_num > device.physical_device().limits().max_viewports() {
return Err(GraphicsPipelineCreationError::MaxViewportsExceeded {
obtained: vp_num,
max: device.physical_device().limits().max_viewports(),
});
}
for vp in vp_vp.iter() {
if vp.width > device.physical_device().limits().max_viewport_dimensions()[0] as f32 ||
vp.height > device.physical_device().limits().max_viewport_dimensions()[1] as f32
{
return Err(GraphicsPipelineCreationError::MaxViewportDimensionsExceeded);
}
if vp.x < device.physical_device().limits().viewport_bounds_range()[0] ||
vp.x + vp.width > device.physical_device().limits().viewport_bounds_range()[1] ||
vp.y < device.physical_device().limits().viewport_bounds_range()[0] ||
vp.y + vp.height > device.physical_device().limits().viewport_bounds_range()[1]
{
return Err(GraphicsPipelineCreationError::ViewportBoundsExceeded);
}
}
let viewport_info = vk::PipelineViewportStateCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
viewportCount: vp_num,
pViewports: if vp_vp.is_empty() {
ptr::null()
} else {
vp_vp.as_ptr()
},
scissorCount: vp_num,
pScissors: if vp_sc.is_empty() {
ptr::null()
} else {
vp_sc.as_ptr()
},
};
if let Some(line_width) = self.raster.line_width {
if line_width != 1.0 && !device.enabled_features().wide_lines {
return Err(GraphicsPipelineCreationError::WideLinesFeatureNotEnabled);
}
} else {
dynamic_states.push(vk::DYNAMIC_STATE_LINE_WIDTH);
}
let (db_enable, db_const, db_clamp, db_slope) = match self.raster.depth_bias {
DepthBiasControl::Dynamic => {
dynamic_states.push(vk::DYNAMIC_STATE_DEPTH_BIAS);
(vk::TRUE, 0.0, 0.0, 0.0)
},
DepthBiasControl::Disabled => {
(vk::FALSE, 0.0, 0.0, 0.0)
},
DepthBiasControl::Static(bias) => {
if bias.clamp != 0.0 && !device.enabled_features().depth_bias_clamp {
return Err(GraphicsPipelineCreationError::DepthBiasClampFeatureNotEnabled);
}
(vk::TRUE, bias.constant_factor, bias.clamp, bias.slope_factor)
},
};
if self.raster.depth_clamp && !device.enabled_features().depth_clamp {
return Err(GraphicsPipelineCreationError::DepthClampFeatureNotEnabled);
}
if self.raster.polygon_mode != PolygonMode::Fill &&
!device.enabled_features().fill_mode_non_solid
{
return Err(GraphicsPipelineCreationError::FillModeNonSolidFeatureNotEnabled);
}
let rasterization = vk::PipelineRasterizationStateCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
depthClampEnable: if self.raster.depth_clamp {
vk::TRUE
} else {
vk::FALSE
},
rasterizerDiscardEnable: if self.raster.rasterizer_discard {
vk::TRUE
} else {
vk::FALSE
},
polygonMode: self.raster.polygon_mode as u32,
cullMode: self.raster.cull_mode as u32,
frontFace: self.raster.front_face as u32,
depthBiasEnable: db_enable,
depthBiasConstantFactor: db_const,
depthBiasClamp: db_clamp,
depthBiasSlopeFactor: db_slope,
lineWidth: self.raster.line_width.unwrap_or(1.0),
};
self.multisample.rasterizationSamples = self.render_pass
.as_ref()
.unwrap()
.num_samples()
.unwrap_or(1);
if self.multisample.sampleShadingEnable != vk::FALSE {
debug_assert!(self.multisample.minSampleShading >= 0.0 &&
self.multisample.minSampleShading <= 1.0);
if !device.enabled_features().sample_rate_shading {
return Err(GraphicsPipelineCreationError::SampleRateShadingFeatureNotEnabled);
}
}
if self.multisample.alphaToOneEnable != vk::FALSE {
if !device.enabled_features().alpha_to_one {
return Err(GraphicsPipelineCreationError::AlphaToOneFeatureNotEnabled);
}
}
let depth_stencil = {
let db = match self.depth_stencil.depth_bounds_test {
DepthBounds::Disabled => (vk::FALSE, 0.0, 0.0),
DepthBounds::Fixed(ref range) => {
if !device.enabled_features().depth_bounds {
return Err(GraphicsPipelineCreationError::DepthBoundsFeatureNotEnabled);
}
(vk::TRUE, range.start, range.end)
},
DepthBounds::Dynamic => {
if !device.enabled_features().depth_bounds {
return Err(GraphicsPipelineCreationError::DepthBoundsFeatureNotEnabled);
}
dynamic_states.push(vk::DYNAMIC_STATE_DEPTH_BOUNDS);
(vk::TRUE, 0.0, 1.0)
},
};
match (self.depth_stencil.stencil_front.compare_mask,
self.depth_stencil.stencil_back.compare_mask) {
(Some(_), Some(_)) => (),
(None, None) => {
dynamic_states.push(vk::DYNAMIC_STATE_STENCIL_COMPARE_MASK);
},
_ => return Err(GraphicsPipelineCreationError::WrongStencilState),
};
match (self.depth_stencil.stencil_front.write_mask,
self.depth_stencil.stencil_back.write_mask) {
(Some(_), Some(_)) => (),
(None, None) => {
dynamic_states.push(vk::DYNAMIC_STATE_STENCIL_WRITE_MASK);
},
_ => return Err(GraphicsPipelineCreationError::WrongStencilState),
};
match (self.depth_stencil.stencil_front.reference,
self.depth_stencil.stencil_back.reference) {
(Some(_), Some(_)) => (),
(None, None) => {
dynamic_states.push(vk::DYNAMIC_STATE_STENCIL_REFERENCE);
},
_ => return Err(GraphicsPipelineCreationError::WrongStencilState),
};
if self.depth_stencil.depth_write &&
!self.render_pass.as_ref().unwrap().has_writable_depth()
{
return Err(GraphicsPipelineCreationError::NoDepthAttachment);
}
if self.depth_stencil.depth_compare != Compare::Always &&
!self.render_pass.as_ref().unwrap().has_depth()
{
return Err(GraphicsPipelineCreationError::NoDepthAttachment);
}
if (!self.depth_stencil.stencil_front.always_keep() ||
!self.depth_stencil.stencil_back.always_keep()) &&
!self.render_pass.as_ref().unwrap().has_stencil()
{
return Err(GraphicsPipelineCreationError::NoStencilAttachment);
}
vk::PipelineDepthStencilStateCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
depthTestEnable: if !self.depth_stencil.depth_write &&
self.depth_stencil.depth_compare == Compare::Always
{
vk::FALSE
} else {
vk::TRUE
},
depthWriteEnable: if self.depth_stencil.depth_write {
vk::TRUE
} else {
vk::FALSE
},
depthCompareOp: self.depth_stencil.depth_compare as u32,
depthBoundsTestEnable: db.0,
stencilTestEnable: if self.depth_stencil.stencil_front.always_keep() &&
self.depth_stencil.stencil_back.always_keep()
{
vk::FALSE
} else {
vk::TRUE
},
front: vk::StencilOpState {
failOp: self.depth_stencil.stencil_front.fail_op as u32,
passOp: self.depth_stencil.stencil_front.pass_op as u32,
depthFailOp: self.depth_stencil.stencil_front.depth_fail_op as u32,
compareOp: self.depth_stencil.stencil_front.compare as u32,
compareMask: self.depth_stencil
.stencil_front
.compare_mask
.unwrap_or(u32::MAX),
writeMask: self.depth_stencil
.stencil_front
.write_mask
.unwrap_or(u32::MAX),
reference: self.depth_stencil.stencil_front.reference.unwrap_or(0),
},
back: vk::StencilOpState {
failOp: self.depth_stencil.stencil_back.fail_op as u32,
passOp: self.depth_stencil.stencil_back.pass_op as u32,
depthFailOp: self.depth_stencil.stencil_back.depth_fail_op as u32,
compareOp: self.depth_stencil.stencil_back.compare as u32,
compareMask: self.depth_stencil
.stencil_back
.compare_mask
.unwrap_or(u32::MAX),
writeMask: self.depth_stencil
.stencil_back
.write_mask
.unwrap_or(u32::MAX),
reference: self.depth_stencil.stencil_back.reference.unwrap_or(0),
},
minDepthBounds: db.1,
maxDepthBounds: db.2,
}
};
let blend_atch: SmallVec<[vk::PipelineColorBlendAttachmentState; 8]> = {
let num_atch = self.render_pass.as_ref().unwrap().num_color_attachments();
match self.blend.attachments {
AttachmentsBlend::Collective(blend) => {
(0 .. num_atch)
.map(|_| blend.clone().into_vulkan_state())
.collect()
},
AttachmentsBlend::Individual(blend) => {
if blend.len() != num_atch as usize {
return Err(GraphicsPipelineCreationError::MismatchBlendingAttachmentsCount);
}
if !device.enabled_features().independent_blend {
return Err(GraphicsPipelineCreationError::IndependentBlendFeatureNotEnabled);
}
blend
.iter()
.map(|b| b.clone().into_vulkan_state())
.collect()
},
}
};
let blend = vk::PipelineColorBlendStateCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
logicOpEnable: if self.blend.logic_op.is_some() {
if !device.enabled_features().logic_op {
return Err(GraphicsPipelineCreationError::LogicOpFeatureNotEnabled);
}
vk::TRUE
} else {
vk::FALSE
},
logicOp: self.blend.logic_op.unwrap_or(Default::default()) as u32,
attachmentCount: blend_atch.len() as u32,
pAttachments: blend_atch.as_ptr(),
blendConstants: if let Some(c) = self.blend.blend_constants {
c
} else {
dynamic_states.push(vk::DYNAMIC_STATE_BLEND_CONSTANTS);
[0.0, 0.0, 0.0, 0.0]
},
};
let dynamic_states = if !dynamic_states.is_empty() {
Some(vk::PipelineDynamicStateCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
dynamicStateCount: dynamic_states.len() as u32,
pDynamicStates: dynamic_states.as_ptr(),
})
} else {
None
};
let pipeline = unsafe {
let infos = vk::GraphicsPipelineCreateInfo {
sType: vk::STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
stageCount: stages.len() as u32,
pStages: stages.as_ptr(),
pVertexInputState: &vertex_input_state,
pInputAssemblyState: &self.input_assembly,
pTessellationState: tessellation
.as_ref()
.map(|t| t as *const _)
.unwrap_or(ptr::null()),
pViewportState: &viewport_info,
pRasterizationState: &rasterization,
pMultisampleState: &self.multisample,
pDepthStencilState: &depth_stencil,
pColorBlendState: &blend,
pDynamicState: dynamic_states
.as_ref()
.map(|s| s as *const _)
.unwrap_or(ptr::null()),
layout: PipelineLayoutAbstract::sys(&pipeline_layout).internal_object(),
renderPass: self.render_pass
.as_ref()
.unwrap()
.render_pass()
.inner()
.internal_object(),
subpass: self.render_pass.as_ref().unwrap().index(),
basePipelineHandle: 0,
basePipelineIndex: -1,
};
let mut output = mem::uninitialized();
check_errors(vk.CreateGraphicsPipelines(device.internal_object(),
0,
1,
&infos,
ptr::null(),
&mut output))?;
output
};
let (render_pass, render_pass_subpass) = self.render_pass.take().unwrap().into();
Ok(GraphicsPipeline {
inner: GraphicsPipelineInner {
device: device.clone(),
pipeline: pipeline,
},
layout: pipeline_layout,
vertex_definition: self.vertex_input,
render_pass: render_pass,
render_pass_subpass: render_pass_subpass,
dynamic_line_width: self.raster.line_width.is_none(),
dynamic_viewport: self.viewport.as_ref().unwrap().dynamic_viewports(),
dynamic_scissor: self.viewport.as_ref().unwrap().dynamic_scissors(),
dynamic_depth_bias: self.raster.depth_bias.is_dynamic(),
dynamic_depth_bounds: self.depth_stencil.depth_bounds_test.is_dynamic(),
dynamic_stencil_compare_mask: self.depth_stencil.stencil_back.compare_mask.is_none(),
dynamic_stencil_write_mask: self.depth_stencil.stencil_back.write_mask.is_none(),
dynamic_stencil_reference: self.depth_stencil.stencil_back.reference.is_none(),
dynamic_blend_constants: self.blend.blend_constants.is_none(),
num_viewports: self.viewport.as_ref().unwrap().num_viewports(),
})
}
}
impl<Vdef, Vs, Vss, Tcs, Tcss, Tes, Tess, Gs, Gss, Fs, Fss, Rp>
GraphicsPipelineBuilder<Vdef, Vs, Vss, Tcs, Tcss, Tes, Tess, Gs, Gss, Fs, Fss, Rp> {
#[inline]
pub fn vertex_input<T>(
self, vertex_input: T)
-> GraphicsPipelineBuilder<T, Vs, Vss, Tcs, Tcss, Tes, Tess, Gs, Gss, Fs, Fss, Rp> {
GraphicsPipelineBuilder {
vertex_input: vertex_input,
vertex_shader: self.vertex_shader,
input_assembly: self.input_assembly,
input_assembly_topology: self.input_assembly_topology,
tessellation: self.tessellation,
geometry_shader: self.geometry_shader,
viewport: self.viewport,
raster: self.raster,
multisample: self.multisample,
fragment_shader: self.fragment_shader,
depth_stencil: self.depth_stencil,
blend: self.blend,
render_pass: self.render_pass,
}
}
#[inline]
pub fn vertex_input_single_buffer<V>(self)
-> GraphicsPipelineBuilder<SingleBufferDefinition<V>,
Vs,
Vss,
Tcs,
Tcss,
Tes,
Tess,
Gs,
Gss,
Fs,
Fss,
Rp> {
self.vertex_input(SingleBufferDefinition::<V>::new())
}
#[inline]
pub fn vertex_shader<Vs2, Vss2>(
self, shader: Vs2, specialization_constants: Vss2)
-> GraphicsPipelineBuilder<Vdef, Vs2, Vss2, Tcs, Tcss, Tes, Tess, Gs, Gss, Fs, Fss, Rp>
where Vs2: GraphicsEntryPointAbstract<SpecializationConstants = Vss2>,
Vss2: SpecializationConstants
{
GraphicsPipelineBuilder {
vertex_input: self.vertex_input,
vertex_shader: Some((shader, specialization_constants)),
input_assembly: self.input_assembly,
input_assembly_topology: self.input_assembly_topology,
tessellation: self.tessellation,
geometry_shader: self.geometry_shader,
viewport: self.viewport,
raster: self.raster,
multisample: self.multisample,
fragment_shader: self.fragment_shader,
depth_stencil: self.depth_stencil,
blend: self.blend,
render_pass: self.render_pass,
}
}
#[inline]
pub fn primitive_restart(mut self, enabled: bool) -> Self {
self.input_assembly.primitiveRestartEnable = if enabled {
vk::TRUE
} else {
vk::FALSE
};
self
}
#[inline]
pub fn primitive_topology(mut self, topology: PrimitiveTopology) -> Self {
self.input_assembly_topology = topology;
self.input_assembly.topology = topology.into();
self
}
#[inline]
pub fn point_list(self) -> Self {
self.primitive_topology(PrimitiveTopology::PointList)
}
#[inline]
pub fn line_list(self) -> Self {
self.primitive_topology(PrimitiveTopology::LineList)
}
#[inline]
pub fn line_strip(self) -> Self {
self.primitive_topology(PrimitiveTopology::LineStrip)
}
#[inline]
pub fn triangle_list(self) -> Self {
self.primitive_topology(PrimitiveTopology::TriangleList)
}
#[inline]
pub fn triangle_strip(self) -> Self {
self.primitive_topology(PrimitiveTopology::TriangleStrip)
}
#[inline]
pub fn triangle_fan(self) -> Self {
self.primitive_topology(PrimitiveTopology::TriangleFan)
}
#[inline]
pub fn line_list_with_adjacency(self) -> Self {
self.primitive_topology(PrimitiveTopology::LineListWithAdjacency)
}
#[inline]
pub fn line_strip_with_adjacency(self) -> Self {
self.primitive_topology(PrimitiveTopology::LineStripWithAdjacency)
}
#[inline]
pub fn triangle_list_with_adjacency(self) -> Self {
self.primitive_topology(PrimitiveTopology::TriangleListWithAdjacency)
}
#[inline]
pub fn triangle_strip_with_adjacency(self) -> Self {
self.primitive_topology(PrimitiveTopology::TriangleStripWithAdjacency)
}
#[inline]
pub fn patch_list(self, vertices_per_patch: u32) -> Self {
self.primitive_topology(PrimitiveTopology::PatchList { vertices_per_patch })
}
#[inline]
pub fn tessellation_shaders<Tcs2, Tcss2, Tes2, Tess2>(
self, tessellation_control_shader: Tcs2,
tessellation_control_shader_spec_constants: Tcss2, tessellation_evaluation_shader: Tes2,
tessellation_evaluation_shader_spec_constants: Tess2)
-> GraphicsPipelineBuilder<Vdef, Vs, Vss, Tcs2, Tcss2, Tes2, Tess2, Gs, Gss, Fs, Fss, Rp>
where Tcs2: GraphicsEntryPointAbstract<SpecializationConstants = Tcss2>,
Tes2: GraphicsEntryPointAbstract<SpecializationConstants = Tess2>,
Tcss2: SpecializationConstants,
Tess2: SpecializationConstants
{
GraphicsPipelineBuilder {
vertex_input: self.vertex_input,
vertex_shader: self.vertex_shader,
input_assembly: self.input_assembly,
input_assembly_topology: self.input_assembly_topology,
tessellation: Some(TessInfo {
tessellation_control_shader:
(tessellation_control_shader,
tessellation_control_shader_spec_constants),
tessellation_evaluation_shader:
(tessellation_evaluation_shader,
tessellation_evaluation_shader_spec_constants),
}),
geometry_shader: self.geometry_shader,
viewport: self.viewport,
raster: self.raster,
multisample: self.multisample,
fragment_shader: self.fragment_shader,
depth_stencil: self.depth_stencil,
blend: self.blend,
render_pass: self.render_pass,
}
}
#[inline]
pub fn tessellation_shaders_disabled(mut self) -> Self {
self.tessellation = None;
self
}
#[inline]
pub fn geometry_shader<Gs2, Gss2>(
self, shader: Gs2, specialization_constants: Gss2)
-> GraphicsPipelineBuilder<Vdef, Vs, Vss, Tcs, Tcss, Tes, Tess, Gs2, Gss2, Fs, Fss, Rp>
where Gs2: GraphicsEntryPointAbstract<SpecializationConstants = Gss2>,
Gss2: SpecializationConstants
{
GraphicsPipelineBuilder {
vertex_input: self.vertex_input,
vertex_shader: self.vertex_shader,
input_assembly: self.input_assembly,
input_assembly_topology: self.input_assembly_topology,
tessellation: self.tessellation,
geometry_shader: Some((shader, specialization_constants)),
viewport: self.viewport,
raster: self.raster,
multisample: self.multisample,
fragment_shader: self.fragment_shader,
depth_stencil: self.depth_stencil,
blend: self.blend,
render_pass: self.render_pass,
}
}
#[inline]
pub fn geometry_shader_disabled(mut self) -> Self {
self.geometry_shader = None;
self
}
#[inline]
pub fn viewports<I>(self, viewports: I) -> Self
where I: IntoIterator<Item = Viewport>
{
self.viewports_scissors(viewports.into_iter().map(|v| (v, Scissor::irrelevant())))
}
#[inline]
pub fn viewports_scissors<I>(mut self, viewports: I) -> Self
where I: IntoIterator<Item = (Viewport, Scissor)>
{
self.viewport = Some(ViewportsState::Fixed { data: viewports.into_iter().collect() });
self
}
#[inline]
pub fn viewports_dynamic_scissors_fixed<I>(mut self, scissors: I) -> Self
where I: IntoIterator<Item = Scissor>
{
self.viewport =
Some(ViewportsState::DynamicViewports { scissors: scissors.into_iter().collect() });
self
}
#[inline]
pub fn viewports_dynamic_scissors_irrelevant(mut self, num: u32) -> Self {
self.viewport = Some(ViewportsState::DynamicViewports {
scissors: (0 .. num).map(|_| Scissor::irrelevant()).collect(),
});
self
}
#[inline]
pub fn viewports_fixed_scissors_dynamic<I>(mut self, viewports: I) -> Self
where I: IntoIterator<Item = Viewport>
{
self.viewport =
Some(ViewportsState::DynamicScissors { viewports: viewports.into_iter().collect() });
self
}
#[inline]
pub fn viewports_scissors_dynamic(mut self, num: u32) -> Self {
self.viewport = Some(ViewportsState::Dynamic { num: num });
self
}
#[inline]
pub fn depth_clamp(mut self, clamp: bool) -> Self {
self.raster.depth_clamp = clamp;
self
}
#[inline]
pub fn front_face_counter_clockwise(mut self) -> Self {
self.raster.front_face = FrontFace::CounterClockwise;
self
}
#[inline]
pub fn front_face_clockwise(mut self) -> Self {
self.raster.front_face = FrontFace::Clockwise;
self
}
#[inline]
pub fn cull_mode_disabled(mut self) -> Self {
self.raster.cull_mode = CullMode::None;
self
}
#[inline]
pub fn cull_mode_front(mut self) -> Self {
self.raster.cull_mode = CullMode::Front;
self
}
#[inline]
pub fn cull_mode_back(mut self) -> Self {
self.raster.cull_mode = CullMode::Back;
self
}
#[inline]
pub fn cull_mode_front_and_back(mut self) -> Self {
self.raster.cull_mode = CullMode::FrontAndBack;
self
}
#[inline]
pub fn polygon_mode_fill(mut self) -> Self {
self.raster.polygon_mode = PolygonMode::Fill;
self
}
#[inline]
pub fn polygon_mode_line(mut self) -> Self {
self.raster.polygon_mode = PolygonMode::Line;
self
}
#[inline]
pub fn polygon_mode_point(mut self) -> Self {
self.raster.polygon_mode = PolygonMode::Point;
self
}
#[inline]
pub fn line_width(mut self, value: f32) -> Self {
self.raster.line_width = Some(value);
self
}
#[inline]
pub fn line_width_dynamic(mut self) -> Self {
self.raster.line_width = None;
self
}
#[inline]
pub fn sample_shading_disabled(mut self) -> Self {
self.multisample.sampleShadingEnable = vk::FALSE;
self
}
#[inline]
pub fn sample_shading_enabled(mut self, min_fract: f32) -> Self {
assert!(min_fract >= 0.0 && min_fract <= 1.0);
self.multisample.sampleShadingEnable = vk::TRUE;
self.multisample.minSampleShading = min_fract;
self
}
pub fn alpha_to_coverage_disabled(mut self) -> Self {
self.multisample.alphaToCoverageEnable = vk::FALSE;
self
}
pub fn alpha_to_coverage_enabled(mut self) -> Self {
self.multisample.alphaToCoverageEnable = vk::TRUE;
self
}
#[inline]
pub fn alpha_to_one_disabled(mut self) -> Self {
self.multisample.alphaToOneEnable = vk::FALSE;
self
}
#[inline]
pub fn alpha_to_one_enabled(mut self) -> Self {
self.multisample.alphaToOneEnable = vk::TRUE;
self
}
#[inline]
pub fn fragment_shader<Fs2, Fss2>(
self, shader: Fs2, specialization_constants: Fss2)
-> GraphicsPipelineBuilder<Vdef, Vs, Vss, Tcs, Tcss, Tes, Tess, Gs, Gss, Fs2, Fss2, Rp>
where Fs2: GraphicsEntryPointAbstract<SpecializationConstants = Fss2>,
Fss2: SpecializationConstants
{
GraphicsPipelineBuilder {
vertex_input: self.vertex_input,
vertex_shader: self.vertex_shader,
input_assembly: self.input_assembly,
input_assembly_topology: self.input_assembly_topology,
tessellation: self.tessellation,
geometry_shader: self.geometry_shader,
viewport: self.viewport,
raster: self.raster,
multisample: self.multisample,
fragment_shader: Some((shader, specialization_constants)),
depth_stencil: self.depth_stencil,
blend: self.blend,
render_pass: self.render_pass,
}
}
#[inline]
pub fn depth_stencil(mut self, depth_stencil: DepthStencil) -> Self {
self.depth_stencil = depth_stencil;
self
}
#[inline]
pub fn depth_stencil_disabled(mut self) -> Self {
self.depth_stencil = DepthStencil::disabled();
self
}
#[inline]
pub fn depth_stencil_simple_depth(mut self) -> Self {
self.depth_stencil = DepthStencil::simple_depth_test();
self
}
#[inline]
pub fn depth_write(mut self, write: bool) -> Self {
self.depth_stencil.depth_write = write;
self
}
#[inline]
pub fn blend_collective(mut self, blend: AttachmentBlend) -> Self {
self.blend.attachments = AttachmentsBlend::Collective(blend);
self
}
#[inline]
pub fn blend_individual<I>(mut self, blend: I) -> Self
where I: IntoIterator<Item = AttachmentBlend>
{
self.blend.attachments = AttachmentsBlend::Individual(blend.into_iter().collect());
self
}
#[inline]
pub fn blend_pass_through(self) -> Self {
self.blend_collective(AttachmentBlend::pass_through())
}
#[inline]
pub fn blend_alpha_blending(self) -> Self {
self.blend_collective(AttachmentBlend::alpha_blending())
}
#[inline]
pub fn blend_logic_op(mut self, logic_op: LogicOp) -> Self {
self.blend.logic_op = Some(logic_op);
self
}
#[inline]
pub fn blend_logic_op_disabled(mut self) -> Self {
self.blend.logic_op = None;
self
}
#[inline]
pub fn blend_constants(mut self, constants: [f32; 4]) -> Self {
self.blend.blend_constants = Some(constants);
self
}
#[inline]
pub fn blend_constants_dynamic(mut self) -> Self {
self.blend.blend_constants = None;
self
}
#[inline]
pub fn render_pass<Rp2>(
self, subpass: Subpass<Rp2>)
-> GraphicsPipelineBuilder<Vdef, Vs, Vss, Tcs, Tcss, Tes, Tess, Gs, Gss, Fs, Fss, Rp2> {
GraphicsPipelineBuilder {
vertex_input: self.vertex_input,
vertex_shader: self.vertex_shader,
input_assembly: self.input_assembly,
input_assembly_topology: self.input_assembly_topology,
tessellation: self.tessellation,
geometry_shader: self.geometry_shader,
viewport: self.viewport,
raster: self.raster,
multisample: self.multisample,
fragment_shader: self.fragment_shader,
depth_stencil: self.depth_stencil,
blend: self.blend,
render_pass: Some(subpass),
}
}
}
impl<Vdef, Vs, Vss, Tcs, Tcss, Tes, Tess, Gs, Gss, Fs, Fss, Rp> Clone
for GraphicsPipelineBuilder<Vdef, Vs, Vss, Tcs, Tcss, Tes, Tess, Gs, Gss, Fs, Fss, Rp>
where Vdef: Clone,
Vs: Clone,
Vss: Clone,
Tcs: Clone,
Tcss: Clone,
Tes: Clone,
Tess: Clone,
Gs: Clone,
Gss: Clone,
Fs: Clone,
Fss: Clone,
Rp: Clone
{
fn clone(&self) -> Self {
GraphicsPipelineBuilder {
vertex_input: self.vertex_input.clone(),
vertex_shader: self.vertex_shader.clone(),
input_assembly: unsafe { ptr::read(&self.input_assembly) },
input_assembly_topology: self.input_assembly_topology,
tessellation: self.tessellation.clone(),
geometry_shader: self.geometry_shader.clone(),
viewport: self.viewport.clone(),
raster: self.raster.clone(),
multisample: vk::PipelineMultisampleStateCreateInfo {
sType: self.multisample.sType,
pNext: self.multisample.pNext,
flags: self.multisample.flags,
rasterizationSamples: self.multisample.rasterizationSamples,
sampleShadingEnable: self.multisample.sampleShadingEnable,
minSampleShading: self.multisample.minSampleShading,
pSampleMask: self.multisample.pSampleMask,
alphaToCoverageEnable: self.multisample.alphaToCoverageEnable,
alphaToOneEnable: self.multisample.alphaToOneEnable,
},
fragment_shader: self.fragment_shader.clone(),
depth_stencil: self.depth_stencil.clone(),
blend: self.blend.clone(),
render_pass: self.render_pass.clone(),
}
}
}