Continue cleanup of the graphics pipeline builder code (#777)

* Move GraphicsPipeline::new_inner() as GraphicsPipelineBuilder::build()

* Move GraphicsPipelineCreationError to a separate module

* Remove GraphicsPipelineParams

Signed-off-by: tomaka <pierre.krieger1708@gmail.com>

* Remove GraphicsPipelineParamsTess

Signed-off-by: tomaka <pierre.krieger1708@gmail.com>
This commit is contained in:
tomaka 2017-08-26 13:06:05 +02:00 committed by GitHub
parent 5207f8a703
commit a8058022bd
3 changed files with 1218 additions and 1377 deletions

View File

@ -11,36 +11,50 @@
// to avoid duplicating code, so we hide the warnings for now
#![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::RenderPassSubpassInterface;
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::Inner as GraphicsPipelineInner;
use pipeline::graphics_pipeline::GraphicsPipelineCreationError;
use pipeline::graphics_pipeline::GraphicsPipelineParams;
use pipeline::graphics_pipeline::GraphicsPipelineParamsTess;
use pipeline::input_assembly::InputAssembly;
use pipeline::input_assembly::PrimitiveTopology;
use pipeline::multisample::Multisample;
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::vertex::SingleBufferDefinition;
use pipeline::vertex::VertexDefinition;
use pipeline::viewport::Scissor;
use pipeline::viewport::Viewport;
use pipeline::viewport::ViewportsState;
use std::sync::Arc;
use VulkanObject;
use check_errors;
use descriptor::pipeline_layout::PipelineLayoutDesc;
use descriptor::pipeline_layout::PipelineLayoutSuperset;
use framebuffer::RenderPassSubpassInterface;
use vk;
/// Prototype for a `GraphicsPipeline`.
// TODO: we can optimize this by filling directly the raw vk structs
@ -56,7 +70,7 @@ pub struct GraphicsPipelineBuilder<
vertex_input: Vdef,
vertex_shader: Option<Vs>,
input_assembly: InputAssembly,
tessellation: Option<GraphicsPipelineParamsTess<Tcs, Tes>>,
tessellation: Option<TessInfo<Tcs, Tes>>,
geometry_shader: Option<Gs>,
viewport: Option<ViewportsState>,
raster: Rasterization,
@ -67,6 +81,13 @@ pub struct GraphicsPipelineBuilder<
render_pass: Option<Subpass<Rp>>,
}
// Additional parameters if tessellation is used.
#[derive(Copy, Clone)]
struct TessInfo<Tcs, Tes> {
tessellation_control_shader: Tcs,
tessellation_evaluation_shader: Tes,
}
impl GraphicsPipelineBuilder<SingleBufferDefinition<()>,
EmptyEntryPointDummy,
EmptyEntryPointDummy,
@ -128,41 +149,825 @@ impl<Vdef,
{
/// Builds the graphics pipeline.
// TODO: replace Box<PipelineLayoutAbstract> with a PipelineUnion struct without template params
pub fn build(self, device: Arc<Device>)
pub fn build(mut self, device: Arc<Device>)
-> Result<GraphicsPipeline<Vdef, Box<PipelineLayoutAbstract + Send + Sync>, Rp>,
GraphicsPipelineCreationError> {
// TODO: return errors instead of panicking if missing param
GraphicsPipeline::with_tessellation_and_geometry(device,
GraphicsPipelineParams {
vertex_input: self.vertex_input,
vertex_shader:
self.vertex_shader
.expect("Vertex shader not \
specified in the \
builder"),
input_assembly: self.input_assembly,
tessellation: self.tessellation,
geometry_shader: self.geometry_shader,
viewport:
self.viewport
.expect("Viewport state not \
specified in the \
builder"),
raster: self.raster,
multisample: self.multisample,
fragment_shader:
self.fragment_shader
.expect("Fragment shader not \
specified in the \
builder"),
depth_stencil: self.depth_stencil,
blend: self.blend,
render_pass:
self.render_pass
.expect("Render pass not \
specified in the \
builder"),
})
let vk = device.pointers();
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
.input()
.matches(self.vertex_shader.as_ref().unwrap().output())
{
return Err(GraphicsPipelineCreationError::VertexTessControlStagesMismatch(err));
}
if let Err(err) = tess.tessellation_evaluation_shader
.input()
.matches(tess.tessellation_control_shader.output())
{
return Err(GraphicsPipelineCreationError::TessControlTessEvalStagesMismatch(err));
}
if let Err(err) = gs.input()
.matches(tess.tessellation_evaluation_shader.output())
{
return Err(GraphicsPipelineCreationError::TessEvalGeometryStagesMismatch(err));
}
if let Err(err) = self.fragment_shader.as_ref().unwrap().input().matches(gs.output()) {
return Err(GraphicsPipelineCreationError::GeometryFragmentStagesMismatch(err));
}
pipeline_layout = Box::new(self.vertex_shader.as_ref().unwrap().layout().clone()
.union(self.fragment_shader.as_ref().unwrap().layout().clone())
.union(self.tessellation.as_ref().unwrap().tessellation_control_shader.layout().clone()) // FIXME: unwrap()
.union(self.tessellation.as_ref().unwrap().tessellation_evaluation_shader.layout().clone()) // FIXME: unwrap()
.union(self.geometry_shader.as_ref().unwrap().layout().clone()) // FIXME: unwrap()
.build(device.clone()).unwrap()) as Box<_>; // TODO: error
} else {
if let Err(err) = tess.tessellation_control_shader
.input()
.matches(self.vertex_shader.as_ref().unwrap().output())
{
return Err(GraphicsPipelineCreationError::VertexTessControlStagesMismatch(err));
}
if let Err(err) = tess.tessellation_evaluation_shader
.input()
.matches(tess.tessellation_control_shader.output())
{
return Err(GraphicsPipelineCreationError::TessControlTessEvalStagesMismatch(err));
}
if let Err(err) = self
.fragment_shader
.as_ref().unwrap()
.input()
.matches(tess.tessellation_evaluation_shader.output())
{
return Err(GraphicsPipelineCreationError::TessEvalFragmentStagesMismatch(err));
}
pipeline_layout = Box::new(self.vertex_shader.as_ref().unwrap().layout().clone()
.union(self.fragment_shader.as_ref().unwrap().layout().clone())
.union(self.tessellation.as_ref().unwrap().tessellation_control_shader.layout().clone()) // FIXME: unwrap()
.union(self.tessellation.as_ref().unwrap().tessellation_evaluation_shader.layout().clone()) // FIXME: unwrap()
.build(device.clone()).unwrap()) as Box<_>; // TODO: error
}
} else {
if let Some(ref geometry_shader) = self.geometry_shader {
if let Err(err) = geometry_shader
.input()
.matches(self.vertex_shader.as_ref().unwrap().output())
{
return Err(GraphicsPipelineCreationError::VertexGeometryStagesMismatch(err));
}
if let Err(err) = self
.fragment_shader
.as_ref().unwrap()
.input()
.matches(geometry_shader.output())
{
return Err(GraphicsPipelineCreationError::GeometryFragmentStagesMismatch(err));
}
pipeline_layout = Box::new(self.vertex_shader.as_ref().unwrap().layout().clone()
.union(self.fragment_shader.as_ref().unwrap().layout().clone())
.union(self.geometry_shader.as_ref().unwrap().layout().clone()) // FIXME: unwrap()
.build(device.clone()).unwrap()) as Box<_>; // TODO: error
} else {
if let Err(err) = self
.fragment_shader
.as_ref().unwrap()
.input()
.matches(self.vertex_shader.as_ref().unwrap().output())
{
return Err(GraphicsPipelineCreationError::VertexFragmentStagesMismatch(err));
}
pipeline_layout = Box::new(self
.vertex_shader
.as_ref().unwrap()
.layout()
.clone()
.union(self.fragment_shader.as_ref().unwrap().layout().clone())
.build(device.clone())
.unwrap()) as Box<_>; // TODO: error
}
}
// Checking that the pipeline layout matches the shader stages.
// TODO: more details in the errors
PipelineLayoutSuperset::ensure_superset_of(&pipeline_layout,
self.vertex_shader.as_ref().unwrap().layout())?;
PipelineLayoutSuperset::ensure_superset_of(&pipeline_layout,
self.fragment_shader.as_ref().unwrap().layout())?;
if let Some(ref geometry_shader) = self.geometry_shader {
PipelineLayoutSuperset::ensure_superset_of(&pipeline_layout, geometry_shader.layout())?;
}
if let Some(ref tess) = self.tessellation {
PipelineLayoutSuperset::ensure_superset_of(&pipeline_layout,
tess.tessellation_control_shader.layout())?;
PipelineLayoutSuperset::ensure_superset_of(&pipeline_layout,
tess.tessellation_evaluation_shader
.layout())?;
}
// Check that the subpass can accept the output of the fragment shader.
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().output())
{
return Err(GraphicsPipelineCreationError::FragmentShaderRenderPassIncompatible);
}
// Will contain the list of dynamic states. Filled throughout this function.
let mut dynamic_states: SmallVec<[vk::DynamicState; 8]> = SmallVec::new();
// List of shader stages.
let stages = {
let mut stages = SmallVec::<[_; 5]>::new();
match self.vertex_shader.as_ref().unwrap().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, // reserved
stage: vk::SHADER_STAGE_VERTEX_BIT,
module: self.vertex_shader.as_ref().unwrap().module().internal_object(),
pName: self.vertex_shader.as_ref().unwrap().name().as_ptr(),
pSpecializationInfo: ptr::null(), // TODO:
});
match self.fragment_shader.as_ref().unwrap().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, // reserved
stage: vk::SHADER_STAGE_FRAGMENT_BIT,
module: self.fragment_shader.as_ref().unwrap().module().internal_object(),
pName: self.fragment_shader.as_ref().unwrap().name().as_ptr(),
pSpecializationInfo: ptr::null(), // TODO:
});
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, // reserved
stage: vk::SHADER_STAGE_GEOMETRY_BIT,
module: gs.module().internal_object(),
pName: gs.name().as_ptr(),
pSpecializationInfo: ptr::null(), // TODO:
});
}
if let Some(ref tess) = self.tessellation {
// FIXME: must check that the control shader and evaluation shader are compatible
if !device.enabled_features().tessellation_shader {
return Err(GraphicsPipelineCreationError::TessellationShaderFeatureNotEnabled);
}
match tess.tessellation_control_shader.ty() {
GraphicsShaderType::TessellationControl => {},
_ => return Err(GraphicsPipelineCreationError::WrongShaderType),
};
match tess.tessellation_evaluation_shader.ty() {
GraphicsShaderType::TessellationControl => {},
_ => return Err(GraphicsPipelineCreationError::WrongShaderType),
};
stages.push(vk::PipelineShaderStageCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
pNext: ptr::null(),
flags: 0, // reserved
stage: vk::SHADER_STAGE_TESSELLATION_CONTROL_BIT,
module: tess.tessellation_control_shader.module().internal_object(),
pName: tess.tessellation_control_shader.name().as_ptr(),
pSpecializationInfo: ptr::null(), // TODO:
});
stages.push(vk::PipelineShaderStageCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
pNext: ptr::null(),
flags: 0, // reserved
stage: vk::SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
module: tess.tessellation_evaluation_shader
.module()
.internal_object(),
pName: tess.tessellation_evaluation_shader.name().as_ptr(),
pSpecializationInfo: ptr::null(), // TODO:
});
}
stages
};
// Vertex bindings.
let (binding_descriptions, attribute_descriptions) = {
let (buffers_iter, attribs_iter) =
self
.vertex_input
.definition(self.vertex_shader.as_ref().unwrap().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 {
// TODO: check attribute format support
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, // reserved
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.primitive_restart_enable &&
!self.input_assembly.topology.supports_primitive_restart()
{
return Err(GraphicsPipelineCreationError::PrimitiveDoesntSupportPrimitiveRestart {
primitive: self.input_assembly.topology,
});
}
// TODO: should check from the tess eval shader instead of the input assembly
if let Some(ref gs) = self.geometry_shader {
match gs.ty() {
GraphicsShaderType::Geometry(primitives) => {
if !primitives.matches(self.input_assembly.topology) {
return Err(GraphicsPipelineCreationError::TopologyNotMatchingGeometryShader);
}
},
_ => return Err(GraphicsPipelineCreationError::WrongShaderType),
}
}
let input_assembly = vk::PipelineInputAssemblyStateCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
pNext: ptr::null(),
flags: 0, // reserved
topology: self.input_assembly.topology.into(),
primitiveRestartEnable: if self.input_assembly.primitive_restart_enable {
vk::TRUE
} else {
vk::FALSE
},
};
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, // reserved,
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, // reserved
viewportCount: vp_num,
pViewports: if vp_vp.is_empty() {
ptr::null()
} else {
vp_vp.as_ptr()
}, // validation layer crashes if you just pass the pointer
scissorCount: vp_num,
pScissors: if vp_sc.is_empty() {
ptr::null()
} else {
vp_sc.as_ptr()
}, // validation layer crashes if you just pass the pointer
};
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, // reserved
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),
};
assert!(self.multisample.rasterization_samples >= 1);
// FIXME: check that rasterization_samples is equal to what's in the renderpass
if let Some(s) = self.multisample.sample_shading {
assert!(s >= 0.0 && s <= 1.0);
}
let multisample = vk::PipelineMultisampleStateCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
pNext: ptr::null(),
flags: 0, // reserved
rasterizationSamples: self.multisample.rasterization_samples,
sampleShadingEnable: if self.multisample.sample_shading.is_some() {
vk::TRUE
} else {
vk::FALSE
},
minSampleShading: self.multisample.sample_shading.unwrap_or(1.0),
pSampleMask: ptr::null(), //self.multisample.sample_mask.as_ptr(), // FIXME:
alphaToCoverageEnable: if self.multisample.alpha_to_coverage {
vk::TRUE
} else {
vk::FALSE
},
alphaToOneEnable: if self.multisample.alpha_to_one {
vk::TRUE
} else {
vk::FALSE
},
};
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);
}
// FIXME: stencil writability
vk::PipelineDepthStencilStateCreateInfo {
sType: vk::STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
pNext: ptr::null(),
flags: 0, // reserved
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, // reserved
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, // reserved
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, // TODO: some flags are available but none are critical
stageCount: stages.len() as u32,
pStages: stages.as_ptr(),
pVertexInputState: &vertex_input_state,
pInputAssemblyState: &input_assembly,
pTessellationState: tessellation
.as_ref()
.map(|t| t as *const _)
.unwrap_or(ptr::null()),
pViewportState: &viewport_info,
pRasterizationState: &rasterization,
pMultisampleState: &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, // TODO:
basePipelineIndex: -1, // TODO:
};
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(),
})
}
// TODO: add build_with_cache method
@ -387,7 +1192,7 @@ impl<Vdef,
vertex_input: self.vertex_input,
vertex_shader: self.vertex_shader,
input_assembly: self.input_assembly,
tessellation: Some(GraphicsPipelineParamsTess {
tessellation: Some(TessInfo {
tessellation_control_shader: tessellation_control_shader,
tessellation_evaluation_shader: tessellation_evaluation_shader,
}),

View File

@ -0,0 +1,372 @@
// Copyright (c) 2017 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use std::error;
use std::fmt;
use std::u32;
use Error;
use OomError;
use descriptor::pipeline_layout::PipelineLayoutNotSupersetError;
use pipeline::input_assembly::PrimitiveTopology;
use pipeline::shader::ShaderInterfaceMismatchError;
use pipeline::vertex::IncompatibleVertexDefinitionError;
/// Error that can happen when creating a graphics pipeline.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GraphicsPipelineCreationError {
/// Not enough memory.
OomError(OomError),
/// The pipeline layout is not compatible with what the shaders expect.
IncompatiblePipelineLayout(PipelineLayoutNotSupersetError),
/// The interface between the vertex shader and the geometry shader mismatches.
VertexGeometryStagesMismatch(ShaderInterfaceMismatchError),
/// The interface between the vertex shader and the tessellation control shader mismatches.
VertexTessControlStagesMismatch(ShaderInterfaceMismatchError),
/// The interface between the vertex shader and the fragment shader mismatches.
VertexFragmentStagesMismatch(ShaderInterfaceMismatchError),
/// The interface between the tessellation control shader and the tessellation evaluation
/// shader mismatches.
TessControlTessEvalStagesMismatch(ShaderInterfaceMismatchError),
/// The interface between the tessellation evaluation shader and the geometry shader
/// mismatches.
TessEvalGeometryStagesMismatch(ShaderInterfaceMismatchError),
/// The interface between the tessellation evaluation shader and the fragment shader
/// mismatches.
TessEvalFragmentStagesMismatch(ShaderInterfaceMismatchError),
/// The interface between the geometry shader and the fragment shader mismatches.
GeometryFragmentStagesMismatch(ShaderInterfaceMismatchError),
/// The output of the fragment shader is not compatible with what the render pass subpass
/// expects.
FragmentShaderRenderPassIncompatible,
/// The vertex definition is not compatible with the input of the vertex shader.
IncompatibleVertexDefinition(IncompatibleVertexDefinitionError),
/// The maximum stride value for vertex input (ie. the distance between two vertex elements)
/// has been exceeded.
MaxVertexInputBindingStrideExceeded {
/// Index of the faulty binding.
binding: usize,
/// Maximum allowed value.
max: usize,
/// Value that was passed.
obtained: usize,
},
/// The maximum number of vertex sources has been exceeded.
MaxVertexInputBindingsExceeded {
/// Maximum allowed value.
max: usize,
/// Value that was passed.
obtained: usize,
},
/// The maximum offset for a vertex attribute has been exceeded. This means that your vertex
/// struct is too large.
MaxVertexInputAttributeOffsetExceeded {
/// Maximum allowed value.
max: usize,
/// Value that was passed.
obtained: usize,
},
/// The maximum number of vertex attributes has been exceeded.
MaxVertexInputAttributesExceeded {
/// Maximum allowed value.
max: usize,
/// Value that was passed.
obtained: usize,
},
/// The user requested to use primitive restart, but the primitive topology doesn't support it.
PrimitiveDoesntSupportPrimitiveRestart {
/// The topology that doesn't support primitive restart.
primitive: PrimitiveTopology,
},
/// The `multi_viewport` feature must be enabled in order to use multiple viewports at once.
MultiViewportFeatureNotEnabled,
/// The maximum number of viewports has been exceeded.
MaxViewportsExceeded {
/// Maximum allowed value.
max: u32,
/// Value that was passed.
obtained: u32,
},
/// The maximum dimensions of viewports has been exceeded.
MaxViewportDimensionsExceeded,
/// The minimum or maximum bounds of viewports have been exceeded.
ViewportBoundsExceeded,
/// The `wide_lines` feature must be enabled in order to use a line width superior to 1.0.
WideLinesFeatureNotEnabled,
/// The `depth_clamp` feature must be enabled in order to use depth clamping.
DepthClampFeatureNotEnabled,
/// The `depth_bias_clamp` feature must be enabled in order to use a depth bias clamp different
/// from 0.0.
DepthBiasClampFeatureNotEnabled,
/// The `fill_mode_non_solid` feature must be enabled in order to use a polygon mode different
/// from `Fill`.
FillModeNonSolidFeatureNotEnabled,
/// The `depth_bounds` feature must be enabled in order to use depth bounds testing.
DepthBoundsFeatureNotEnabled,
/// The requested stencil test is invalid.
WrongStencilState,
/// The primitives topology does not match what the geometry shader expects.
TopologyNotMatchingGeometryShader,
/// The `geometry_shader` feature must be enabled in order to use geometry shaders.
GeometryShaderFeatureNotEnabled,
/// The `tessellation_shader` feature must be enabled in order to use tessellation shaders.
TessellationShaderFeatureNotEnabled,
/// The number of attachments specified in the blending does not match the number of
/// attachments in the subpass.
MismatchBlendingAttachmentsCount,
/// The `independent_blend` feature must be enabled in order to use different blending
/// operations per attachment.
IndependentBlendFeatureNotEnabled,
/// The `logic_op` feature must be enabled in order to use logic operations.
LogicOpFeatureNotEnabled,
/// The depth test requires a depth attachment but render pass has no depth attachment, or
/// depth writing is enabled and the depth attachment is read-only.
NoDepthAttachment,
/// The stencil test requires a stencil attachment but render pass has no stencil attachment, or
/// stencil writing is enabled and the stencil attachment is read-only.
NoStencilAttachment,
/// Tried to use a patch list without a tessellation shader, or a non-patch-list with a
/// tessellation shader.
InvalidPrimitiveTopology,
/// The `maxTessellationPatchSize` limit was exceeded.
MaxTessellationPatchSizeExceeded,
/// The wrong type of shader has been passed.
///
/// For example you passed a vertex shader as the fragment shader.
WrongShaderType,
}
impl error::Error for GraphicsPipelineCreationError {
#[inline]
// TODO: finish
fn description(&self) -> &str {
match *self {
GraphicsPipelineCreationError::OomError(_) => "not enough memory available",
GraphicsPipelineCreationError::VertexGeometryStagesMismatch(_) => {
"the interface between the vertex shader and the geometry shader mismatches"
},
GraphicsPipelineCreationError::VertexTessControlStagesMismatch(_) => {
"the interface between the vertex shader and the tessellation control shader \
mismatches"
},
GraphicsPipelineCreationError::VertexFragmentStagesMismatch(_) => {
"the interface between the vertex shader and the fragment shader mismatches"
},
GraphicsPipelineCreationError::TessControlTessEvalStagesMismatch(_) => {
"the interface between the tessellation control shader and the tessellation \
evaluation shader mismatches"
},
GraphicsPipelineCreationError::TessEvalGeometryStagesMismatch(_) => {
"the interface between the tessellation evaluation shader and the geometry \
shader mismatches"
},
GraphicsPipelineCreationError::TessEvalFragmentStagesMismatch(_) => {
"the interface between the tessellation evaluation shader and the fragment \
shader mismatches"
},
GraphicsPipelineCreationError::GeometryFragmentStagesMismatch(_) => {
"the interface between the geometry shader and the fragment shader mismatches"
},
GraphicsPipelineCreationError::IncompatiblePipelineLayout(_) => {
"the pipeline layout is not compatible with what the shaders expect"
},
GraphicsPipelineCreationError::FragmentShaderRenderPassIncompatible => {
"the output of the fragment shader is not compatible with what the render pass \
subpass expects"
},
GraphicsPipelineCreationError::IncompatibleVertexDefinition(_) => {
"the vertex definition is not compatible with the input of the vertex shader"
},
GraphicsPipelineCreationError::MaxVertexInputBindingStrideExceeded { .. } => {
"the maximum stride value for vertex input (ie. the distance between two vertex \
elements) has been exceeded"
},
GraphicsPipelineCreationError::MaxVertexInputBindingsExceeded { .. } => {
"the maximum number of vertex sources has been exceeded"
},
GraphicsPipelineCreationError::MaxVertexInputAttributeOffsetExceeded { .. } => {
"the maximum offset for a vertex attribute has been exceeded"
},
GraphicsPipelineCreationError::MaxVertexInputAttributesExceeded { .. } => {
"the maximum number of vertex attributes has been exceeded"
},
GraphicsPipelineCreationError::PrimitiveDoesntSupportPrimitiveRestart { .. } => {
"the user requested to use primitive restart, but the primitive topology \
doesn't support it"
},
GraphicsPipelineCreationError::MultiViewportFeatureNotEnabled => {
"the `multi_viewport` feature must be enabled in order to use multiple viewports \
at once"
},
GraphicsPipelineCreationError::MaxViewportsExceeded { .. } => {
"the maximum number of viewports has been exceeded"
},
GraphicsPipelineCreationError::MaxViewportDimensionsExceeded => {
"the maximum dimensions of viewports has been exceeded"
},
GraphicsPipelineCreationError::ViewportBoundsExceeded => {
"the minimum or maximum bounds of viewports have been exceeded"
},
GraphicsPipelineCreationError::WideLinesFeatureNotEnabled => {
"the `wide_lines` feature must be enabled in order to use a line width \
superior to 1.0"
},
GraphicsPipelineCreationError::DepthClampFeatureNotEnabled => {
"the `depth_clamp` feature must be enabled in order to use depth clamping"
},
GraphicsPipelineCreationError::DepthBiasClampFeatureNotEnabled => {
"the `depth_bias_clamp` feature must be enabled in order to use a depth bias \
clamp different from 0.0."
},
GraphicsPipelineCreationError::FillModeNonSolidFeatureNotEnabled => {
"the `fill_mode_non_solid` feature must be enabled in order to use a polygon mode \
different from `Fill`"
},
GraphicsPipelineCreationError::DepthBoundsFeatureNotEnabled => {
"the `depth_bounds` feature must be enabled in order to use depth bounds testing"
},
GraphicsPipelineCreationError::WrongStencilState => {
"the requested stencil test is invalid"
},
GraphicsPipelineCreationError::TopologyNotMatchingGeometryShader => {
"the primitives topology does not match what the geometry shader expects"
},
GraphicsPipelineCreationError::GeometryShaderFeatureNotEnabled => {
"the `geometry_shader` feature must be enabled in order to use geometry shaders"
},
GraphicsPipelineCreationError::TessellationShaderFeatureNotEnabled => {
"the `tessellation_shader` feature must be enabled in order to use tessellation \
shaders"
},
GraphicsPipelineCreationError::MismatchBlendingAttachmentsCount => {
"the number of attachments specified in the blending does not match the number of \
attachments in the subpass"
},
GraphicsPipelineCreationError::IndependentBlendFeatureNotEnabled => {
"the `independent_blend` feature must be enabled in order to use different \
blending operations per attachment"
},
GraphicsPipelineCreationError::LogicOpFeatureNotEnabled => {
"the `logic_op` feature must be enabled in order to use logic operations"
},
GraphicsPipelineCreationError::NoDepthAttachment => {
"the depth attachment of the render pass does not match the depth test"
},
GraphicsPipelineCreationError::NoStencilAttachment => {
"the stencil attachment of the render pass does not match the stencil test"
},
GraphicsPipelineCreationError::InvalidPrimitiveTopology => {
"trying to use a patch list without a tessellation shader, or a non-patch-list \
with a tessellation shader"
},
GraphicsPipelineCreationError::MaxTessellationPatchSizeExceeded => {
"the maximum tessellation patch size was exceeded"
},
GraphicsPipelineCreationError::WrongShaderType => {
"the wrong type of shader has been passed"
},
}
}
#[inline]
fn cause(&self) -> Option<&error::Error> {
match *self {
GraphicsPipelineCreationError::OomError(ref err) => Some(err),
GraphicsPipelineCreationError::IncompatiblePipelineLayout(ref err) => Some(err),
GraphicsPipelineCreationError::VertexGeometryStagesMismatch(ref err) => Some(err),
GraphicsPipelineCreationError::VertexTessControlStagesMismatch(ref err) => Some(err),
GraphicsPipelineCreationError::VertexFragmentStagesMismatch(ref err) => Some(err),
GraphicsPipelineCreationError::TessControlTessEvalStagesMismatch(ref err) => Some(err),
GraphicsPipelineCreationError::TessEvalGeometryStagesMismatch(ref err) => Some(err),
GraphicsPipelineCreationError::TessEvalFragmentStagesMismatch(ref err) => Some(err),
GraphicsPipelineCreationError::GeometryFragmentStagesMismatch(ref err) => Some(err),
GraphicsPipelineCreationError::IncompatibleVertexDefinition(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for GraphicsPipelineCreationError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self))
}
}
impl From<OomError> for GraphicsPipelineCreationError {
#[inline]
fn from(err: OomError) -> GraphicsPipelineCreationError {
GraphicsPipelineCreationError::OomError(err)
}
}
impl From<PipelineLayoutNotSupersetError> for GraphicsPipelineCreationError {
#[inline]
fn from(err: PipelineLayoutNotSupersetError) -> GraphicsPipelineCreationError {
GraphicsPipelineCreationError::IncompatiblePipelineLayout(err)
}
}
impl From<IncompatibleVertexDefinitionError> for GraphicsPipelineCreationError {
#[inline]
fn from(err: IncompatibleVertexDefinitionError) -> GraphicsPipelineCreationError {
GraphicsPipelineCreationError::IncompatibleVertexDefinition(err)
}
}
impl From<Error> for GraphicsPipelineCreationError {
#[inline]
fn from(err: Error) -> GraphicsPipelineCreationError {
match err {
err @ Error::OutOfHostMemory => {
GraphicsPipelineCreationError::OomError(OomError::from(err))
},
err @ Error::OutOfDeviceMemory => {
GraphicsPipelineCreationError::OomError(OomError::from(err))
},
_ => panic!("unexpected error: {:?}", err),
}
}
}

File diff suppressed because it is too large Load Diff