Track state in the pipeline, invalidate dynamic state when binding pipeline with fixed state (#1722)

* Track state in the pipeline, invalidate dynamic state when binding pipeline with fixed state

* Rename `Compare` to `CompareOp`, rearrange builder a little

* Nicer and more failsafe command buffer dynamic state checking
This commit is contained in:
Rua 2021-10-03 08:17:05 +02:00 committed by GitHub
parent 6b17090b4c
commit 464fc0d549
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 541 additions and 257 deletions

View File

@ -58,6 +58,7 @@ use crate::pipeline::vertex::VertexBuffersCollection;
use crate::pipeline::viewport::Scissor;
use crate::pipeline::viewport::Viewport;
use crate::pipeline::ComputePipeline;
use crate::pipeline::DynamicState;
use crate::pipeline::GraphicsPipeline;
use crate::pipeline::PipelineBindPoint;
use crate::query::QueryControlFlags;
@ -1773,16 +1774,34 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
self
}
// Helper function for dynamic state setting.
fn has_fixed_state(&self, state: DynamicState) -> bool {
self.state()
.pipeline_graphics()
.map(|pipeline| {
matches!(
pipeline.dynamic_state(state),
Some(crate::pipeline::DynamicStateMode::Fixed)
)
})
.unwrap_or(false)
}
/// Sets the dynamic blend constants for future draw calls.
///
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
pub fn set_blend_constants(&mut self, constants: [f32; 4]) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
!self.has_fixed_state(DynamicState::BlendConstants),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner.set_blend_constants(constants);
@ -1796,6 +1815,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
/// - If the
/// [`ext_depth_range_unrestricted`](crate::device::DeviceExtensions::ext_depth_range_unrestricted)
/// device extension is not enabled, panics if `min` or `max` is not between 0.0 and 1.0 inclusive.
@ -1804,6 +1824,10 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
!self.has_fixed_state(DynamicState::DepthBounds),
"the currently bound graphics pipeline must not contain this state internally"
);
if !self
.device()
@ -1828,6 +1852,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
/// - If the [`wide_lines`](crate::device::Features::wide_lines) feature is not enabled, panics
/// if `line_width` is not 1.0.
pub fn set_line_width(&mut self, line_width: f32) -> &mut Self {
@ -1835,6 +1860,10 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
!self.has_fixed_state(DynamicState::LineWidth),
"the currently bound graphics pipeline must not contain this state internally"
);
if !self.device().enabled_features().wide_lines {
assert!(
@ -1855,6 +1884,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
/// - Panics if the highest scissor slot being set is greater than the
/// [`max_viewports`](crate::device::Properties::max_viewports) device property.
/// - If the [`multi_viewport`](crate::device::Features::multi_viewport) feature is not enabled,
@ -1863,12 +1893,16 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
where
I: IntoIterator<Item = Scissor>,
{
let scissors: SmallVec<[Scissor; 2]> = scissors.into_iter().collect();
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
!self.has_fixed_state(DynamicState::Scissor),
"the currently bound graphics pipeline must not contain this state internally"
);
let scissors: SmallVec<[Scissor; 2]> = scissors.into_iter().collect();
assert!(
first_scissor + scissors.len() as u32 <= self.device().physical_device().properties().max_viewports,
@ -1904,6 +1938,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
pub fn set_stencil_compare_mask(
&mut self,
faces: StencilFaces,
@ -1913,6 +1948,10 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
!self.has_fixed_state(DynamicState::StencilCompareMask),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner.set_stencil_compare_mask(faces, compare_mask);
@ -1926,11 +1965,16 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
pub fn set_stencil_reference(&mut self, faces: StencilFaces, reference: u32) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
!self.has_fixed_state(DynamicState::StencilReference),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner.set_stencil_reference(faces, reference);
@ -1944,11 +1988,16 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
pub fn set_stencil_write_mask(&mut self, faces: StencilFaces, write_mask: u32) -> &mut Self {
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
!self.has_fixed_state(DynamicState::StencilWriteMask),
"the currently bound graphics pipeline must not contain this state internally"
);
unsafe {
self.inner.set_stencil_write_mask(faces, write_mask);
@ -1962,6 +2011,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
/// # Panics
///
/// - Panics if the queue family of the command buffer does not support graphics operations.
/// - Panics if the currently bound graphics pipeline already contains this state internally.
/// - Panics if the highest viewport slot being set is greater than the
/// [`max_viewports`](crate::device::Properties::max_viewports) device property.
/// - If the [`multi_viewport`](crate::device::Features::multi_viewport) feature is not enabled,
@ -1970,12 +2020,16 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
where
I: IntoIterator<Item = Viewport>,
{
let viewports: SmallVec<[Viewport; 2]> = viewports.into_iter().collect();
assert!(
self.queue_family().supports_graphics(),
"the queue family of the command buffer must support graphics operations"
);
assert!(
!self.has_fixed_state(DynamicState::Viewport),
"the currently bound graphics pipeline must not contain this state internally"
);
let viewports: SmallVec<[Viewport; 2]> = viewports.into_iter().collect();
assert!(
first_viewport + viewports.len() as u32 <= self.device().physical_device().properties().max_viewports,

View File

@ -32,6 +32,7 @@ use crate::pipeline::layout::PipelineLayout;
use crate::pipeline::viewport::Scissor;
use crate::pipeline::viewport::Viewport;
use crate::pipeline::ComputePipeline;
use crate::pipeline::DynamicState;
use crate::pipeline::GraphicsPipeline;
use crate::pipeline::PipelineBindPoint;
use crate::render_pass::FramebufferAbstract;
@ -732,6 +733,26 @@ struct CurrentState {
viewport: FnvHashMap<u32, Viewport>,
}
impl CurrentState {
fn reset_dynamic_states(&mut self, states: impl IntoIterator<Item = DynamicState>) {
for state in states {
// TODO: If more dynamic states are added to CurrentState, add match arms here
match state {
DynamicState::BlendConstants => self.blend_constants = None,
DynamicState::DepthBias => self.depth_bias = None,
DynamicState::DepthBounds => self.depth_bounds = None,
DynamicState::LineWidth => self.line_width = None,
DynamicState::StencilCompareMask => self.stencil_compare_mask = Default::default(),
DynamicState::StencilReference => self.stencil_reference = Default::default(),
DynamicState::StencilWriteMask => self.stencil_write_mask = Default::default(),
DynamicState::Scissor => self.scissor.clear(),
DynamicState::Viewport => self.viewport.clear(),
_ => (),
}
}
}
}
#[derive(Debug)]
struct DescriptorSetState {
descriptor_sets: FnvHashMap<u32, Arc<dyn Command>>,

View File

@ -39,6 +39,7 @@ use crate::pipeline::vertex::VertexInput;
use crate::pipeline::viewport::Scissor;
use crate::pipeline::viewport::Viewport;
use crate::pipeline::ComputePipeline;
use crate::pipeline::DynamicStateMode;
use crate::pipeline::GraphicsPipeline;
use crate::pipeline::PipelineBindPoint;
use crate::query::QueryControlFlags;
@ -286,6 +287,16 @@ impl SyncCommandBufferBuilder {
}
}
self.current_state
.reset_dynamic_states(pipeline.dynamic_states().filter_map(|(state, mode)| {
// Reset any states that are fixed in the new pipeline. The pipeline bind command
// will overwrite these states.
if matches!(mode, DynamicStateMode::Fixed) {
Some(state)
} else {
None
}
}));
self.append_command(Cmd { pipeline }, &[]).unwrap();
self.current_state.pipeline_graphics = self.commands.last().cloned();
}

View File

@ -8,6 +8,8 @@
// according to those terms.
use crate::command_buffer::synced::CommandBufferState;
use crate::pipeline::DynamicState;
use crate::pipeline::DynamicStateMode;
use crate::pipeline::GraphicsPipeline;
use std::error;
use std::fmt;
@ -19,63 +21,97 @@ pub(in super::super) fn check_dynamic_state_validity(
) -> Result<(), CheckDynamicStateValidityError> {
let device = pipeline.device();
if pipeline.has_dynamic_blend_constants() {
for state in pipeline.dynamic_states().filter_map(|(state, mode)| {
if matches!(mode, DynamicStateMode::Dynamic) {
Some(state)
} else {
None
}
}) {
match state {
DynamicState::BlendConstants => {
if current_state.blend_constants().is_none() {
return Err(CheckDynamicStateValidityError::BlendConstantsNotSet);
}
}
if pipeline.has_dynamic_depth_bounds() {
if current_state.blend_constants().is_none() {
return Err(CheckDynamicStateValidityError::BlendConstantsNotSet);
DynamicState::ColorWriteEnable => todo!(),
DynamicState::CullMode => todo!(),
DynamicState::DepthBias => todo!(),
DynamicState::DepthBiasEnable => todo!(),
DynamicState::DepthBounds => {
if current_state.depth_bounds().is_none() {
return Err(CheckDynamicStateValidityError::DepthBoundsNotSet);
}
}
if pipeline.has_dynamic_line_width() {
DynamicState::DepthBoundsTestEnable => todo!(),
DynamicState::DepthCompareOp => todo!(),
DynamicState::DepthTestEnable => todo!(),
DynamicState::DepthWriteEnable => todo!(),
DynamicState::DiscardRectangle => todo!(),
DynamicState::ExclusiveScissor => todo!(),
DynamicState::FragmentShadingRate => todo!(),
DynamicState::FrontFace => todo!(),
DynamicState::LineStipple => todo!(),
DynamicState::LineWidth => {
if current_state.line_width().is_none() {
return Err(CheckDynamicStateValidityError::LineWidthNotSet);
}
}
if pipeline.has_dynamic_scissor() {
DynamicState::LogicOp => todo!(),
DynamicState::PatchControlPoints => todo!(),
DynamicState::PrimitiveRestartEnable => todo!(),
DynamicState::PrimitiveTopology => todo!(),
DynamicState::RasterizerDiscardEnable => todo!(),
DynamicState::RayTracingPipelineStackSize => unreachable!(
"RayTracingPipelineStackSize dynamic state should not occur on a graphics pipeline"
),
DynamicState::SampleLocations => todo!(),
DynamicState::Scissor => {
for num in 0..pipeline.num_viewports() {
if current_state.scissor(num).is_none() {
return Err(CheckDynamicStateValidityError::ScissorNotSet { num });
}
}
}
if pipeline.has_dynamic_stencil_compare_mask() {
DynamicState::ScissorWithCount => todo!(),
DynamicState::StencilCompareMask => {
let state = current_state.stencil_compare_mask();
if state.front.is_none() || state.back.is_none() {
return Err(CheckDynamicStateValidityError::StencilCompareMaskNotSet);
}
}
if pipeline.has_dynamic_stencil_reference() {
DynamicState::StencilOp => todo!(),
DynamicState::StencilReference => {
let state = current_state.stencil_reference();
if state.front.is_none() || state.back.is_none() {
return Err(CheckDynamicStateValidityError::StencilReferenceNotSet);
}
}
if pipeline.has_dynamic_stencil_write_mask() {
DynamicState::StencilTestEnable => todo!(),
DynamicState::StencilWriteMask => {
let state = current_state.stencil_write_mask();
if state.front.is_none() || state.back.is_none() {
return Err(CheckDynamicStateValidityError::StencilWriteMaskNotSet);
}
}
if pipeline.has_dynamic_viewport() {
DynamicState::VertexInput => todo!(),
DynamicState::VertexInputBindingStride => todo!(),
DynamicState::Viewport => {
for num in 0..pipeline.num_viewports() {
if current_state.viewport(num).is_none() {
return Err(CheckDynamicStateValidityError::ViewportNotSet { num });
}
}
}
DynamicState::ViewportCoarseSampleOrder => todo!(),
DynamicState::ViewportShadingRatePalette => todo!(),
DynamicState::ViewportWithCount => todo!(),
DynamicState::ViewportWScaling => todo!(),
}
}
Ok(())
}
@ -138,5 +174,3 @@ impl fmt::Display for CheckDynamicStateValidityError {
)
}
}
// TODO: tests

View File

@ -28,7 +28,7 @@ use std::u32;
pub struct DepthStencil {
/// Comparison to use between the depth value of each fragment and the depth value currently
/// in the depth buffer.
pub depth_compare: Compare,
pub depth_compare: CompareOp,
/// If `true`, then the value in the depth buffer will be updated when the depth test succeeds.
pub depth_write: bool,
@ -51,7 +51,7 @@ impl DepthStencil {
pub fn disabled() -> DepthStencil {
DepthStencil {
depth_write: false,
depth_compare: Compare::Always,
depth_compare: CompareOp::Always,
depth_bounds_test: DepthBounds::Disabled,
stencil_front: Default::default(),
stencil_back: Default::default(),
@ -64,7 +64,7 @@ impl DepthStencil {
pub fn simple_depth_test() -> DepthStencil {
DepthStencil {
depth_write: true,
depth_compare: Compare::Less,
depth_compare: CompareOp::Less,
depth_bounds_test: DepthBounds::Disabled,
stencil_front: Default::default(),
stencil_back: Default::default(),
@ -84,7 +84,7 @@ impl Default for DepthStencil {
pub struct Stencil {
/// The comparison to perform between the existing stencil value in the stencil buffer, and
/// the reference value (given by `reference`).
pub compare: Compare,
pub compare: CompareOp,
/// The operation to perform when both the depth test and the stencil test passed.
pub pass_op: StencilOp,
@ -133,10 +133,10 @@ impl Stencil {
#[inline]
pub fn always_keep(&self) -> bool {
match self.compare {
Compare::Always => {
CompareOp::Always => {
self.pass_op == StencilOp::Keep && self.depth_fail_op == StencilOp::Keep
}
Compare::Never => self.fail_op == StencilOp::Keep,
CompareOp::Never => self.fail_op == StencilOp::Keep,
_ => {
self.pass_op == StencilOp::Keep
&& self.fail_op == StencilOp::Keep
@ -150,7 +150,7 @@ impl Default for Stencil {
#[inline]
fn default() -> Stencil {
Stencil {
compare: Compare::Never,
compare: CompareOp::Never,
pass_op: StencilOp::Keep,
fail_op: StencilOp::Keep,
depth_fail_op: StencilOp::Keep,
@ -236,7 +236,7 @@ impl DepthBounds {
/// Used for both depth testing and stencil testing.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(i32)]
pub enum Compare {
pub enum CompareOp {
/// The test never passes.
Never = ash::vk::CompareOp::NEVER.as_raw(),
/// The test passes if `value < reference_value`.
@ -255,9 +255,9 @@ pub enum Compare {
Always = ash::vk::CompareOp::ALWAYS.as_raw(),
}
impl From<Compare> for ash::vk::CompareOp {
impl From<CompareOp> for ash::vk::CompareOp {
#[inline]
fn from(val: Compare) -> Self {
fn from(val: CompareOp) -> Self {
Self::from_raw(val as i32)
}
}

View File

@ -12,47 +12,30 @@
#![allow(deprecated)]
use crate::check_errors;
use crate::descriptor_set::layout::DescriptorSetDesc;
use crate::descriptor_set::layout::DescriptorSetLayout;
use crate::descriptor_set::layout::{DescriptorSetDesc, DescriptorSetLayout};
use crate::device::Device;
use crate::image::SampleCount;
use crate::pipeline::blend::AttachmentBlend;
use crate::pipeline::blend::AttachmentsBlend;
use crate::pipeline::blend::Blend;
use crate::pipeline::blend::LogicOp;
use crate::pipeline::blend::{AttachmentBlend, AttachmentsBlend, Blend, LogicOp};
use crate::pipeline::cache::PipelineCache;
use crate::pipeline::depth_stencil::Compare;
use crate::pipeline::depth_stencil::DepthBounds;
use crate::pipeline::depth_stencil::DepthStencil;
use crate::pipeline::graphics_pipeline::GraphicsPipeline;
use crate::pipeline::graphics_pipeline::GraphicsPipelineCreationError;
use crate::pipeline::graphics_pipeline::Inner as GraphicsPipelineInner;
use crate::pipeline::depth_stencil::{CompareOp, DepthBounds, DepthStencil};
use crate::pipeline::graphics_pipeline::{
GraphicsPipeline, GraphicsPipelineCreationError, Inner as GraphicsPipelineInner,
};
use crate::pipeline::input_assembly::PrimitiveTopology;
use crate::pipeline::layout::PipelineLayout;
use crate::pipeline::layout::PipelineLayoutCreationError;
use crate::pipeline::layout::PipelineLayoutPcRange;
use crate::pipeline::raster::CullMode;
use crate::pipeline::raster::DepthBiasControl;
use crate::pipeline::raster::FrontFace;
use crate::pipeline::raster::PolygonMode;
use crate::pipeline::raster::Rasterization;
use crate::pipeline::shader::EntryPointAbstract;
use crate::pipeline::shader::GraphicsEntryPoint;
use crate::pipeline::shader::GraphicsShaderType;
use crate::pipeline::shader::SpecializationConstants;
use crate::pipeline::vertex::BuffersDefinition;
use crate::pipeline::vertex::Vertex;
use crate::pipeline::vertex::VertexDefinition;
use crate::pipeline::vertex::VertexInputRate;
use crate::pipeline::viewport::Scissor;
use crate::pipeline::viewport::Viewport;
use crate::pipeline::viewport::ViewportsState;
use crate::pipeline::layout::{PipelineLayout, PipelineLayoutCreationError, PipelineLayoutPcRange};
use crate::pipeline::raster::{CullMode, DepthBiasControl, FrontFace, PolygonMode, Rasterization};
use crate::pipeline::shader::{
EntryPointAbstract, GraphicsEntryPoint, GraphicsShaderType, SpecializationConstants,
};
use crate::pipeline::vertex::{BuffersDefinition, Vertex, VertexDefinition, VertexInputRate};
use crate::pipeline::viewport::{Scissor, Viewport, ViewportsState};
use crate::pipeline::{DynamicState, DynamicStateMode};
use crate::render_pass::Subpass;
use crate::VulkanObject;
use fnv::FnvHashMap;
use smallvec::SmallVec;
use std::collections::hash_map::{Entry, HashMap};
use std::mem;
use std::mem::MaybeUninit;
use std::mem::{self, MaybeUninit};
use std::ptr;
use std::sync::Arc;
use std::u32;
@ -60,27 +43,29 @@ use std::u32;
/// Prototype for a `GraphicsPipeline`.
// TODO: we can optimize this by filling directly the raw vk structs
pub struct GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> {
vertex_definition: Vdef,
vertex_shader: Option<(GraphicsEntryPoint<'vs>, Vss)>,
tessellation_shaders: Option<TessellationShaders<'tcs, 'tes, Tcss, Tess>>,
geometry_shader: Option<(GraphicsEntryPoint<'gs>, Gss)>,
fragment_shader: Option<(GraphicsEntryPoint<'fs>, Fss)>,
vertex_definition: Vdef,
input_assembly: ash::vk::PipelineInputAssemblyStateCreateInfo,
// Note: the `input_assembly_topology` member is temporary in order to not lose information
// about the number of patches per primitive.
input_assembly_topology: PrimitiveTopology,
tessellation: Option<TessInfo<'tcs, 'tes, Tcss, Tess>>,
geometry_shader: Option<(GraphicsEntryPoint<'gs>, Gss)>,
viewport: Option<ViewportsState>,
raster: Rasterization,
multisample: ash::vk::PipelineMultisampleStateCreateInfo,
fragment_shader: Option<(GraphicsEntryPoint<'fs>, Fss)>,
depth_stencil: DepthStencil,
blend: Blend,
subpass: Option<Subpass>,
cache: Option<Arc<PipelineCache>>,
}
// Additional parameters if tessellation is used.
#[derive(Clone, Debug)]
struct TessInfo<'tcs, 'tes, Tcss, Tess> {
struct TessellationShaders<'tcs, 'tes, Tcss, Tess> {
tessellation_control_shader: (GraphicsEntryPoint<'tcs>, Tcss),
tessellation_evaluation_shader: (GraphicsEntryPoint<'tes>, Tess),
}
@ -103,21 +88,23 @@ impl
/// Builds a new empty builder.
pub(super) fn new() -> Self {
GraphicsPipelineBuilder {
vertex_definition: BuffersDefinition::new(),
vertex_shader: None,
tessellation_shaders: None,
geometry_shader: None,
fragment_shader: None,
vertex_definition: BuffersDefinition::new(),
input_assembly: ash::vk::PipelineInputAssemblyStateCreateInfo {
topology: PrimitiveTopology::TriangleList.into(),
..Default::default()
},
input_assembly_topology: PrimitiveTopology::TriangleList,
tessellation: None,
geometry_shader: None,
viewport: None,
raster: Default::default(),
multisample: ash::vk::PipelineMultisampleStateCreateInfo::default(),
fragment_shader: None,
depth_stencil: DepthStencil::disabled(),
blend: Blend::pass_through(),
subpass: None,
cache: None,
}
@ -156,10 +143,10 @@ where
let (descriptor_set_layout_descs, push_constant_ranges) = {
let stages: SmallVec<[&GraphicsEntryPoint; 5]> = std::array::IntoIter::new([
self.vertex_shader.as_ref().map(|s| &s.0),
self.tessellation
self.tessellation_shaders
.as_ref()
.map(|s| &s.tessellation_control_shader.0),
self.tessellation
self.tessellation_shaders
.as_ref()
.map(|s| &s.tessellation_evaluation_shader.0),
self.geometry_shader.as_ref().map(|s| &s.0),
@ -254,7 +241,7 @@ where
)?;
}
if let Some(ref tess) = self.tessellation {
if let Some(ref tess) = self.tessellation_shaders {
{
let shader = &tess.tessellation_control_shader.0;
pipeline_layout.ensure_compatible_with_shader(
@ -292,7 +279,8 @@ where
}
// Will contain the list of dynamic states. Filled throughout this function.
let mut dynamic_states: SmallVec<[ash::vk::DynamicState; 8]> = SmallVec::new();
let mut dynamic_state_modes: FnvHashMap<DynamicState, DynamicStateMode> =
HashMap::default();
// Creating the specialization constants of the various stages.
let vertex_shader_specialization = {
@ -311,7 +299,7 @@ where
}
};
let tess_shader_specialization = if let Some(ref tess) = self.tessellation {
let tess_shader_specialization = if let Some(ref tess) = self.tessellation_shaders {
let tcs_spec = {
let shader = &tess.tessellation_control_shader;
let spec_descriptors = Tcss::descriptors();
@ -405,7 +393,7 @@ where
..Default::default()
});
if let Some(ref tess) = self.tessellation {
if let Some(ref tess) = self.tessellation_shaders {
// FIXME: must check that the control shader and evaluation shader are compatible
if !device.enabled_features().tessellation_shader {
@ -668,7 +656,7 @@ where
None
};
let vertex_input_state = ash::vk::PipelineVertexInputStateCreateInfo {
let vertex_input_state = Some(ash::vk::PipelineVertexInputStateCreateInfo {
p_next: if let Some(next) = vertex_input_divisor_state.as_ref() {
next as *const _ as *const _
} else {
@ -680,7 +668,9 @@ where
vertex_attribute_description_count: attribute_descriptions.len() as u32,
p_vertex_attribute_descriptions: attribute_descriptions.as_ptr(),
..Default::default()
};
});
let input_assembly_state = Some(self.input_assembly);
if self.input_assembly.primitive_restart_enable != ash::vk::FALSE
&& !self.input_assembly_topology.supports_primitive_restart()
@ -706,9 +696,9 @@ where
}
}
let tessellation = match self.input_assembly_topology {
let tessellation_state = match self.input_assembly_topology {
PrimitiveTopology::PatchList { vertices_per_patch } => {
if self.tessellation.is_none() {
if self.tessellation_shaders.is_none() {
return Err(GraphicsPipelineCreationError::InvalidPrimitiveTopology);
}
if vertices_per_patch
@ -727,7 +717,7 @@ where
})
}
_ => {
if self.tessellation.is_some() {
if self.tessellation_shaders.is_some() {
return Err(GraphicsPipelineCreationError::InvalidPrimitiveTopology);
}
@ -751,7 +741,7 @@ where
.iter()
.map(|e| e.clone().into())
.collect::<SmallVec<[ash::vk::Rect2D; 4]>>();
dynamic_states.push(ash::vk::DynamicState::VIEWPORT);
dynamic_state_modes.insert(DynamicState::Viewport, DynamicStateMode::Dynamic);
(SmallVec::new(), scissors, num)
}
ViewportsState::DynamicScissors { ref viewports } => {
@ -760,12 +750,12 @@ where
.iter()
.map(|e| e.clone().into())
.collect::<SmallVec<[ash::vk::Viewport; 4]>>();
dynamic_states.push(ash::vk::DynamicState::SCISSOR);
dynamic_state_modes.insert(DynamicState::Scissor, DynamicStateMode::Dynamic);
(viewports, SmallVec::new(), num)
}
ViewportsState::Dynamic { num } => {
dynamic_states.push(ash::vk::DynamicState::VIEWPORT);
dynamic_states.push(ash::vk::DynamicState::SCISSOR);
dynamic_state_modes.insert(DynamicState::Viewport, DynamicStateMode::Dynamic);
dynamic_state_modes.insert(DynamicState::Scissor, DynamicStateMode::Dynamic);
(SmallVec::new(), SmallVec::new(), num)
}
};
@ -805,7 +795,7 @@ where
}
}
let viewport_info = ash::vk::PipelineViewportStateCreateInfo {
let viewport_state = Some(ash::vk::PipelineViewportStateCreateInfo {
flags: ash::vk::PipelineViewportStateCreateFlags::empty(),
viewport_count: vp_num,
p_viewports: if vp_vp.is_empty() {
@ -820,19 +810,19 @@ where
vp_sc.as_ptr()
}, // validation layer crashes if you just pass the pointer
..Default::default()
};
});
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(ash::vk::DynamicState::LINE_WIDTH);
dynamic_state_modes.insert(DynamicState::LineWidth, DynamicStateMode::Dynamic);
}
let (db_enable, db_const, db_clamp, db_slope) = match self.raster.depth_bias {
DepthBiasControl::Dynamic => {
dynamic_states.push(ash::vk::DynamicState::DEPTH_BIAS);
dynamic_state_modes.insert(DynamicState::DepthBias, DynamicStateMode::Dynamic);
(ash::vk::TRUE, 0.0, 0.0, 0.0)
}
DepthBiasControl::Disabled => (ash::vk::FALSE, 0.0, 0.0, 0.0),
@ -860,7 +850,7 @@ where
return Err(GraphicsPipelineCreationError::FillModeNonSolidFeatureNotEnabled);
}
let rasterization = ash::vk::PipelineRasterizationStateCreateInfo {
let rasterization_state = Some(ash::vk::PipelineRasterizationStateCreateInfo {
flags: ash::vk::PipelineRasterizationStateCreateFlags::empty(),
depth_clamp_enable: if self.raster.depth_clamp {
ash::vk::TRUE
@ -881,7 +871,7 @@ where
depth_bias_slope_factor: db_slope,
line_width: self.raster.line_width.unwrap_or(1.0),
..Default::default()
};
});
self.multisample.rasterization_samples = self
.subpass
@ -905,7 +895,9 @@ where
}
}
let depth_stencil = {
let multisample_state = Some(self.multisample);
let depth_stencil_state = {
let db = match self.depth_stencil.depth_bounds_test {
DepthBounds::Disabled => (ash::vk::FALSE, 0.0, 0.0),
DepthBounds::Fixed(ref range) => {
@ -920,7 +912,8 @@ where
return Err(GraphicsPipelineCreationError::DepthBoundsFeatureNotEnabled);
}
dynamic_states.push(ash::vk::DynamicState::DEPTH_BOUNDS);
dynamic_state_modes
.insert(DynamicState::DepthBounds, DynamicStateMode::Dynamic);
(ash::vk::TRUE, 0.0, 1.0)
}
@ -932,7 +925,8 @@ where
) {
(Some(_), Some(_)) => (),
(None, None) => {
dynamic_states.push(ash::vk::DynamicState::STENCIL_COMPARE_MASK);
dynamic_state_modes
.insert(DynamicState::StencilCompareMask, DynamicStateMode::Dynamic);
}
_ => return Err(GraphicsPipelineCreationError::WrongStencilState),
};
@ -943,7 +937,8 @@ where
) {
(Some(_), Some(_)) => (),
(None, None) => {
dynamic_states.push(ash::vk::DynamicState::STENCIL_WRITE_MASK);
dynamic_state_modes
.insert(DynamicState::StencilWriteMask, DynamicStateMode::Dynamic);
}
_ => return Err(GraphicsPipelineCreationError::WrongStencilState),
};
@ -954,7 +949,8 @@ where
) {
(Some(_), Some(_)) => (),
(None, None) => {
dynamic_states.push(ash::vk::DynamicState::STENCIL_REFERENCE);
dynamic_state_modes
.insert(DynamicState::StencilReference, DynamicStateMode::Dynamic);
}
_ => return Err(GraphicsPipelineCreationError::WrongStencilState),
};
@ -965,7 +961,7 @@ where
return Err(GraphicsPipelineCreationError::NoDepthAttachment);
}
if self.depth_stencil.depth_compare != Compare::Always
if self.depth_stencil.depth_compare != CompareOp::Always
&& !self.subpass.as_ref().unwrap().has_depth()
{
return Err(GraphicsPipelineCreationError::NoDepthAttachment);
@ -980,10 +976,10 @@ where
// FIXME: stencil writability
ash::vk::PipelineDepthStencilStateCreateInfo {
Some(ash::vk::PipelineDepthStencilStateCreateInfo {
flags: ash::vk::PipelineDepthStencilStateCreateFlags::empty(),
depth_test_enable: if !self.depth_stencil.depth_write
&& self.depth_stencil.depth_compare == Compare::Always
&& self.depth_stencil.depth_compare == CompareOp::Always
{
ash::vk::FALSE
} else {
@ -1040,7 +1036,7 @@ where
min_depth_bounds: db.1,
max_depth_bounds: db.2,
..Default::default()
}
})
};
let blend_atch: SmallVec<[ash::vk::PipelineColorBlendAttachmentState; 8]> = {
@ -1068,7 +1064,7 @@ where
}
};
let blend = ash::vk::PipelineColorBlendStateCreateInfo {
let color_blend_state = Some(ash::vk::PipelineColorBlendStateCreateInfo {
flags: ash::vk::PipelineColorBlendStateCreateFlags::empty(),
logic_op_enable: if self.blend.logic_op.is_some() {
if !device.enabled_features().logic_op {
@ -1084,23 +1080,143 @@ where
blend_constants: if let Some(c) = self.blend.blend_constants {
c
} else {
dynamic_states.push(ash::vk::DynamicState::BLEND_CONSTANTS);
dynamic_state_modes.insert(DynamicState::BlendConstants, DynamicStateMode::Dynamic);
[0.0, 0.0, 0.0, 0.0]
},
..Default::default()
};
});
let dynamic_states = if !dynamic_states.is_empty() {
// Dynamic state
let dynamic_state_list: Vec<ash::vk::DynamicState> = dynamic_state_modes
.iter()
.filter_map(|(&state, &mode)| {
if matches!(mode, DynamicStateMode::Dynamic) {
Some(state.into())
} else {
None
}
})
.collect();
let dynamic_state = if !dynamic_state_list.is_empty() {
Some(ash::vk::PipelineDynamicStateCreateInfo {
flags: ash::vk::PipelineDynamicStateCreateFlags::empty(),
dynamic_state_count: dynamic_states.len() as u32,
p_dynamic_states: dynamic_states.as_ptr(),
dynamic_state_count: dynamic_state_list.len() as u32,
p_dynamic_states: dynamic_state_list.as_ptr(),
..Default::default()
})
} else {
None
};
// Set any remaining states to fixed, if the corresponding state is enabled.
if vertex_input_state.is_some() {
dynamic_state_modes
.entry(DynamicState::VertexInput)
.or_insert(DynamicStateMode::Fixed);
}
if input_assembly_state.is_some() {
dynamic_state_modes
.entry(DynamicState::PrimitiveTopology)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::PrimitiveRestartEnable)
.or_insert(DynamicStateMode::Fixed);
}
if tessellation_state.is_some() {
dynamic_state_modes
.entry(DynamicState::PatchControlPoints)
.or_insert(DynamicStateMode::Fixed);
}
if viewport_state.is_some() {
dynamic_state_modes
.entry(DynamicState::Viewport)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::Scissor)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::ViewportWithCount)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::ScissorWithCount)
.or_insert(DynamicStateMode::Fixed);
}
if rasterization_state.is_some() {
dynamic_state_modes
.entry(DynamicState::RasterizerDiscardEnable)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::CullMode)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::FrontFace)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::DepthBiasEnable)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::DepthBias)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::LineWidth)
.or_insert(DynamicStateMode::Fixed);
}
if depth_stencil_state.is_some() {
dynamic_state_modes
.entry(DynamicState::DepthTestEnable)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::DepthWriteEnable)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::DepthCompareOp)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::DepthBoundsTestEnable)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::StencilTestEnable)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::StencilCompareMask)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::StencilWriteMask)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::StencilReference)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::DepthBounds)
.or_insert(DynamicStateMode::Fixed);
}
if color_blend_state.is_some() {
dynamic_state_modes
.entry(DynamicState::LogicOp)
.or_insert(DynamicStateMode::Fixed);
dynamic_state_modes
.entry(DynamicState::BlendConstants)
.or_insert(DynamicStateMode::Fixed);
}
// Dynamic states not handled yet:
// - ViewportWScaling (VkPipelineViewportWScalingStateCreateInfoNV)
// - DiscardRectangle (VkPipelineDiscardRectangleStateCreateInfoEXT)
// - SampleLocations (VkPipelineSampleLocationsStateCreateInfoEXT)
// - ViewportShadingRatePalette (VkPipelineViewportShadingRateImageStateCreateInfoNV)
// - ViewportCoarseSampleOrder (VkPipelineViewportCoarseSampleOrderStateCreateInfoNV)
// - ExclusiveScissor (VkPipelineViewportExclusiveScissorStateCreateInfoNV)
// - FragmentShadingRate (VkPipelineFragmentShadingRateStateCreateInfoKHR)
// - LineStipple (VkPipelineRasterizationLineStateCreateInfoEXT)
// - ColorWriteEnable (VkPipelineColorWriteCreateInfoEXT)
if let Some(multiview) = self
.subpass
.as_ref()
@ -1120,7 +1236,7 @@ where
return Err(GraphicsPipelineCreationError::MultiviewGeometryShaderNotSupported);
}
if self.tessellation.is_some()
if self.tessellation_shaders.is_some()
&& !device
.physical_device()
.supported_features()
@ -1138,18 +1254,39 @@ where
flags: ash::vk::PipelineCreateFlags::empty(), // TODO: some flags are available but none are critical
stage_count: stages.len() as u32,
p_stages: stages.as_ptr(),
p_vertex_input_state: &vertex_input_state,
p_input_assembly_state: &self.input_assembly,
p_tessellation_state: tessellation
p_vertex_input_state: vertex_input_state
.as_ref()
.map(|t| t as *const _)
.map(|p| p as *const _)
.unwrap_or(ptr::null()),
p_viewport_state: &viewport_info,
p_rasterization_state: &rasterization,
p_multisample_state: &self.multisample,
p_depth_stencil_state: &depth_stencil,
p_color_blend_state: &blend,
p_dynamic_state: dynamic_states
p_input_assembly_state: input_assembly_state
.as_ref()
.map(|p| p as *const _)
.unwrap_or(ptr::null()),
p_tessellation_state: tessellation_state
.as_ref()
.map(|p| p as *const _)
.unwrap_or(ptr::null()),
p_viewport_state: viewport_state
.as_ref()
.map(|p| p as *const _)
.unwrap_or(ptr::null()),
p_rasterization_state: rasterization_state
.as_ref()
.map(|p| p as *const _)
.unwrap_or(ptr::null()),
p_multisample_state: multisample_state
.as_ref()
.map(|p| p as *const _)
.unwrap_or(ptr::null()),
p_depth_stencil_state: depth_stencil_state
.as_ref()
.map(|p| p as *const _)
.unwrap_or(ptr::null()),
p_color_blend_state: color_blend_state
.as_ref()
.map(|p| p as *const _)
.unwrap_or(ptr::null()),
p_dynamic_state: dynamic_state
.as_ref()
.map(|s| s as *const _)
.unwrap_or(ptr::null()),
@ -1199,17 +1336,7 @@ where
layout: pipeline_layout,
subpass: self.subpass.take().unwrap(),
vertex_input,
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(),
dynamic_state: dynamic_state_modes,
num_viewports: self.viewport.as_ref().unwrap().num_viewports(),
})
}
@ -1229,18 +1356,20 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss>
vertex_definition: T,
) -> GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs, 'fs, T, Vss, Tcss, Tess, Gss, Fss> {
GraphicsPipelineBuilder {
vertex_definition,
vertex_shader: self.vertex_shader,
tessellation_shaders: self.tessellation_shaders,
geometry_shader: self.geometry_shader,
fragment_shader: self.fragment_shader,
vertex_definition,
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,
subpass: self.subpass,
cache: self.cache,
}
@ -1281,18 +1410,20 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss>
Vss2: SpecializationConstants,
{
GraphicsPipelineBuilder {
vertex_definition: self.vertex_definition,
vertex_shader: Some((shader, specialization_constants)),
tessellation_shaders: self.tessellation_shaders,
geometry_shader: self.geometry_shader,
fragment_shader: self.fragment_shader,
vertex_definition: self.vertex_definition,
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,
subpass: self.subpass,
cache: self.cache,
}
@ -1433,11 +1564,8 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss>
Tess2: SpecializationConstants,
{
GraphicsPipelineBuilder {
vertex_definition: self.vertex_definition,
vertex_shader: self.vertex_shader,
input_assembly: self.input_assembly,
input_assembly_topology: self.input_assembly_topology,
tessellation: Some(TessInfo {
tessellation_shaders: Some(TessellationShaders {
tessellation_control_shader: (
tessellation_control_shader,
tessellation_control_shader_spec_constants,
@ -1448,12 +1576,17 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss>
),
}),
geometry_shader: self.geometry_shader,
fragment_shader: self.fragment_shader,
vertex_definition: self.vertex_definition,
input_assembly: self.input_assembly,
input_assembly_topology: self.input_assembly_topology,
viewport: self.viewport,
raster: self.raster,
multisample: self.multisample,
fragment_shader: self.fragment_shader,
depth_stencil: self.depth_stencil,
blend: self.blend,
subpass: self.subpass,
cache: self.cache,
}
@ -1462,7 +1595,7 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss>
/// Sets the tessellation shaders stage as disabled. This is the default.
#[inline]
pub fn tessellation_shaders_disabled(mut self) -> Self {
self.tessellation = None;
self.tessellation_shaders = None;
self
}
@ -1478,18 +1611,20 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss>
Gss2: SpecializationConstants,
{
GraphicsPipelineBuilder {
vertex_definition: self.vertex_definition,
vertex_shader: self.vertex_shader,
tessellation_shaders: self.tessellation_shaders,
geometry_shader: Some((shader, specialization_constants)),
fragment_shader: self.fragment_shader,
vertex_definition: self.vertex_definition,
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,
subpass: self.subpass,
cache: self.cache,
}
@ -1760,18 +1895,20 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss>
Fss2: SpecializationConstants,
{
GraphicsPipelineBuilder {
vertex_definition: self.vertex_definition,
vertex_shader: self.vertex_shader,
tessellation_shaders: self.tessellation_shaders,
geometry_shader: self.geometry_shader,
fragment_shader: Some((shader, specialization_constants)),
vertex_definition: self.vertex_definition,
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,
subpass: self.subpass,
cache: self.cache,
}
@ -1875,18 +2012,20 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss>
#[inline]
pub fn render_pass(self, subpass: Subpass) -> Self {
GraphicsPipelineBuilder {
vertex_definition: self.vertex_definition,
vertex_shader: self.vertex_shader,
tessellation_shaders: self.tessellation_shaders,
geometry_shader: self.geometry_shader,
fragment_shader: self.fragment_shader,
vertex_definition: self.vertex_definition,
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,
subpass: Some(subpass),
cache: self.cache,
}
@ -1916,18 +2055,20 @@ where
{
fn clone(&self) -> Self {
GraphicsPipelineBuilder {
vertex_definition: self.vertex_definition.clone(),
vertex_shader: self.vertex_shader.clone(),
tessellation_shaders: self.tessellation_shaders.clone(),
geometry_shader: self.geometry_shader.clone(),
fragment_shader: self.fragment_shader.clone(),
vertex_definition: self.vertex_definition.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: self.multisample,
fragment_shader: self.fragment_shader.clone(),
depth_stencil: self.depth_stencil.clone(),
blend: self.blend.clone(),
subpass: self.subpass.clone(),
cache: self.cache.clone(),
}

View File

@ -16,6 +16,7 @@ use crate::pipeline::vertex::BuffersDefinition;
use crate::pipeline::vertex::VertexInput;
use crate::render_pass::Subpass;
use crate::VulkanObject;
use fnv::FnvHashMap;
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
@ -38,17 +39,7 @@ pub struct GraphicsPipeline {
layout: Arc<PipelineLayout>,
subpass: Subpass,
vertex_input: VertexInput,
dynamic_line_width: bool,
dynamic_viewport: bool,
dynamic_scissor: bool,
dynamic_depth_bias: bool,
dynamic_depth_bounds: bool,
dynamic_stencil_compare_mask: bool,
dynamic_stencil_write_mask: bool,
dynamic_stencil_reference: bool,
dynamic_blend_constants: bool,
dynamic_state: FnvHashMap<DynamicState, DynamicStateMode>,
num_viewports: u32,
}
@ -107,52 +98,19 @@ impl GraphicsPipeline {
self.num_viewports
}
/// Returns true if the blend constants used by this pipeline are dynamic.
#[inline]
pub fn has_dynamic_blend_constants(&self) -> bool {
self.dynamic_blend_constants
/// Returns the mode of a particular dynamic state.
///
/// `None` is returned if the pipeline does not contain this state. Previously set dynamic
/// state is not disturbed when binding it.
pub fn dynamic_state(&self, state: DynamicState) -> Option<DynamicStateMode> {
self.dynamic_state.get(&state).copied()
}
/// Returns true if the depth bounds used by this pipeline are dynamic.
#[inline]
pub fn has_dynamic_depth_bounds(&self) -> bool {
self.dynamic_depth_bounds
}
/// Returns true if the line width used by this pipeline is dynamic.
#[inline]
pub fn has_dynamic_line_width(&self) -> bool {
self.dynamic_line_width
}
/// Returns true if the scissors used by this pipeline are dynamic.
#[inline]
pub fn has_dynamic_scissor(&self) -> bool {
self.dynamic_scissor
}
/// Returns true if the stencil compare masks used by this pipeline are dynamic.
#[inline]
pub fn has_dynamic_stencil_compare_mask(&self) -> bool {
self.dynamic_stencil_compare_mask
}
/// Returns true if the stencil references used by this pipeline are dynamic.
#[inline]
pub fn has_dynamic_stencil_reference(&self) -> bool {
self.dynamic_stencil_reference
}
/// Returns true if the stencil write masks used by this pipeline are dynamic.
#[inline]
pub fn has_dynamic_stencil_write_mask(&self) -> bool {
self.dynamic_stencil_write_mask
}
/// Returns true if the viewports used by this pipeline are dynamic.
#[inline]
pub fn has_dynamic_viewport(&self) -> bool {
self.dynamic_viewport
/// Returns all dynamic states and their modes.
pub fn dynamic_states(
&self,
) -> impl ExactSizeIterator<Item = (DynamicState, DynamicStateMode)> + '_ {
self.dynamic_state.iter().map(|(k, v)| (*k, *v))
}
}
@ -218,3 +176,66 @@ unsafe impl<'a> VulkanObject for GraphicsPipelineSys<'a> {
self.0
}
}
/// A particular state value within a graphics pipeline that can be dynamically set by a command
/// buffer.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum DynamicState {
Viewport = ash::vk::DynamicState::VIEWPORT.as_raw(),
Scissor = ash::vk::DynamicState::SCISSOR.as_raw(),
LineWidth = ash::vk::DynamicState::LINE_WIDTH.as_raw(),
DepthBias = ash::vk::DynamicState::DEPTH_BIAS.as_raw(),
BlendConstants = ash::vk::DynamicState::BLEND_CONSTANTS.as_raw(),
DepthBounds = ash::vk::DynamicState::DEPTH_BOUNDS.as_raw(),
StencilCompareMask = ash::vk::DynamicState::STENCIL_COMPARE_MASK.as_raw(),
StencilWriteMask = ash::vk::DynamicState::STENCIL_WRITE_MASK.as_raw(),
StencilReference = ash::vk::DynamicState::STENCIL_REFERENCE.as_raw(),
ViewportWScaling = ash::vk::DynamicState::VIEWPORT_W_SCALING_NV.as_raw(),
DiscardRectangle = ash::vk::DynamicState::DISCARD_RECTANGLE_EXT.as_raw(),
SampleLocations = ash::vk::DynamicState::SAMPLE_LOCATIONS_EXT.as_raw(),
RayTracingPipelineStackSize =
ash::vk::DynamicState::RAY_TRACING_PIPELINE_STACK_SIZE_KHR.as_raw(),
ViewportShadingRatePalette = ash::vk::DynamicState::VIEWPORT_SHADING_RATE_PALETTE_NV.as_raw(),
ViewportCoarseSampleOrder = ash::vk::DynamicState::VIEWPORT_COARSE_SAMPLE_ORDER_NV.as_raw(),
ExclusiveScissor = ash::vk::DynamicState::EXCLUSIVE_SCISSOR_NV.as_raw(),
FragmentShadingRate = ash::vk::DynamicState::FRAGMENT_SHADING_RATE_KHR.as_raw(),
LineStipple = ash::vk::DynamicState::LINE_STIPPLE_EXT.as_raw(),
CullMode = ash::vk::DynamicState::CULL_MODE_EXT.as_raw(),
FrontFace = ash::vk::DynamicState::FRONT_FACE_EXT.as_raw(),
PrimitiveTopology = ash::vk::DynamicState::PRIMITIVE_TOPOLOGY_EXT.as_raw(),
ViewportWithCount = ash::vk::DynamicState::VIEWPORT_WITH_COUNT_EXT.as_raw(),
ScissorWithCount = ash::vk::DynamicState::SCISSOR_WITH_COUNT_EXT.as_raw(),
VertexInputBindingStride = ash::vk::DynamicState::VERTEX_INPUT_BINDING_STRIDE_EXT.as_raw(),
DepthTestEnable = ash::vk::DynamicState::DEPTH_TEST_ENABLE_EXT.as_raw(),
DepthWriteEnable = ash::vk::DynamicState::DEPTH_WRITE_ENABLE_EXT.as_raw(),
DepthCompareOp = ash::vk::DynamicState::DEPTH_COMPARE_OP_EXT.as_raw(),
DepthBoundsTestEnable = ash::vk::DynamicState::DEPTH_BOUNDS_TEST_ENABLE_EXT.as_raw(),
StencilTestEnable = ash::vk::DynamicState::STENCIL_TEST_ENABLE_EXT.as_raw(),
StencilOp = ash::vk::DynamicState::STENCIL_OP_EXT.as_raw(),
VertexInput = ash::vk::DynamicState::VERTEX_INPUT_EXT.as_raw(),
PatchControlPoints = ash::vk::DynamicState::PATCH_CONTROL_POINTS_EXT.as_raw(),
RasterizerDiscardEnable = ash::vk::DynamicState::RASTERIZER_DISCARD_ENABLE_EXT.as_raw(),
DepthBiasEnable = ash::vk::DynamicState::DEPTH_BIAS_ENABLE_EXT.as_raw(),
LogicOp = ash::vk::DynamicState::LOGIC_OP_EXT.as_raw(),
PrimitiveRestartEnable = ash::vk::DynamicState::PRIMITIVE_RESTART_ENABLE_EXT.as_raw(),
ColorWriteEnable = ash::vk::DynamicState::COLOR_WRITE_ENABLE_EXT.as_raw(),
}
impl From<DynamicState> for ash::vk::DynamicState {
#[inline]
fn from(val: DynamicState) -> Self {
Self::from_raw(val as i32)
}
}
/// Specifies how a dynamic state is handled by a graphics pipeline.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DynamicStateMode {
/// The pipeline has a fixed value for this state. Previously set dynamic state will be lost
/// when binding it, and will have to be re-set after binding a pipeline that uses it.
Fixed,
/// The pipeline expects a dynamic value to be set by a command buffer. Previously set dynamic
/// state is not disturbed when binding it.
Dynamic,
}

View File

@ -78,6 +78,8 @@
pub use self::compute_pipeline::ComputePipeline;
pub use self::compute_pipeline::ComputePipelineCreationError;
pub use self::compute_pipeline::ComputePipelineSys;
pub use self::graphics_pipeline::DynamicState;
pub use self::graphics_pipeline::DynamicStateMode;
pub use self::graphics_pipeline::GraphicsPipeline;
pub use self::graphics_pipeline::GraphicsPipelineBuilder;
pub use self::graphics_pipeline::GraphicsPipelineCreationError;

View File

@ -65,7 +65,7 @@
use crate::check_errors;
use crate::device::Device;
use crate::device::DeviceOwned;
pub use crate::pipeline::depth_stencil::Compare;
use crate::pipeline::depth_stencil::CompareOp;
use crate::Error;
use crate::OomError;
use crate::VulkanObject;
@ -225,7 +225,7 @@ impl Sampler {
max_anisotropy: f32,
min_lod: f32,
max_lod: f32,
compare: Compare,
compare: CompareOp,
) -> Result<Arc<Sampler>, SamplerCreationError> {
Sampler::new_impl(
device,
@ -255,7 +255,7 @@ impl Sampler {
max_anisotropy: f32,
min_lod: f32,
max_lod: f32,
compare: Option<Compare>,
compare: Option<CompareOp>,
) -> Result<Arc<Sampler>, SamplerCreationError> {
assert!(max_anisotropy >= 1.0);
assert!(min_lod <= max_lod);
@ -854,7 +854,7 @@ mod tests {
1.0,
0.0,
2.0,
sampler::Compare::Less,
sampler::CompareOp::Less,
)
.unwrap();