// Copyright (c) 2017 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. // TODO: graphics pipeline params are deprecated, but are still the primary implementation in order // to avoid duplicating code, so we hide the warnings for now #![allow(deprecated)] use super::{ color_blend::{ AttachmentBlend, ColorBlendAttachmentState, ColorBlendState, ColorComponents, LogicOp, }, depth_stencil::{DepthStencilState, StencilOps}, discard_rectangle::DiscardRectangleState, input_assembly::{InputAssemblyState, PrimitiveTopology, PrimitiveTopologyClass}, multisample::MultisampleState, rasterization::{ CullMode, DepthBiasState, FrontFace, LineRasterizationMode, PolygonMode, RasterizationState, }, render_pass::{PipelineRenderPassType, PipelineRenderingCreateInfo}, tessellation::TessellationState, vertex_input::{ VertexDefinition, VertexInputAttributeDescription, VertexInputBindingDescription, VertexInputState, }, viewport::{Scissor, Viewport, ViewportState}, GraphicsPipeline, GraphicsPipelineCreationError, }; use crate::{ descriptor_set::layout::{DescriptorSetLayout, DescriptorSetLayoutCreateInfo}, device::{Device, DeviceOwned}, format::{FormatFeatures, NumericType}, image::ImageAspects, pipeline::{ cache::PipelineCache, graphics::{ color_blend::BlendFactor, depth_stencil::{DepthBoundsState, DepthState, StencilOpState, StencilState}, vertex_input::VertexInputRate, }, layout::{PipelineLayoutCreateInfo, PushConstantRange}, DynamicState, PartialStateMode, PipelineLayout, StateMode, }, shader::{ DescriptorBindingRequirements, EntryPoint, FragmentShaderExecution, FragmentTestsStages, ShaderExecution, ShaderScalarType, ShaderStage, SpecializationConstants, SpecializationMapEntry, }, DeviceSize, RequiresOneOf, Version, VulkanError, VulkanObject, }; use ahash::HashMap; use smallvec::SmallVec; use std::{ collections::hash_map::Entry, mem::{size_of_val, MaybeUninit}, ptr, slice, sync::Arc, }; /// Prototype for a `GraphicsPipeline`. #[derive(Debug)] pub struct GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> { render_pass: Option, cache: Option>, vertex_shader: Option<(EntryPoint<'vs>, Vss)>, tessellation_shaders: Option>, geometry_shader: Option<(EntryPoint<'gs>, Gss)>, fragment_shader: Option<(EntryPoint<'fs>, Fss)>, vertex_input_state: Vdef, input_assembly_state: InputAssemblyState, tessellation_state: TessellationState, viewport_state: ViewportState, discard_rectangle_state: DiscardRectangleState, rasterization_state: RasterizationState, multisample_state: MultisampleState, depth_stencil_state: DepthStencilState, color_blend_state: ColorBlendState, } // Additional parameters if tessellation is used. #[derive(Clone, Debug)] struct TessellationShaders<'tcs, 'tes, Tcss, Tess> { control: (EntryPoint<'tcs>, Tcss), evaluation: (EntryPoint<'tes>, Tess), } impl GraphicsPipelineBuilder< 'static, 'static, 'static, 'static, 'static, VertexInputState, (), (), (), (), (), > { /// Builds a new empty builder. pub(super) fn new() -> Self { GraphicsPipelineBuilder { render_pass: None, cache: None, vertex_shader: None, tessellation_shaders: None, geometry_shader: None, fragment_shader: None, vertex_input_state: Default::default(), input_assembly_state: Default::default(), tessellation_state: Default::default(), viewport_state: Default::default(), discard_rectangle_state: Default::default(), rasterization_state: Default::default(), multisample_state: Default::default(), depth_stencil_state: Default::default(), color_blend_state: Default::default(), } } } #[derive(Clone, Copy, Debug)] struct Has { vertex_input_state: bool, pre_rasterization_shader_state: bool, tessellation_state: bool, viewport_state: bool, fragment_shader_state: bool, depth_stencil_state: bool, fragment_output_state: bool, color_blend_state: bool, } impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> where Vdef: VertexDefinition, Vss: SpecializationConstants, Tcss: SpecializationConstants, Tess: SpecializationConstants, Gss: SpecializationConstants, Fss: SpecializationConstants, { /// Builds the graphics pipeline, using an inferred a pipeline layout. #[inline] pub fn build( self, device: Arc, ) -> Result, GraphicsPipelineCreationError> { self.with_auto_layout(device, |_| {}) } /// The same as `new`, but allows you to provide a closure that is given a mutable reference to /// the inferred descriptor set definitions. This can be used to make changes to the layout /// before it's created, for example to add dynamic buffers or immutable samplers. pub fn with_auto_layout( self, device: Arc, func: F, ) -> Result, GraphicsPipelineCreationError> where F: FnOnce(&mut [DescriptorSetLayoutCreateInfo]), { let (set_layout_create_infos, push_constant_ranges) = { let stages: SmallVec<[&EntryPoint<'_>; 5]> = [ self.vertex_shader.as_ref().map(|s| &s.0), self.tessellation_shaders.as_ref().map(|s| &s.control.0), self.tessellation_shaders.as_ref().map(|s| &s.evaluation.0), self.geometry_shader.as_ref().map(|s| &s.0), self.fragment_shader.as_ref().map(|s| &s.0), ] .into_iter() .flatten() .collect(); // Produce `DescriptorBindingRequirements` for each binding, by iterating over all // shaders and adding the requirements of each. let mut descriptor_binding_requirements: HashMap< (u32, u32), DescriptorBindingRequirements, > = HashMap::default(); for (loc, reqs) in stages .iter() .flat_map(|shader| shader.descriptor_binding_requirements()) { match descriptor_binding_requirements.entry(loc) { Entry::Occupied(entry) => { // Previous shaders already added requirements, so we merge requirements of // the current shader into the requirements of the previous one. entry.into_mut().merge(reqs).expect("Could not produce an intersection of the shader descriptor requirements"); } Entry::Vacant(entry) => { // No previous shader had this descriptor yet, so we just insert the // requirements. entry.insert(reqs.clone()); } } } // Build a description of a descriptor set layout from the shader requirements, then // feed it to the user-provided closure to allow tweaking. let mut set_layout_create_infos = DescriptorSetLayoutCreateInfo::from_requirements( descriptor_binding_requirements .iter() .map(|(&loc, reqs)| (loc, reqs)), ); func(&mut set_layout_create_infos); // We want to union each push constant range into a set of ranges that do not have intersecting stage flags. // e.g. The range [0, 16) is either made available to Vertex | Fragment or we only make [0, 16) available to // Vertex and a subrange available to Fragment, like [0, 8) let mut range_map = HashMap::default(); for stage in stages.iter() { if let Some(range) = stage.push_constant_requirements() { match range_map.entry((range.offset, range.size)) { Entry::Vacant(entry) => { entry.insert(range.stages); } Entry::Occupied(mut entry) => { *entry.get_mut() = *entry.get() | range.stages; } } } } let push_constant_ranges: Vec<_> = range_map .iter() .map(|((offset, size), stages)| PushConstantRange { stages: *stages, offset: *offset, size: *size, }) .collect(); (set_layout_create_infos, push_constant_ranges) }; let set_layouts = set_layout_create_infos .into_iter() .map(|desc| DescriptorSetLayout::new(device.clone(), desc)) .collect::, _>>()?; let pipeline_layout = PipelineLayout::new( device.clone(), PipelineLayoutCreateInfo { set_layouts, push_constant_ranges, ..Default::default() }, ) .unwrap(); self.with_pipeline_layout(device, pipeline_layout) } /// Builds the graphics pipeline. /// /// Does the same as `build`, except that `build` automatically builds the pipeline layout /// object corresponding to the union of your shaders while this function allows you to specify /// the pipeline layout. #[inline] pub fn with_pipeline_layout( mut self, device: Arc, pipeline_layout: Arc, ) -> Result, GraphicsPipelineCreationError> { let vertex_input_state = self .vertex_input_state .definition(self.vertex_shader.as_ref().unwrap().0.input_interface())?; // If there is one element, duplicate it for all attachments. // TODO: this is undocumented and only exists for compatibility with some of the // deprecated builder methods. Remove it when those methods are gone. if self.color_blend_state.attachments.len() == 1 { let color_attachment_count = match self.render_pass.as_ref().expect("Missing render pass") { PipelineRenderPassType::BeginRenderPass(subpass) => { subpass.subpass_desc().color_attachments.len() } PipelineRenderPassType::BeginRendering(rendering_info) => { rendering_info.color_attachment_formats.len() } }; let element = self.color_blend_state.attachments.pop().unwrap(); self.color_blend_state .attachments .extend(std::iter::repeat(element).take(color_attachment_count)); } let has = { let Self { render_pass, cache: _, vertex_shader, tessellation_shaders, geometry_shader: _, fragment_shader: _, vertex_input_state: _, input_assembly_state: _, tessellation_state: _, viewport_state: _, discard_rectangle_state: _, rasterization_state, multisample_state: _, depth_stencil_state: _, color_blend_state: _, } = &self; let render_pass = render_pass.as_ref().expect("Missing render pass"); let has_pre_rasterization_shader_state = true; let has_vertex_input_state = vertex_shader.is_some(); let has_fragment_shader_state = rasterization_state.rasterizer_discard_enable != StateMode::Fixed(true); let has_fragment_output_state = rasterization_state.rasterizer_discard_enable != StateMode::Fixed(true); let has_tessellation_state = has_pre_rasterization_shader_state && tessellation_shaders.is_some(); let has_viewport_state = has_pre_rasterization_shader_state && has_fragment_shader_state; let has_depth_stencil_state = has_fragment_shader_state && match render_pass { PipelineRenderPassType::BeginRenderPass(subpass) => { subpass.subpass_desc().depth_stencil_attachment.is_some() } PipelineRenderPassType::BeginRendering(rendering_info) => { !has_fragment_output_state || rendering_info.depth_attachment_format.is_some() || rendering_info.stencil_attachment_format.is_some() } }; let has_color_blend_state = has_fragment_output_state && match render_pass { PipelineRenderPassType::BeginRenderPass(subpass) => { !subpass.subpass_desc().color_attachments.is_empty() } PipelineRenderPassType::BeginRendering(rendering_info) => { !rendering_info.color_attachment_formats.is_empty() } }; Has { vertex_input_state: has_vertex_input_state, pre_rasterization_shader_state: has_pre_rasterization_shader_state, tessellation_state: has_tessellation_state, viewport_state: has_viewport_state, fragment_shader_state: has_fragment_shader_state, depth_stencil_state: has_depth_stencil_state, fragment_output_state: has_fragment_output_state, color_blend_state: has_color_blend_state, } }; self.validate_create(&device, &pipeline_layout, &vertex_input_state, has)?; let (handle, descriptor_requirements, dynamic_state, shaders, fragment_tests_stages) = unsafe { self.record_create(&device, &pipeline_layout, &vertex_input_state, has)? }; let Self { mut render_pass, cache: _, vertex_shader: _, tessellation_shaders: _, geometry_shader: _, fragment_shader: _, vertex_input_state: _, input_assembly_state, tessellation_state, viewport_state, discard_rectangle_state, rasterization_state, multisample_state, depth_stencil_state, color_blend_state, } = self; let num_used_descriptor_sets = descriptor_requirements .keys() .map(|loc| loc.0) .max() .map(|x| x + 1) .unwrap_or(0); Ok(Arc::new(GraphicsPipeline { handle, device, id: GraphicsPipeline::next_id(), layout: pipeline_layout, render_pass: render_pass.take().expect("Missing render pass"), shaders, descriptor_binding_requirements: descriptor_requirements, num_used_descriptor_sets, fragment_tests_stages, vertex_input_state, // Can be None if there's a mesh shader, but we don't support that yet input_assembly_state, // Can be None if there's a mesh shader, but we don't support that yet tessellation_state: has.tessellation_state.then_some(tessellation_state), viewport_state: has.viewport_state.then_some(viewport_state), discard_rectangle_state: has .pre_rasterization_shader_state .then_some(discard_rectangle_state), rasterization_state, multisample_state: has.fragment_output_state.then_some(multisample_state), depth_stencil_state: has.depth_stencil_state.then_some(depth_stencil_state), color_blend_state: has.color_blend_state.then_some(color_blend_state), dynamic_state, })) } fn validate_create( &self, device: &Device, pipeline_layout: &PipelineLayout, vertex_input_state: &VertexInputState, has: Has, ) -> Result<(), GraphicsPipelineCreationError> { let physical_device = device.physical_device(); let properties = physical_device.properties(); let Self { render_pass, cache: _, vertex_shader, tessellation_shaders, geometry_shader, fragment_shader, vertex_input_state: _, input_assembly_state, tessellation_state, viewport_state, discard_rectangle_state, rasterization_state, multisample_state, depth_stencil_state, color_blend_state, } = self; let render_pass = render_pass.as_ref().expect("Missing render pass"); let mut shader_stages: SmallVec<[_; 5]> = SmallVec::new(); // VUID-VkGraphicsPipelineCreateInfo-layout-01688 // Checked at pipeline layout creation time. /* Render pass */ match render_pass { PipelineRenderPassType::BeginRenderPass(subpass) => { // VUID-VkGraphicsPipelineCreateInfo-commonparent assert_eq!(device, subpass.render_pass().device().as_ref()); } PipelineRenderPassType::BeginRendering(rendering_info) => { let &PipelineRenderingCreateInfo { view_mask, ref color_attachment_formats, depth_attachment_format, stencil_attachment_format, _ne: _, } = rendering_info; // VUID-VkGraphicsPipelineCreateInfo-dynamicRendering-06576 if !device.enabled_features().dynamic_rendering { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`render_pass` is `PipelineRenderPassType::BeginRendering`", requires_one_of: RequiresOneOf { features: &["dynamic_rendering"], ..Default::default() }, }); } // VUID-VkGraphicsPipelineCreateInfo-multiview-06577 if view_mask != 0 && !device.enabled_features().multiview { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`render_pass` is `PipelineRenderPassType::BeginRendering` \ where `view_mask` is not `0`", requires_one_of: RequiresOneOf { features: &["multiview"], ..Default::default() }, }); } let view_count = u32::BITS - view_mask.leading_zeros(); // VUID-VkGraphicsPipelineCreateInfo-renderPass-06578 if view_count > properties.max_multiview_view_count.unwrap_or(0) { return Err( GraphicsPipelineCreationError::MaxMultiviewViewCountExceeded { view_count, max: properties.max_multiview_view_count.unwrap_or(0), }, ); } if has.fragment_output_state { for (attachment_index, format) in color_attachment_formats .iter() .enumerate() .flat_map(|(i, f)| f.map(|f| (i, f))) { let attachment_index = attachment_index as u32; // VUID-VkGraphicsPipelineCreateInfo-renderPass-06580 format.validate_device(device)?; // VUID-VkGraphicsPipelineCreateInfo-renderPass-06582 // Use unchecked, because all validation has been done above. if !unsafe { physical_device.format_properties_unchecked(format) } .potential_format_features() .intersects(FormatFeatures::COLOR_ATTACHMENT) { return Err( GraphicsPipelineCreationError::ColorAttachmentFormatUsageNotSupported { attachment_index, }, ); } } if let Some(format) = depth_attachment_format { // VUID-VkGraphicsPipelineCreateInfo-renderPass-06583 format.validate_device(device)?; // VUID-VkGraphicsPipelineCreateInfo-renderPass-06585 // Use unchecked, because all validation has been done above. if !unsafe { physical_device.format_properties_unchecked(format) } .potential_format_features() .intersects(FormatFeatures::DEPTH_STENCIL_ATTACHMENT) { return Err( GraphicsPipelineCreationError::DepthAttachmentFormatUsageNotSupported, ); } // VUID-VkGraphicsPipelineCreateInfo-renderPass-06587 if !format.aspects().intersects(ImageAspects::DEPTH) { return Err( GraphicsPipelineCreationError::DepthAttachmentFormatUsageNotSupported, ); } } if let Some(format) = stencil_attachment_format { // VUID-VkGraphicsPipelineCreateInfo-renderPass-06584 format.validate_device(device)?; // VUID-VkGraphicsPipelineCreateInfo-renderPass-06586 // Use unchecked, because all validation has been done above. if !unsafe { physical_device.format_properties_unchecked(format) } .potential_format_features() .intersects(FormatFeatures::DEPTH_STENCIL_ATTACHMENT) { return Err( GraphicsPipelineCreationError::StencilAttachmentFormatUsageNotSupported, ); } // VUID-VkGraphicsPipelineCreateInfo-renderPass-06588 if !format.aspects().intersects(ImageAspects::STENCIL) { return Err( GraphicsPipelineCreationError::StencilAttachmentFormatUsageNotSupported, ); } } if let (Some(depth_format), Some(stencil_format)) = (depth_attachment_format, stencil_attachment_format) { // VUID-VkGraphicsPipelineCreateInfo-renderPass-06589 if depth_format != stencil_format { return Err( GraphicsPipelineCreationError::DepthStencilAttachmentFormatMismatch, ); } } } } } /* Vertex input state */ if has.vertex_input_state { // Vertex input state // VUID-VkGraphicsPipelineCreateInfo-pVertexInputState-04910 { let VertexInputState { bindings, attributes, } = vertex_input_state; // VUID-VkPipelineVertexInputStateCreateInfo-vertexBindingDescriptionCount-00613 if bindings.len() > properties.max_vertex_input_bindings as usize { return Err( GraphicsPipelineCreationError::MaxVertexInputBindingsExceeded { max: properties.max_vertex_input_bindings, obtained: bindings.len() as u32, }, ); } // VUID-VkPipelineVertexInputStateCreateInfo-pVertexBindingDescriptions-00616 // Ensured by HashMap. for (&binding, binding_desc) in bindings { let &VertexInputBindingDescription { stride, input_rate } = binding_desc; // VUID-VkVertexInputBindingDescription-binding-00618 if binding >= properties.max_vertex_input_bindings { return Err( GraphicsPipelineCreationError::MaxVertexInputBindingsExceeded { max: properties.max_vertex_input_bindings, obtained: binding, }, ); } // VUID-VkVertexInputBindingDescription-stride-00619 if stride > properties.max_vertex_input_binding_stride { return Err( GraphicsPipelineCreationError::MaxVertexInputBindingStrideExceeded { binding, max: properties.max_vertex_input_binding_stride, obtained: stride, }, ); } // VUID-VkVertexInputBindingDescription-stride-04456 if device.enabled_extensions().khr_portability_subset && (stride == 0 || stride % properties .min_vertex_input_binding_stride_alignment .unwrap() != 0) { return Err(GraphicsPipelineCreationError::MinVertexInputBindingStrideAlignmentExceeded { binding, max: properties.min_vertex_input_binding_stride_alignment.unwrap(), obtained: binding, }); } match input_rate { VertexInputRate::Instance { divisor } if divisor != 1 => { // VUID-VkVertexInputBindingDivisorDescriptionEXT-vertexAttributeInstanceRateDivisor-02229 if !device .enabled_features() .vertex_attribute_instance_rate_divisor { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`vertex_input_state.bindings` has an element \ where `input_rate` is `VertexInputRate::Instance`, where \ `divisor` is not `1`", requires_one_of: RequiresOneOf { features: &["vertex_attribute_instance_rate_divisor"], ..Default::default() }, }); } // VUID-VkVertexInputBindingDivisorDescriptionEXT-vertexAttributeInstanceRateZeroDivisor-02228 if divisor == 0 && !device .enabled_features() .vertex_attribute_instance_rate_zero_divisor { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`vertex_input_state.bindings` has an element \ where `input_rate` is `VertexInputRate::Instance`, where \ `divisor` is `0`", requires_one_of: RequiresOneOf { features: &["vertex_attribute_instance_rate_zero_divisor"], ..Default::default() }, }); } // VUID-VkVertexInputBindingDivisorDescriptionEXT-divisor-01870 if divisor > properties.max_vertex_attrib_divisor.unwrap() { return Err( GraphicsPipelineCreationError::MaxVertexAttribDivisorExceeded { binding, max: properties.max_vertex_attrib_divisor.unwrap(), obtained: divisor, }, ); } } _ => (), } } // VUID-VkPipelineVertexInputStateCreateInfo-vertexAttributeDescriptionCount-00614 if attributes.len() > properties.max_vertex_input_attributes as usize { return Err( GraphicsPipelineCreationError::MaxVertexInputAttributesExceeded { max: properties.max_vertex_input_attributes, obtained: attributes.len(), }, ); } // VUID-VkPipelineVertexInputStateCreateInfo-pVertexAttributeDescriptions-00617 // Ensured by HashMap with the exception of formats exceeding a single location. // When a format exceeds a single location the location following it (e.g. // R64B64G64_SFLOAT) needs to be unassigned. let unassigned_locations = attributes .iter() .filter(|&(_, attribute_desc)| attribute_desc.format.block_size().unwrap() > 16) .map(|(location, _)| location + 1); for location in unassigned_locations { if !attributes.get(&location).is_none() { return Err( GraphicsPipelineCreationError::VertexInputAttributeInvalidAssignedLocation { location, }, ); } } for (&location, attribute_desc) in attributes { let &VertexInputAttributeDescription { binding, format, offset, } = attribute_desc; // VUID-VkVertexInputAttributeDescription-format-parameter format.validate_device(device)?; // TODO: // VUID-VkVertexInputAttributeDescription-location-00620 // VUID-VkPipelineVertexInputStateCreateInfo-binding-00615 let binding_desc = bindings.get(&binding).ok_or( GraphicsPipelineCreationError::VertexInputAttributeInvalidBinding { location, binding, }, )?; // VUID-VkVertexInputAttributeDescription-offset-00622 if offset > properties.max_vertex_input_attribute_offset { return Err( GraphicsPipelineCreationError::MaxVertexInputAttributeOffsetExceeded { max: properties.max_vertex_input_attribute_offset, obtained: offset, }, ); } // Use unchecked, because all validation has been done above. let format_features = unsafe { device .physical_device() .format_properties_unchecked(format) .buffer_features }; // VUID-VkVertexInputAttributeDescription-format-00623 if !format_features.intersects(FormatFeatures::VERTEX_BUFFER) { return Err( GraphicsPipelineCreationError::VertexInputAttributeUnsupportedFormat { location, format, }, ); } // VUID-VkVertexInputAttributeDescription-vertexAttributeAccessBeyondStride-04457 if device.enabled_extensions().khr_portability_subset && !device .enabled_features() .vertex_attribute_access_beyond_stride && offset as DeviceSize + format.block_size().unwrap() > binding_desc.stride as DeviceSize { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "this device is a portability subset device, and \ `vertex_input_state.attributes` has an element where \ `offset + format.block_size()` is greater than the `stride` of \ `binding`", requires_one_of: RequiresOneOf { features: &["vertex_attribute_access_beyond_stride"], ..Default::default() }, }); } } } // Input assembly state // VUID-VkGraphicsPipelineCreateInfo-pStages-02098 { let &InputAssemblyState { topology, primitive_restart_enable, } = input_assembly_state; match topology { PartialStateMode::Fixed(topology) => { // VUID-VkPipelineInputAssemblyStateCreateInfo-topology-parameter topology.validate_device(device)?; match topology { PrimitiveTopology::TriangleFan => { // VUID-VkPipelineInputAssemblyStateCreateInfo-triangleFans-04452 if device.enabled_extensions().khr_portability_subset && !device.enabled_features().triangle_fans { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "this device is a portability subset \ device, and `input_assembly_state.topology` is \ `StateMode::Fixed(PrimitiveTopology::TriangleFan)`", requires_one_of: RequiresOneOf { features: &["triangle_fans"], ..Default::default() }, }); } } PrimitiveTopology::LineListWithAdjacency | PrimitiveTopology::LineStripWithAdjacency | PrimitiveTopology::TriangleListWithAdjacency | PrimitiveTopology::TriangleStripWithAdjacency => { // VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00429 if !device.enabled_features().geometry_shader { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`input_assembly_state.topology` is \ `StateMode::Fixed(PrimitiveTopology::*WithAdjacency)`", requires_one_of: RequiresOneOf { features: &["geometry_shader"], ..Default::default() }, }); } } PrimitiveTopology::PatchList => { // VUID-VkPipelineInputAssemblyStateCreateInfo-topology-00430 if !device.enabled_features().tessellation_shader { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`input_assembly_state.topology` is \ `StateMode::Fixed(PrimitiveTopology::PatchList)`", requires_one_of: RequiresOneOf { features: &["tessellation_shader"], ..Default::default() }, }); } // TODO: // VUID-VkGraphicsPipelineCreateInfo-topology-00737 } _ => (), } } PartialStateMode::Dynamic(topology_class) => { // VUID-VkPipelineInputAssemblyStateCreateInfo-topology-parameter topology_class.example().validate_device(device)?; // VUID? if !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`input_assembly_state.topology` is \ `PartialStateMode::Dynamic`", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state"], ..Default::default() }, }); } } } match primitive_restart_enable { StateMode::Fixed(primitive_restart_enable) => { if primitive_restart_enable { match topology { PartialStateMode::Fixed( PrimitiveTopology::PointList | PrimitiveTopology::LineList | PrimitiveTopology::TriangleList | PrimitiveTopology::LineListWithAdjacency | PrimitiveTopology::TriangleListWithAdjacency, ) => { // VUID-VkPipelineInputAssemblyStateCreateInfo-topology-06252 if !device.enabled_features().primitive_topology_list_restart { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`input_assembly_state.primitive_restart_enable` \ is `StateMode::Fixed(true)` and \ `input_assembly_state.topology` is \ `StateMode::Fixed(PrimitiveTopology::*List)`", requires_one_of: RequiresOneOf { features: &["primitive_topology_list_restart"], ..Default::default() }, }); } } PartialStateMode::Fixed(PrimitiveTopology::PatchList) => { // VUID-VkPipelineInputAssemblyStateCreateInfo-topology-06253 if !device .enabled_features() .primitive_topology_patch_list_restart { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`input_assembly_state.primitive_restart_enable` \ is `StateMode::Fixed(true)` and \ `input_assembly_state.topology` is \ `StateMode::Fixed(PrimitiveTopology::PatchList)`", requires_one_of: RequiresOneOf { features: &["primitive_topology_patch_list_restart"], ..Default::default() }, }); } } _ => (), } } } StateMode::Dynamic => { // VUID? if !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state2) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`input_assembly_state.primitive_restart_enable` is \ `StateMode::Dynamic`", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state2"], ..Default::default() }, }); } } }; } } /* Pre-rasterization shader state */ if has.pre_rasterization_shader_state { // Vertex shader if let Some((entry_point, specialization_data)) = vertex_shader { shader_stages.push(ShaderStageInfo { entry_point, specialization_map_entries: Vss::descriptors(), _specialization_data: unsafe { std::slice::from_raw_parts( specialization_data as *const _ as *const u8, size_of_val(specialization_data), ) }, }); match entry_point.execution() { ShaderExecution::Vertex => (), _ => return Err(GraphicsPipelineCreationError::WrongShaderType), } // VUID? // Check that the vertex input state contains attributes for all the shader's input // variables. for element in entry_point.input_interface().elements() { assert!(!element.ty.is_64bit); // TODO: implement let location_range = element.location..element.location + element.ty.num_locations(); for location in location_range { let attribute_desc = match vertex_input_state.attributes.get(&location) { Some(attribute_desc) => attribute_desc, None => return Err( GraphicsPipelineCreationError::VertexInputAttributeMissing { location, }, ), }; // TODO: Check component assignments too. Multiple variables can occupy the same // location but in different components. let shader_type = element.ty.base_type; let attribute_type = attribute_desc.format.type_color().unwrap(); if !matches!( (shader_type, attribute_type), ( ShaderScalarType::Float, NumericType::SFLOAT | NumericType::UFLOAT | NumericType::SNORM | NumericType::UNORM | NumericType::SSCALED | NumericType::USCALED | NumericType::SRGB, ) | (ShaderScalarType::Sint, NumericType::SINT) | (ShaderScalarType::Uint, NumericType::UINT) ) { return Err( GraphicsPipelineCreationError::VertexInputAttributeIncompatibleFormat { location, shader_type, attribute_type, }, ); } } } // TODO: // VUID-VkPipelineShaderStageCreateInfo-stage-00712 } else { // VUID-VkGraphicsPipelineCreateInfo-stage-02096 panic!("Missing vertex shader"); // TODO: return error } // Tessellation shaders & tessellation state if let Some(tessellation_shaders) = tessellation_shaders { // VUID-VkGraphicsPipelineCreateInfo-pStages-00729 // VUID-VkGraphicsPipelineCreateInfo-pStages-00730 // Ensured by the definition of TessellationShaders. // FIXME: must check that the control shader and evaluation shader are compatible // VUID-VkPipelineShaderStageCreateInfo-stage-00705 if !device.enabled_features().tessellation_shader { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`tessellation_shaders` are provided", requires_one_of: RequiresOneOf { features: &["tessellation_shader"], ..Default::default() }, }); } { let (entry_point, specialization_data) = &tessellation_shaders.control; shader_stages.push(ShaderStageInfo { entry_point, specialization_map_entries: Tcss::descriptors(), _specialization_data: unsafe { std::slice::from_raw_parts( specialization_data as *const _ as *const u8, size_of_val(specialization_data), ) }, }); match entry_point.execution() { ShaderExecution::TessellationControl => (), _ => return Err(GraphicsPipelineCreationError::WrongShaderType), } } { let (entry_point, specialization_data) = &tessellation_shaders.evaluation; shader_stages.push(ShaderStageInfo { entry_point, specialization_map_entries: Tess::descriptors(), _specialization_data: unsafe { std::slice::from_raw_parts( specialization_data as *const _ as *const u8, size_of_val(specialization_data), ) }, }); match entry_point.execution() { ShaderExecution::TessellationEvaluation => (), _ => return Err(GraphicsPipelineCreationError::WrongShaderType), } } if !device.enabled_features().multiview_tessellation_shader { let view_mask = match render_pass { PipelineRenderPassType::BeginRenderPass(subpass) => { subpass.render_pass().views_used() } PipelineRenderPassType::BeginRendering(rendering_info) => { rendering_info.view_mask } }; // VUID-VkGraphicsPipelineCreateInfo-renderPass-06047 // VUID-VkGraphicsPipelineCreateInfo-renderPass-06057 if view_mask != 0 { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`tessellation_shaders` are provided and `render_pass` \ has a subpass where `view_mask` is not `0`", requires_one_of: RequiresOneOf { features: &["multiview_tessellation_shader"], ..Default::default() }, }); } } // TODO: // VUID-VkPipelineShaderStageCreateInfo-stage-00713 // VUID-VkGraphicsPipelineCreateInfo-pStages-00732 // VUID-VkGraphicsPipelineCreateInfo-pStages-00733 // VUID-VkGraphicsPipelineCreateInfo-pStages-00734 // VUID-VkGraphicsPipelineCreateInfo-pStages-00735 } // Geometry shader if let Some((entry_point, specialization_data)) = geometry_shader { shader_stages.push(ShaderStageInfo { entry_point, specialization_map_entries: Gss::descriptors(), _specialization_data: unsafe { std::slice::from_raw_parts( specialization_data as *const _ as *const u8, size_of_val(specialization_data), ) }, }); // VUID-VkPipelineShaderStageCreateInfo-stage-00704 if !device.enabled_features().geometry_shader { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`geometry_shader` is provided", requires_one_of: RequiresOneOf { features: &["geometry_shader"], ..Default::default() }, }); } let input = match entry_point.execution() { ShaderExecution::Geometry(execution) => execution.input, _ => return Err(GraphicsPipelineCreationError::WrongShaderType), }; if let PartialStateMode::Fixed(topology) = input_assembly_state.topology { // VUID-VkGraphicsPipelineCreateInfo-pStages-00738 if !input.is_compatible_with(topology) { return Err( GraphicsPipelineCreationError::TopologyNotMatchingGeometryShader, ); } } if !device.enabled_features().multiview_geometry_shader { let view_mask = match render_pass { PipelineRenderPassType::BeginRenderPass(subpass) => { subpass.render_pass().views_used() } PipelineRenderPassType::BeginRendering(rendering_info) => { rendering_info.view_mask } }; // VUID-VkGraphicsPipelineCreateInfo-renderPass-06048 // VUID-VkGraphicsPipelineCreateInfo-renderPass-06058 if view_mask != 0 { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`geometry_shader` is provided and `render_pass` has a \ subpass where `view_mask` is not `0`", requires_one_of: RequiresOneOf { features: &["multiview_geometry_shader"], ..Default::default() }, }); } } // TODO: // VUID-VkPipelineShaderStageCreateInfo-stage-00714 // VUID-VkPipelineShaderStageCreateInfo-stage-00715 // VUID-VkGraphicsPipelineCreateInfo-pStages-00739 } // Rasterization state // VUID? { let &RasterizationState { depth_clamp_enable, rasterizer_discard_enable, polygon_mode, cull_mode, front_face, depth_bias, line_width, line_rasterization_mode, line_stipple, } = rasterization_state; // VUID-VkPipelineRasterizationStateCreateInfo-polygonMode-parameter polygon_mode.validate_device(device)?; // VUID-VkPipelineRasterizationStateCreateInfo-depthClampEnable-00782 if depth_clamp_enable && !device.enabled_features().depth_clamp { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.depth_clamp_enable` is set", requires_one_of: RequiresOneOf { features: &["depth_clamp"], ..Default::default() }, }); } match rasterizer_discard_enable { StateMode::Dynamic => { // VUID? if !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state2) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.rasterizer_discard_enable` is \ `StateMode::Dynamic`", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state"], ..Default::default() }, }); } } StateMode::Fixed(false) => { // VUID-VkPipelineRasterizationStateCreateInfo-pointPolygons-04458 if device.enabled_extensions().khr_portability_subset && !device.enabled_features().point_polygons && polygon_mode == PolygonMode::Point { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "this device is a portability subset device, \ `rasterization_state.rasterizer_discard_enable` is \ `StateMode::Fixed(false)` and \ `rasterization_state.polygon_mode` is `PolygonMode::Point`", requires_one_of: RequiresOneOf { features: &["point_polygons"], ..Default::default() }, }); } } _ => (), } // VUID-VkPipelineRasterizationStateCreateInfo-polygonMode-01507 if polygon_mode != PolygonMode::Fill && !device.enabled_features().fill_mode_non_solid { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.polygon_mode` is not \ `PolygonMode::Fill`", requires_one_of: RequiresOneOf { features: &["fill_mode_non_solid"], ..Default::default() }, }); } match cull_mode { StateMode::Fixed(cull_mode) => { // VUID-VkPipelineRasterizationStateCreateInfo-cullMode-parameter cull_mode.validate_device(device)?; } StateMode::Dynamic => { // VUID? if !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.cull_mode` is \ `StateMode::Dynamic`", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state"], ..Default::default() }, }); } } } match front_face { StateMode::Fixed(front_face) => { // VUID-VkPipelineRasterizationStateCreateInfo-frontFace-parameter front_face.validate_device(device)?; } StateMode::Dynamic => { // VUID? if !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.front_face` is \ `StateMode::Dynamic`", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state"], ..Default::default() }, }); } } } if let Some(depth_bias_state) = depth_bias { let DepthBiasState { enable_dynamic, bias, } = depth_bias_state; // VUID? if enable_dynamic && !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state2) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.depth_bias` is \ `Some(depth_bias_state)`, where `depth_bias_state.enable_dynamic` \ is set", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state2"], ..Default::default() }, }); } // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00754 if matches!(bias, StateMode::Fixed(bias) if bias.clamp != 0.0) && !device.enabled_features().depth_bias_clamp { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.depth_bias` is \ `Some(depth_bias_state)`, where `depth_bias_state.bias` is \ `StateMode::Fixed(bias)`, where `bias.clamp` is not `0.0`", requires_one_of: RequiresOneOf { features: &["depth_bias_clamp"], ..Default::default() }, }); } } // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-00749 if matches!(line_width, StateMode::Fixed(line_width) if line_width != 1.0) && !device.enabled_features().wide_lines { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.line_width` is \ `StateMode::Fixed(line_width)`, where `line_width` is not `1.0`", requires_one_of: RequiresOneOf { features: &["wide_lines"], ..Default::default() }, }); } if device.enabled_extensions().ext_line_rasterization { // VUID-VkPipelineRasterizationLineStateCreateInfoEXT-lineRasterizationMode-parameter line_rasterization_mode.validate_device(device)?; match line_rasterization_mode { LineRasterizationMode::Default => (), LineRasterizationMode::Rectangular => { // VUID-VkPipelineRasterizationLineStateCreateInfoEXT-lineRasterizationMode-02768 if !device.enabled_features().rectangular_lines { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.line_rasterization_mode` \ is `LineRasterizationMode::Rectangular`", requires_one_of: RequiresOneOf { features: &["rectangular_lines"], ..Default::default() }, }); } } LineRasterizationMode::Bresenham => { // VUID-VkPipelineRasterizationLineStateCreateInfoEXT-lineRasterizationMode-02769 if !device.enabled_features().bresenham_lines { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.line_rasterization_mode` \ is `LineRasterizationMode::Bresenham`", requires_one_of: RequiresOneOf { features: &["bresenham_lines"], ..Default::default() }, }); } } LineRasterizationMode::RectangularSmooth => { // VUID-VkPipelineRasterizationLineStateCreateInfoEXT-lineRasterizationMode-02770 if !device.enabled_features().smooth_lines { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.line_rasterization_mode` \ is `LineRasterizationMode::RectangularSmooth`", requires_one_of: RequiresOneOf { features: &["smooth_lines"], ..Default::default() }, }); } } } if let Some(line_stipple) = line_stipple { match line_rasterization_mode { LineRasterizationMode::Default => { // VUID-VkPipelineRasterizationLineStateCreateInfoEXT-stippledLineEnable-02774 if !device.enabled_features().stippled_rectangular_lines { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.line_stipple` is \ `Some` and \ `rasterization_state.line_rasterization_mode` \ is `LineRasterizationMode::Default`", requires_one_of: RequiresOneOf { features: &["stippled_rectangular_lines"], ..Default::default() }, }); } // VUID-VkPipelineRasterizationLineStateCreateInfoEXT-stippledLineEnable-02774 if !properties.strict_lines { return Err( GraphicsPipelineCreationError::StrictLinesNotSupported, ); } } LineRasterizationMode::Rectangular => { // VUID-VkPipelineRasterizationLineStateCreateInfoEXT-stippledLineEnable-02771 if !device.enabled_features().stippled_rectangular_lines { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.line_stipple` is \ `Some` and \ `rasterization_state.line_rasterization_mode` \ is `LineRasterizationMode::Rectangular`", requires_one_of: RequiresOneOf { features: &["stippled_rectangular_lines"], ..Default::default() }, }); } } LineRasterizationMode::Bresenham => { // VUID-VkPipelineRasterizationLineStateCreateInfoEXT-stippledLineEnable-02772 if !device.enabled_features().stippled_bresenham_lines { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.line_stipple` is \ `Some` and \ `rasterization_state.line_rasterization_mode` \ is `LineRasterizationMode::Bresenham`", requires_one_of: RequiresOneOf { features: &["stippled_bresenham_lines"], ..Default::default() }, }); } } LineRasterizationMode::RectangularSmooth => { // VUID-VkPipelineRasterizationLineStateCreateInfoEXT-stippledLineEnable-02773 if !device.enabled_features().stippled_smooth_lines { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.line_stipple` is \ `Some` and \ `rasterization_state.line_rasterization_mode` \ is `LineRasterizationMode::RectangularSmooth`", requires_one_of: RequiresOneOf { features: &["stippled_smooth_lines"], ..Default::default() }, }); } } } if let StateMode::Fixed(line_stipple) = line_stipple { // VUID-VkGraphicsPipelineCreateInfo-stippledLineEnable-02767 assert!(line_stipple.factor >= 1 && line_stipple.factor <= 256); // TODO: return error? } } } else { if line_rasterization_mode != LineRasterizationMode::Default { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.line_rasterization_mode` is not \ `LineRasterizationMode::Default`", requires_one_of: RequiresOneOf { device_extensions: &["ext_line_rasterization"], ..Default::default() }, }); } if line_stipple.is_some() { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`rasterization_state.line_stipple` is `Some`", requires_one_of: RequiresOneOf { device_extensions: &["ext_line_rasterization"], ..Default::default() }, }); } } } // Discard rectangle state { let DiscardRectangleState { mode, rectangles } = discard_rectangle_state; if device.enabled_extensions().ext_discard_rectangles { // VUID-VkPipelineDiscardRectangleStateCreateInfoEXT-discardRectangleMode-parameter mode.validate_device(device)?; let discard_rectangle_count = match rectangles { PartialStateMode::Dynamic(count) => *count, PartialStateMode::Fixed(rectangles) => rectangles.len() as u32, }; // VUID-VkPipelineDiscardRectangleStateCreateInfoEXT-discardRectangleCount-00582 if discard_rectangle_count > properties.max_discard_rectangles.unwrap() { return Err( GraphicsPipelineCreationError::MaxDiscardRectanglesExceeded { max: properties.max_discard_rectangles.unwrap(), obtained: discard_rectangle_count, }, ); } } else { let error = match rectangles { PartialStateMode::Dynamic(_) => true, PartialStateMode::Fixed(rectangles) => !rectangles.is_empty(), }; if error { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`discard_rectangle_state.rectangles` is not \ `PartialStateMode::Fixed(vec![])`", requires_one_of: RequiresOneOf { device_extensions: &["ext_discard_rectangles"], ..Default::default() }, }); } } } // TODO: // VUID-VkPipelineShaderStageCreateInfo-stage-02596 // VUID-VkPipelineShaderStageCreateInfo-stage-02597 // VUID-VkGraphicsPipelineCreateInfo-pStages-00740 // VUID-VkGraphicsPipelineCreateInfo-renderPass-06049 // VUID-VkGraphicsPipelineCreateInfo-renderPass-06050 // VUID-VkGraphicsPipelineCreateInfo-renderPass-06059 } // VUID-VkGraphicsPipelineCreateInfo-pStages-00731 if has.tessellation_state { let &TessellationState { patch_control_points, } = tessellation_state; // VUID-VkGraphicsPipelineCreateInfo-pStages-00736 if !matches!( input_assembly_state.topology, PartialStateMode::Dynamic(PrimitiveTopologyClass::Patch) | PartialStateMode::Fixed(PrimitiveTopology::PatchList) ) { return Err(GraphicsPipelineCreationError::InvalidPrimitiveTopology); } match patch_control_points { StateMode::Fixed(patch_control_points) => { // VUID-VkPipelineTessellationStateCreateInfo-patchControlPoints-01214 if patch_control_points == 0 || patch_control_points > properties.max_tessellation_patch_size { return Err(GraphicsPipelineCreationError::InvalidNumPatchControlPoints); } } StateMode::Dynamic => { // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04870 if !device .enabled_features() .extended_dynamic_state2_patch_control_points { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`tessellation_state.patch_control_points` is \ `StateMode::Dynamic`", requires_one_of: RequiresOneOf { features: &["extended_dynamic_state2_patch_control_points"], ..Default::default() }, }); } } }; } // Viewport state // VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00750 // VUID-VkGraphicsPipelineCreateInfo-pViewportState-04892 if has.viewport_state { let (viewport_count, scissor_count) = match viewport_state { ViewportState::Fixed { data } => { let count = data.len() as u32; assert!(count != 0); // TODO: return error? for (viewport, _) in data { for i in 0..2 { if viewport.dimensions[i] > properties.max_viewport_dimensions[i] as f32 { return Err( GraphicsPipelineCreationError::MaxViewportDimensionsExceeded, ); } if viewport.origin[i] < properties.viewport_bounds_range[0] || viewport.origin[i] + viewport.dimensions[i] > properties.viewport_bounds_range[1] { return Err(GraphicsPipelineCreationError::ViewportBoundsExceeded); } } } // TODO: // VUID-VkPipelineViewportStateCreateInfo-offset-02822 // VUID-VkPipelineViewportStateCreateInfo-offset-02823 (count, count) } ViewportState::FixedViewport { viewports, scissor_count_dynamic, } => { let viewport_count = viewports.len() as u32; // VUID-VkPipelineViewportStateCreateInfo-scissorCount-04136 assert!(viewport_count != 0); // TODO: return error? for viewport in viewports { for i in 0..2 { if viewport.dimensions[i] > properties.max_viewport_dimensions[i] as f32 { return Err( GraphicsPipelineCreationError::MaxViewportDimensionsExceeded, ); } if viewport.origin[i] < properties.viewport_bounds_range[0] || viewport.origin[i] + viewport.dimensions[i] > properties.viewport_bounds_range[1] { return Err(GraphicsPipelineCreationError::ViewportBoundsExceeded); } } } // VUID-VkPipelineViewportStateCreateInfo-scissorCount-04136 let scissor_count = if *scissor_count_dynamic { if !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`viewport_state` is \ `ViewportState::FixedViewport`, where `scissor_count_dynamic` \ is set", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state"], ..Default::default() }, }); } // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-03380 0 } else { viewport_count }; (viewport_count, scissor_count) } ViewportState::FixedScissor { scissors, viewport_count_dynamic, } => { let scissor_count = scissors.len() as u32; // VUID-VkPipelineViewportStateCreateInfo-viewportCount-04135 assert!(scissor_count != 0); // TODO: return error? // VUID-VkPipelineViewportStateCreateInfo-viewportCount-04135 let viewport_count = if *viewport_count_dynamic { if !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`viewport_state` is \ `ViewportState::FixedScissor`, where `viewport_count_dynamic` \ is set", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state"], ..Default::default() }, }); } // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-03379 0 } else { scissor_count }; // TODO: // VUID-VkPipelineViewportStateCreateInfo-offset-02822 // VUID-VkPipelineViewportStateCreateInfo-offset-02823 (viewport_count, scissor_count) } &ViewportState::Dynamic { count, viewport_count_dynamic, scissor_count_dynamic, } => { // VUID-VkPipelineViewportStateCreateInfo-viewportCount-04135 // VUID-VkPipelineViewportStateCreateInfo-scissorCount-04136 if !(viewport_count_dynamic && scissor_count_dynamic) { assert!(count != 0); // TODO: return error? } // VUID-VkPipelineViewportStateCreateInfo-viewportCount-04135 let viewport_count = if viewport_count_dynamic { if !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`viewport_state` is \ `ViewportState::Dynamic`, where `viewport_count_dynamic` \ is set", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state"], ..Default::default() }, }); } // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-03379 0 } else { count }; // VUID-VkPipelineViewportStateCreateInfo-scissorCount-04136 let scissor_count = if scissor_count_dynamic { if !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`viewport_state` is \ `ViewportState::Dynamic`, where `scissor_count_dynamic` \ is set", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state"], ..Default::default() }, }); } // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-03380 0 } else { count }; (viewport_count, scissor_count) } }; // VUID-VkPipelineViewportStateCreateInfo-scissorCount-04134 // Ensured by the definition of `ViewportState`. let viewport_scissor_count = u32::max(viewport_count, scissor_count); // VUID-VkPipelineViewportStateCreateInfo-viewportCount-01216 // VUID-VkPipelineViewportStateCreateInfo-scissorCount-01217 if viewport_scissor_count > 1 && !device.enabled_features().multi_viewport { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`viewport_state` has a fixed viewport/scissor count that is \ greater than `1`", requires_one_of: RequiresOneOf { features: &["multi_viewport"], ..Default::default() }, }); } // VUID-VkPipelineViewportStateCreateInfo-viewportCount-01218 // VUID-VkPipelineViewportStateCreateInfo-scissorCount-01219 if viewport_scissor_count > properties.max_viewports { return Err(GraphicsPipelineCreationError::MaxViewportsExceeded { obtained: viewport_scissor_count, max: properties.max_viewports, }); } // TODO: // VUID-VkGraphicsPipelineCreateInfo-primitiveFragmentShadingRateWithMultipleViewports-04503 // VUID-VkGraphicsPipelineCreateInfo-primitiveFragmentShadingRateWithMultipleViewports-04504 } /* Fragment shader state */ if has.fragment_shader_state { // Fragment shader if let Some((entry_point, specialization_data)) = fragment_shader { shader_stages.push(ShaderStageInfo { entry_point, specialization_map_entries: Fss::descriptors(), _specialization_data: unsafe { std::slice::from_raw_parts( specialization_data as *const _ as *const u8, size_of_val(specialization_data), ) }, }); match entry_point.execution() { ShaderExecution::Fragment(_) => (), _ => return Err(GraphicsPipelineCreationError::WrongShaderType), } // Check that the subpass can accept the output of the fragment shader. match render_pass { PipelineRenderPassType::BeginRenderPass(subpass) => { if !subpass.is_compatible_with(entry_point.output_interface()) { return Err( GraphicsPipelineCreationError::FragmentShaderRenderPassIncompatible, ); } } PipelineRenderPassType::BeginRendering(_) => { // TODO: } } // TODO: // VUID-VkPipelineShaderStageCreateInfo-stage-00718 // VUID-VkPipelineShaderStageCreateInfo-stage-06685 // VUID-VkPipelineShaderStageCreateInfo-stage-06686 // VUID-VkGraphicsPipelineCreateInfo-pStages-01565 // VUID-VkGraphicsPipelineCreateInfo-renderPass-06056 // VUID-VkGraphicsPipelineCreateInfo-renderPass-06061 } else { // TODO: should probably error out here at least under some circumstances? // VUID? } // TODO: // VUID-VkGraphicsPipelineCreateInfo-renderPass-06038 } // Depth/stencil state // VUID-VkGraphicsPipelineCreateInfo-renderPass-06043 // VUID-VkGraphicsPipelineCreateInfo-renderPass-06053 // VUID-VkGraphicsPipelineCreateInfo-renderPass-06590 if has.depth_stencil_state { let DepthStencilState { depth, depth_bounds, stencil, } = depth_stencil_state; if let Some(depth_state) = depth { let &DepthState { enable_dynamic, write_enable, compare_op, } = depth_state; let has_depth_attachment = match render_pass { PipelineRenderPassType::BeginRenderPass(subpass) => subpass.has_depth(), PipelineRenderPassType::BeginRendering(rendering_info) => { rendering_info.depth_attachment_format.is_some() } }; // VUID? if !has_depth_attachment { return Err(GraphicsPipelineCreationError::NoDepthAttachment); } // VUID? if enable_dynamic && !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`depth_stencil_state.depth` is `Some(depth_state)`, where \ `depth_state.enable_dynamic` is set", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state"], ..Default::default() }, }); } match write_enable { StateMode::Fixed(write_enable) => { match render_pass { PipelineRenderPassType::BeginRenderPass(subpass) => { // VUID-VkGraphicsPipelineCreateInfo-renderPass-06039 if write_enable && !subpass.has_writable_depth() { return Err(GraphicsPipelineCreationError::NoDepthAttachment); } } PipelineRenderPassType::BeginRendering(_) => { // No VUID? } } } StateMode::Dynamic => { // VUID? if !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`depth_stencil_state.depth` is \ `Some(depth_state)`, where `depth_state.write_enable` is \ `StateMode::Dynamic`", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state"], ..Default::default() }, }); } } } match compare_op { StateMode::Fixed(compare_op) => { // VUID-VkPipelineDepthStencilStateCreateInfo-depthCompareOp-parameter compare_op.validate_device(device)?; } StateMode::Dynamic => { // VUID? if !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`depth_stencil_state.depth` is \ `Some(depth_state)`, where `depth_state.compare_op` is \ `StateMode::Dynamic`", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state"], ..Default::default() }, }); } } } } if let Some(depth_bounds_state) = depth_bounds { let DepthBoundsState { enable_dynamic, bounds, } = depth_bounds_state; // VUID-VkPipelineDepthStencilStateCreateInfo-depthBoundsTestEnable-00598 if !device.enabled_features().depth_bounds { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`depth_stencil_state.depth_bounds` is `Some`", requires_one_of: RequiresOneOf { features: &["depth_bounds"], ..Default::default() }, }); } // VUID? if *enable_dynamic && !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`depth_stencil_state.depth_bounds` is \ `Some(depth_bounds_state)`, where `depth_bounds_state.enable_dynamic` \ is set", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state"], ..Default::default() }, }); } if let StateMode::Fixed(bounds) = bounds { // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-02510 if !device.enabled_extensions().ext_depth_range_unrestricted && !(0.0..1.0).contains(bounds.start()) && !(0.0..1.0).contains(bounds.end()) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`depth_stencil_state.depth_bounds` is \ `Some(depth_bounds_state)`, where `depth_bounds_state.bounds` is \ not between `0.0` and `1.0` inclusive", requires_one_of: RequiresOneOf { device_extensions: &["ext_depth_range_unrestricted"], ..Default::default() }, }); } } } if let Some(stencil_state) = stencil { let StencilState { enable_dynamic, front, back, } = stencil_state; let has_stencil_attachment = match render_pass { PipelineRenderPassType::BeginRenderPass(subpass) => subpass.has_stencil(), PipelineRenderPassType::BeginRendering(rendering_info) => { rendering_info.stencil_attachment_format.is_some() } }; if !has_stencil_attachment { return Err(GraphicsPipelineCreationError::NoStencilAttachment); } // VUID? if *enable_dynamic && !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`depth_stencil_state.stencil` is `Some(stencil_state)`, \ where `stencil_state.enable_dynamic` is set", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state"], ..Default::default() }, }); } match (front.ops, back.ops) { (StateMode::Fixed(front_ops), StateMode::Fixed(back_ops)) => { for ops in [front_ops, back_ops] { let StencilOps { fail_op, pass_op, depth_fail_op, compare_op, } = ops; // VUID-VkStencilOpState-failOp-parameter fail_op.validate_device(device)?; // VUID-VkStencilOpState-passOp-parameter pass_op.validate_device(device)?; // VUID-VkStencilOpState-depthFailOp-parameter depth_fail_op.validate_device(device)?; // VUID-VkStencilOpState-compareOp-parameter compare_op.validate_device(device)?; } } (StateMode::Dynamic, StateMode::Dynamic) => { // VUID? if !(device.api_version() >= Version::V1_3 || device.enabled_features().extended_dynamic_state) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`depth_stencil_state.stencil` is \ `Some(stencil_state)`, where `stencil_state.front.ops` and \ `stencil_state.back.ops` are `StateMode::Dynamic`", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_3), features: &["extended_dynamic_state"], ..Default::default() }, }); } } _ => return Err(GraphicsPipelineCreationError::WrongStencilState), } if !matches!( (front.compare_mask, back.compare_mask), (StateMode::Fixed(_), StateMode::Fixed(_)) | (StateMode::Dynamic, StateMode::Dynamic) ) { return Err(GraphicsPipelineCreationError::WrongStencilState); } if !matches!( (front.write_mask, back.write_mask), (StateMode::Fixed(_), StateMode::Fixed(_)) | (StateMode::Dynamic, StateMode::Dynamic) ) { return Err(GraphicsPipelineCreationError::WrongStencilState); } match (front.reference, back.reference) { (StateMode::Fixed(front_reference), StateMode::Fixed(back_reference)) => { // VUID-VkPipelineDepthStencilStateCreateInfo-separateStencilMaskRef-04453 if device.enabled_extensions().khr_portability_subset && !device.enabled_features().separate_stencil_mask_ref && matches!( rasterization_state.cull_mode, StateMode::Fixed(CullMode::None) ) && front_reference != back_reference { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "this device is a portability subset device, \ `rasterization_state.cull_mode` is \ `StateMode::Fixed(CullMode::None)`, and \ `depth_stencil_state.stencil` is `Some(stencil_state)`, \ where `stencil_state.front.reference` does not equal \ `stencil_state.back.reference`", requires_one_of: RequiresOneOf { features: &["separate_stencil_mask_ref"], ..Default::default() }, }); } } (StateMode::Dynamic, StateMode::Dynamic) => (), _ => return Err(GraphicsPipelineCreationError::WrongStencilState), } // TODO: // VUID-VkGraphicsPipelineCreateInfo-renderPass-06040 } } /* Fragment output state */ if has.fragment_output_state { // Multisample state // VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00751 { let &MultisampleState { rasterization_samples, sample_shading, sample_mask: _, alpha_to_coverage_enable: _, alpha_to_one_enable, } = multisample_state; // VUID-VkPipelineMultisampleStateCreateInfo-rasterizationSamples-parameter rasterization_samples.validate_device(device)?; match render_pass { PipelineRenderPassType::BeginRenderPass(subpass) => { if let Some(samples) = subpass.num_samples() { // VUID-VkGraphicsPipelineCreateInfo-subpass-00757 if rasterization_samples != samples { return Err(GraphicsPipelineCreationError::MultisampleRasterizationSamplesMismatch); } } // TODO: // VUID-VkGraphicsPipelineCreateInfo-subpass-00758 // VUID-VkGraphicsPipelineCreateInfo-subpass-01505 // VUID-VkGraphicsPipelineCreateInfo-subpass-01411 // VUID-VkGraphicsPipelineCreateInfo-subpass-01412 } PipelineRenderPassType::BeginRendering(_) => { // No equivalent VUIDs for dynamic rendering, as no sample count information // is provided until `begin_rendering`. } } if let Some(min_sample_shading) = sample_shading { // VUID-VkPipelineMultisampleStateCreateInfo-sampleShadingEnable-00784 if !device.enabled_features().sample_rate_shading { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`multisample_state.sample_shading` is `Some`", requires_one_of: RequiresOneOf { features: &["sample_rate_shading"], ..Default::default() }, }); } // VUID-VkPipelineMultisampleStateCreateInfo-minSampleShading-00786 // TODO: return error? assert!((0.0..=1.0).contains(&min_sample_shading)); } // VUID-VkPipelineMultisampleStateCreateInfo-alphaToOneEnable-00785 if alpha_to_one_enable && !device.enabled_features().alpha_to_one { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`multisample_state.alpha_to_one_enable` is set", requires_one_of: RequiresOneOf { features: &["alpha_to_one"], ..Default::default() }, }); } // TODO: // VUID-VkGraphicsPipelineCreateInfo-lineRasterizationMode-02766 } } // Color blend state // VUID-VkGraphicsPipelineCreateInfo-renderPass-06044 // VUID-VkGraphicsPipelineCreateInfo-renderPass-06054 if has.color_blend_state { let ColorBlendState { logic_op, attachments, blend_constants: _, } = color_blend_state; if let Some(logic_op) = logic_op { // VUID-VkPipelineColorBlendStateCreateInfo-logicOpEnable-00606 if !device.enabled_features().logic_op { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`color_blend_state.logic_op` is `Some`", requires_one_of: RequiresOneOf { features: &["logic_op"], ..Default::default() }, }); } match logic_op { StateMode::Fixed(logic_op) => { // VUID-VkPipelineColorBlendStateCreateInfo-logicOpEnable-00607 logic_op.validate_device(device)? } StateMode::Dynamic => { // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04869 if !device.enabled_features().extended_dynamic_state2_logic_op { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`color_blend_state.logic_op` is \ `Some(StateMode::Dynamic)`", requires_one_of: RequiresOneOf { features: &["extended_dynamic_state2_logic_op"], ..Default::default() }, }); } } } } let color_attachment_count = match render_pass { PipelineRenderPassType::BeginRenderPass(subpass) => { subpass.subpass_desc().color_attachments.len() } PipelineRenderPassType::BeginRendering(rendering_info) => { rendering_info.color_attachment_formats.len() } }; // VUID-VkGraphicsPipelineCreateInfo-renderPass-06042 // VUID-VkGraphicsPipelineCreateInfo-renderPass-06055 // VUID-VkGraphicsPipelineCreateInfo-renderPass-06060 if color_attachment_count != attachments.len() { return Err(GraphicsPipelineCreationError::MismatchBlendingAttachmentsCount); } if attachments.len() > 1 && !device.enabled_features().independent_blend { // Ensure that all `blend` and `color_write_mask` are identical. let mut iter = attachments .iter() .map(|state| (&state.blend, &state.color_write_mask)); let first = iter.next().unwrap(); // VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 if !iter.all(|state| state == first) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`color_blend_state.attachments` has elements where \ `blend` and `color_write_mask` do not match the other elements", requires_one_of: RequiresOneOf { features: &["independent_blend"], ..Default::default() }, }); } } for (attachment_index, state) in attachments.iter().enumerate() { let &ColorBlendAttachmentState { blend, color_write_mask: _, color_write_enable, } = state; if let Some(blend) = blend { let AttachmentBlend { color_op, color_source, color_destination, alpha_op, alpha_source, alpha_destination, } = blend; // VUID-VkPipelineColorBlendAttachmentState-colorBlendOp-parameter color_op.validate_device(device)?; // VUID-VkPipelineColorBlendAttachmentState-srcColorBlendFactor-parameter color_source.validate_device(device)?; // VUID-VkPipelineColorBlendAttachmentState-dstColorBlendFactor-parameter color_destination.validate_device(device)?; // VUID-VkPipelineColorBlendAttachmentState-alphaBlendOp-parameter alpha_op.validate_device(device)?; // VUID-VkPipelineColorBlendAttachmentState-srcAlphaBlendFactor-parameter alpha_source.validate_device(device)?; // VUID-VkPipelineColorBlendAttachmentState-dstAlphaBlendFactor-parameter alpha_destination.validate_device(device)?; // VUID? if !device.enabled_features().dual_src_blend && [ color_source, color_destination, alpha_source, alpha_destination, ] .into_iter() .any(|blend_factor| { matches!( blend_factor, BlendFactor::Src1Color | BlendFactor::OneMinusSrc1Color | BlendFactor::Src1Alpha | BlendFactor::OneMinusSrc1Alpha ) }) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`color_blend_state.attachments` has an element where \ `blend` is `Some(blend)`, where `blend.color_source`, \ `blend.color_destination`, `blend.alpha_source` or \ `blend.alpha_destination` is `BlendFactor::Src1*`", requires_one_of: RequiresOneOf { features: &["dual_src_blend"], ..Default::default() }, }); } let attachment_format = match render_pass { PipelineRenderPassType::BeginRenderPass(subpass) => subpass .subpass_desc() .color_attachments[attachment_index] .as_ref() .and_then(|atch_ref| { subpass.render_pass().attachments()[atch_ref.attachment as usize] .format }), PipelineRenderPassType::BeginRendering(rendering_info) => { rendering_info.color_attachment_formats[attachment_index] } }; // VUID-VkGraphicsPipelineCreateInfo-renderPass-06041 // VUID-VkGraphicsPipelineCreateInfo-renderPass-06062 // Use unchecked, because all validation has been done above or by the // render pass creation. if !attachment_format.map_or(false, |format| unsafe { physical_device .format_properties_unchecked(format) .potential_format_features() .intersects(FormatFeatures::COLOR_ATTACHMENT_BLEND) }) { return Err( GraphicsPipelineCreationError::ColorAttachmentFormatBlendNotSupported { attachment_index: attachment_index as u32, }, ); } // VUID-VkPipelineColorBlendAttachmentState-constantAlphaColorBlendFactors-04454 // VUID-VkPipelineColorBlendAttachmentState-constantAlphaColorBlendFactors-04455 if device.enabled_extensions().khr_portability_subset && !device.enabled_features().constant_alpha_color_blend_factors && (matches!( color_source, BlendFactor::ConstantAlpha | BlendFactor::OneMinusConstantAlpha ) || matches!( color_destination, BlendFactor::ConstantAlpha | BlendFactor::OneMinusConstantAlpha )) { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "this device is a portability subset device, and \ `color_blend_state.attachments` has an element where `blend` is \ `Some(blend)`, where \ `blend.color_source` or `blend.color_destination` is \ `BlendFactor::ConstantAlpha` or \ `BlendFactor::OneMinusConstantAlpha`", requires_one_of: RequiresOneOf { features: &["constant_alpha_color_blend_factors"], ..Default::default() }, }); } } match color_write_enable { StateMode::Fixed(enable) => { // VUID-VkPipelineColorWriteCreateInfoEXT-pAttachments-04801 if !enable && !device.enabled_features().color_write_enable { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`color_blend_state.attachments` has an element \ where `color_write_enable` is `StateMode::Fixed(false)`", requires_one_of: RequiresOneOf { features: &["color_write_enable"], ..Default::default() }, }); } } StateMode::Dynamic => { // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04800 if !device.enabled_features().color_write_enable { return Err(GraphicsPipelineCreationError::RequirementNotMet { required_for: "`color_blend_state.attachments` has an element \ where `color_write_enable` is `StateMode::Dynamic`", requires_one_of: RequiresOneOf { features: &["color_write_enable"], ..Default::default() }, }); } } } } } /* Generic shader checks */ for stage_info in &shader_stages { // VUID-VkGraphicsPipelineCreateInfo-layout-00756 pipeline_layout.ensure_compatible_with_shader( stage_info.entry_point.descriptor_binding_requirements(), stage_info.entry_point.push_constant_requirements(), )?; for (constant_id, reqs) in stage_info .entry_point .specialization_constant_requirements() { let map_entry = stage_info .specialization_map_entries .iter() .find(|desc| desc.constant_id == constant_id) .ok_or(GraphicsPipelineCreationError::IncompatibleSpecializationConstants)?; if map_entry.size as DeviceSize != reqs.size { return Err(GraphicsPipelineCreationError::IncompatibleSpecializationConstants); } } } // VUID-VkGraphicsPipelineCreateInfo-pStages-00742 // VUID-VkGraphicsPipelineCreateInfo-None-04889 // TODO: this check is too strict; the output only has to be a superset, any variables // not used in the input of the next shader are just ignored. for (output, input) in shader_stages.iter().zip(shader_stages.iter().skip(1)) { if let Err(err) = input .entry_point .input_interface() .matches(output.entry_point.output_interface()) { return Err(GraphicsPipelineCreationError::ShaderStagesMismatch(err)); } } // TODO: // VUID-VkPipelineShaderStageCreateInfo-maxClipDistances-00708 // VUID-VkPipelineShaderStageCreateInfo-maxCullDistances-00709 // VUID-VkPipelineShaderStageCreateInfo-maxCombinedClipAndCullDistances-00710 // VUID-VkPipelineShaderStageCreateInfo-maxSampleMaskWords-00711 // Dynamic states not handled yet: // - ViewportWScaling (VkPipelineViewportWScalingStateCreateInfoNV) // - SampleLocations (VkPipelineSampleLocationsStateCreateInfoEXT) // - ViewportShadingRatePalette (VkPipelineViewportShadingRateImageStateCreateInfoNV) // - ViewportCoarseSampleOrder (VkPipelineViewportCoarseSampleOrderStateCreateInfoNV) // - ExclusiveScissor (VkPipelineViewportExclusiveScissorStateCreateInfoNV) // - FragmentShadingRate (VkPipelineFragmentShadingRateStateCreateInfoKHR) Ok(()) } unsafe fn record_create( &self, device: &Device, pipeline_layout: &PipelineLayout, vertex_input_state: &VertexInputState, has: Has, ) -> Result< ( ash::vk::Pipeline, HashMap<(u32, u32), DescriptorBindingRequirements>, HashMap, HashMap, Option, ), GraphicsPipelineCreationError, > { let Self { render_pass, cache, vertex_shader, tessellation_shaders, geometry_shader, fragment_shader, vertex_input_state: _, input_assembly_state, tessellation_state, viewport_state, discard_rectangle_state, rasterization_state, multisample_state, depth_stencil_state, color_blend_state, } = self; let render_pass = render_pass.as_ref().unwrap(); let mut descriptor_binding_requirements: HashMap< (u32, u32), DescriptorBindingRequirements, > = HashMap::default(); let mut dynamic_state: HashMap = HashMap::default(); let mut stages = HashMap::default(); let mut stages_vk: SmallVec<[_; 5]> = SmallVec::new(); let mut fragment_tests_stages = None; /* Render pass */ let mut render_pass_vk = ash::vk::RenderPass::null(); let mut subpass_vk = 0; let mut color_attachment_formats_vk: SmallVec<[_; 4]> = SmallVec::new(); let mut rendering_create_info_vk = None; match render_pass { PipelineRenderPassType::BeginRenderPass(subpass) => { render_pass_vk = subpass.render_pass().handle(); subpass_vk = subpass.index(); } PipelineRenderPassType::BeginRendering(rendering_info) => { let &PipelineRenderingCreateInfo { view_mask, ref color_attachment_formats, depth_attachment_format, stencil_attachment_format, _ne: _, } = rendering_info; color_attachment_formats_vk.extend( color_attachment_formats .iter() .map(|format| format.map_or(ash::vk::Format::UNDEFINED, Into::into)), ); let _ = rendering_create_info_vk.insert(ash::vk::PipelineRenderingCreateInfo { view_mask, color_attachment_count: color_attachment_formats_vk.len() as u32, p_color_attachment_formats: color_attachment_formats_vk.as_ptr(), depth_attachment_format: depth_attachment_format .map_or(ash::vk::Format::UNDEFINED, Into::into), stencil_attachment_format: stencil_attachment_format .map_or(ash::vk::Format::UNDEFINED, Into::into), ..Default::default() }); } } /* Vertex input state */ let mut vertex_binding_descriptions_vk: SmallVec<[_; 8]> = SmallVec::new(); let mut vertex_attribute_descriptions_vk: SmallVec<[_; 8]> = SmallVec::new(); let mut vertex_binding_divisor_descriptions_vk: SmallVec<[_; 8]> = SmallVec::new(); let mut vertex_binding_divisor_state_vk = None; let mut vertex_input_state_vk = None; let mut input_assembly_state_vk = None; if has.vertex_input_state { // Vertex input state { dynamic_state.insert(DynamicState::VertexInput, false); let VertexInputState { bindings, attributes, } = vertex_input_state; vertex_binding_descriptions_vk.extend(bindings.iter().map( |(&binding, binding_desc)| ash::vk::VertexInputBindingDescription { binding, stride: binding_desc.stride, input_rate: binding_desc.input_rate.into(), }, )); vertex_attribute_descriptions_vk.extend(attributes.iter().map( |(&location, attribute_desc)| ash::vk::VertexInputAttributeDescription { location, binding: attribute_desc.binding, format: attribute_desc.format.into(), offset: attribute_desc.offset, }, )); let vertex_input_state = vertex_input_state_vk.insert(ash::vk::PipelineVertexInputStateCreateInfo { flags: ash::vk::PipelineVertexInputStateCreateFlags::empty(), vertex_binding_description_count: vertex_binding_descriptions_vk.len() as u32, p_vertex_binding_descriptions: vertex_binding_descriptions_vk.as_ptr(), vertex_attribute_description_count: vertex_attribute_descriptions_vk.len() as u32, p_vertex_attribute_descriptions: vertex_attribute_descriptions_vk.as_ptr(), ..Default::default() }); { vertex_binding_divisor_descriptions_vk.extend( bindings .iter() .filter_map(|(&binding, binding_desc)| match binding_desc.input_rate { VertexInputRate::Instance { divisor } if divisor != 1 => { Some((binding, divisor)) } _ => None, }) .map(|(binding, divisor)| { ash::vk::VertexInputBindingDivisorDescriptionEXT { binding, divisor, } }), ); // VUID-VkPipelineVertexInputDivisorStateCreateInfoEXT-vertexBindingDivisorCount-arraylength if !vertex_binding_divisor_descriptions_vk.is_empty() { vertex_input_state.p_next = vertex_binding_divisor_state_vk.insert( ash::vk::PipelineVertexInputDivisorStateCreateInfoEXT { vertex_binding_divisor_count: vertex_binding_divisor_descriptions_vk.len() as u32, p_vertex_binding_divisors: vertex_binding_divisor_descriptions_vk.as_ptr(), ..Default::default() }, ) as *const _ as *const _; } } } // Input assembly state { let &InputAssemblyState { topology, primitive_restart_enable, } = input_assembly_state; let topology = match topology { PartialStateMode::Fixed(topology) => { dynamic_state.insert(DynamicState::PrimitiveTopology, false); topology.into() } PartialStateMode::Dynamic(topology_class) => { dynamic_state.insert(DynamicState::PrimitiveTopology, true); topology_class.example().into() } }; let primitive_restart_enable = match primitive_restart_enable { StateMode::Fixed(primitive_restart_enable) => { dynamic_state.insert(DynamicState::PrimitiveRestartEnable, false); primitive_restart_enable as ash::vk::Bool32 } StateMode::Dynamic => { dynamic_state.insert(DynamicState::PrimitiveRestartEnable, true); Default::default() } }; let _ = input_assembly_state_vk.insert(ash::vk::PipelineInputAssemblyStateCreateInfo { flags: ash::vk::PipelineInputAssemblyStateCreateFlags::empty(), topology, primitive_restart_enable, ..Default::default() }); } } /* Pre-rasterization shader state */ let mut vertex_shader_specialization_vk = None; let mut tessellation_control_shader_specialization_vk = None; let mut tessellation_evaluation_shader_specialization_vk = None; let mut tessellation_state_vk = None; let mut geometry_shader_specialization_vk = None; let mut viewports_vk: SmallVec<[_; 2]> = SmallVec::new(); let mut scissors_vk: SmallVec<[_; 2]> = SmallVec::new(); let mut viewport_state_vk = None; let mut rasterization_line_state_vk = None; let mut rasterization_state_vk = None; let mut discard_rectangles: SmallVec<[_; 2]> = SmallVec::new(); let mut discard_rectangle_state_vk = None; if has.pre_rasterization_shader_state { // Vertex shader if let Some((entry_point, specialization_data)) = vertex_shader { let specialization_map_entries = Vss::descriptors(); let specialization_data = slice::from_raw_parts( specialization_data as *const _ as *const u8, size_of_val(specialization_data), ); let specialization_info_vk = vertex_shader_specialization_vk.insert(ash::vk::SpecializationInfo { map_entry_count: specialization_map_entries.len() as u32, p_map_entries: specialization_map_entries.as_ptr() as *const _, data_size: specialization_data.len(), p_data: specialization_data.as_ptr() as *const _, }); for (loc, reqs) in entry_point.descriptor_binding_requirements() { match descriptor_binding_requirements.entry(loc) { Entry::Occupied(entry) => { entry.into_mut().merge(reqs).expect("Could not produce an intersection of the shader descriptor requirements"); } Entry::Vacant(entry) => { entry.insert(reqs.clone()); } } } stages.insert(ShaderStage::Vertex, ()); stages_vk.push(ash::vk::PipelineShaderStageCreateInfo { flags: ash::vk::PipelineShaderStageCreateFlags::empty(), stage: ash::vk::ShaderStageFlags::VERTEX, module: entry_point.module().handle(), p_name: entry_point.name().as_ptr(), p_specialization_info: specialization_info_vk as *const _, ..Default::default() }); } // Tessellation shaders if let Some(tessellation_shaders) = tessellation_shaders { { let (entry_point, specialization_data) = &tessellation_shaders.control; let specialization_map_entries = Tcss::descriptors(); let specialization_data = slice::from_raw_parts( specialization_data as *const _ as *const u8, size_of_val(specialization_data), ); let specialization_info_vk = tessellation_control_shader_specialization_vk .insert(ash::vk::SpecializationInfo { map_entry_count: specialization_map_entries.len() as u32, p_map_entries: specialization_map_entries.as_ptr() as *const _, data_size: specialization_data.len(), p_data: specialization_data.as_ptr() as *const _, }); for (loc, reqs) in entry_point.descriptor_binding_requirements() { match descriptor_binding_requirements.entry(loc) { Entry::Occupied(entry) => { entry.into_mut().merge(reqs).expect("Could not produce an intersection of the shader descriptor requirements"); } Entry::Vacant(entry) => { entry.insert(reqs.clone()); } } } stages.insert(ShaderStage::TessellationControl, ()); stages_vk.push(ash::vk::PipelineShaderStageCreateInfo { flags: ash::vk::PipelineShaderStageCreateFlags::empty(), stage: ash::vk::ShaderStageFlags::TESSELLATION_CONTROL, module: entry_point.module().handle(), p_name: entry_point.name().as_ptr(), p_specialization_info: specialization_info_vk as *const _, ..Default::default() }); } { let (entry_point, specialization_data) = &tessellation_shaders.evaluation; let specialization_map_entries = Tess::descriptors(); let specialization_data = slice::from_raw_parts( specialization_data as *const _ as *const u8, size_of_val(specialization_data), ); let specialization_info_vk = tessellation_evaluation_shader_specialization_vk .insert(ash::vk::SpecializationInfo { map_entry_count: specialization_map_entries.len() as u32, p_map_entries: specialization_map_entries.as_ptr() as *const _, data_size: specialization_data.len(), p_data: specialization_data.as_ptr() as *const _, }); for (loc, reqs) in entry_point.descriptor_binding_requirements() { match descriptor_binding_requirements.entry(loc) { Entry::Occupied(entry) => { entry.into_mut().merge(reqs).expect("Could not produce an intersection of the shader descriptor requirements"); } Entry::Vacant(entry) => { entry.insert(reqs.clone()); } } } stages.insert(ShaderStage::TessellationEvaluation, ()); stages_vk.push(ash::vk::PipelineShaderStageCreateInfo { flags: ash::vk::PipelineShaderStageCreateFlags::empty(), stage: ash::vk::ShaderStageFlags::TESSELLATION_EVALUATION, module: entry_point.module().handle(), p_name: entry_point.name().as_ptr(), p_specialization_info: specialization_info_vk as *const _, ..Default::default() }); } } // Geometry shader if let Some((entry_point, specialization_data)) = geometry_shader { let specialization_map_entries = Gss::descriptors(); let specialization_data = slice::from_raw_parts( specialization_data as *const _ as *const u8, size_of_val(specialization_data), ); let specialization_info_vk = geometry_shader_specialization_vk.insert(ash::vk::SpecializationInfo { map_entry_count: specialization_map_entries.len() as u32, p_map_entries: specialization_map_entries.as_ptr() as *const _, data_size: specialization_data.len(), p_data: specialization_data.as_ptr() as *const _, }); for (loc, reqs) in entry_point.descriptor_binding_requirements() { match descriptor_binding_requirements.entry(loc) { Entry::Occupied(entry) => { entry.into_mut().merge(reqs).expect("Could not produce an intersection of the shader descriptor requirements"); } Entry::Vacant(entry) => { entry.insert(reqs.clone()); } } } stages.insert(ShaderStage::Geometry, ()); stages_vk.push(ash::vk::PipelineShaderStageCreateInfo { flags: ash::vk::PipelineShaderStageCreateFlags::empty(), stage: ash::vk::ShaderStageFlags::GEOMETRY, module: entry_point.module().handle(), p_name: entry_point.name().as_ptr(), p_specialization_info: specialization_info_vk as *const _, ..Default::default() }); } // Rasterization state { let &RasterizationState { depth_clamp_enable, rasterizer_discard_enable, polygon_mode, cull_mode, front_face, depth_bias, line_width, line_rasterization_mode, line_stipple, } = rasterization_state; let rasterizer_discard_enable = match rasterizer_discard_enable { StateMode::Fixed(rasterizer_discard_enable) => { dynamic_state.insert(DynamicState::RasterizerDiscardEnable, false); rasterizer_discard_enable as ash::vk::Bool32 } StateMode::Dynamic => { dynamic_state.insert(DynamicState::RasterizerDiscardEnable, true); ash::vk::FALSE } }; let cull_mode = match cull_mode { StateMode::Fixed(cull_mode) => { dynamic_state.insert(DynamicState::CullMode, false); cull_mode.into() } StateMode::Dynamic => { dynamic_state.insert(DynamicState::CullMode, true); CullMode::default().into() } }; let front_face = match front_face { StateMode::Fixed(front_face) => { dynamic_state.insert(DynamicState::FrontFace, false); front_face.into() } StateMode::Dynamic => { dynamic_state.insert(DynamicState::FrontFace, true); FrontFace::default().into() } }; let ( depth_bias_enable, depth_bias_constant_factor, depth_bias_clamp, depth_bias_slope_factor, ) = if let Some(depth_bias_state) = depth_bias { if depth_bias_state.enable_dynamic { dynamic_state.insert(DynamicState::DepthBiasEnable, true); } else { dynamic_state.insert(DynamicState::DepthBiasEnable, false); } let (constant_factor, clamp, slope_factor) = match depth_bias_state.bias { StateMode::Fixed(bias) => { dynamic_state.insert(DynamicState::DepthBias, false); (bias.constant_factor, bias.clamp, bias.slope_factor) } StateMode::Dynamic => { dynamic_state.insert(DynamicState::DepthBias, true); (0.0, 0.0, 0.0) } }; (ash::vk::TRUE, constant_factor, clamp, slope_factor) } else { (ash::vk::FALSE, 0.0, 0.0, 0.0) }; let line_width = match line_width { StateMode::Fixed(line_width) => { dynamic_state.insert(DynamicState::LineWidth, false); line_width } StateMode::Dynamic => { dynamic_state.insert(DynamicState::LineWidth, true); 1.0 } }; let rasterization_state = rasterization_state_vk.insert(ash::vk::PipelineRasterizationStateCreateInfo { flags: ash::vk::PipelineRasterizationStateCreateFlags::empty(), depth_clamp_enable: depth_clamp_enable as ash::vk::Bool32, rasterizer_discard_enable, polygon_mode: polygon_mode.into(), cull_mode, front_face, depth_bias_enable, depth_bias_constant_factor, depth_bias_clamp, depth_bias_slope_factor, line_width, ..Default::default() }); if device.enabled_extensions().ext_line_rasterization { let (stippled_line_enable, line_stipple_factor, line_stipple_pattern) = if let Some(line_stipple) = line_stipple { let (factor, pattern) = match line_stipple { StateMode::Fixed(line_stipple) => { dynamic_state.insert(DynamicState::LineStipple, false); (line_stipple.factor, line_stipple.pattern) } StateMode::Dynamic => { dynamic_state.insert(DynamicState::LineStipple, true); (1, 0) } }; (ash::vk::TRUE, factor, pattern) } else { (ash::vk::FALSE, 1, 0) }; rasterization_state.p_next = rasterization_line_state_vk.insert( ash::vk::PipelineRasterizationLineStateCreateInfoEXT { line_rasterization_mode: line_rasterization_mode.into(), stippled_line_enable, line_stipple_factor, line_stipple_pattern, ..Default::default() }, ) as *const _ as *const _; } } // Discard rectangle state if device.enabled_extensions().ext_discard_rectangles { let DiscardRectangleState { mode, rectangles } = discard_rectangle_state; let discard_rectangle_count = match rectangles { PartialStateMode::Fixed(rectangles) => { dynamic_state.insert(DynamicState::DiscardRectangle, false); discard_rectangles.extend(rectangles.iter().map(|&rect| rect.into())); discard_rectangles.len() as u32 } PartialStateMode::Dynamic(count) => { dynamic_state.insert(DynamicState::DiscardRectangle, true); *count } }; let _ = discard_rectangle_state_vk.insert( ash::vk::PipelineDiscardRectangleStateCreateInfoEXT { flags: ash::vk::PipelineDiscardRectangleStateCreateFlagsEXT::empty(), discard_rectangle_mode: (*mode).into(), discard_rectangle_count, p_discard_rectangles: discard_rectangles.as_ptr(), ..Default::default() }, ); } } // Tessellation state if has.tessellation_state { let &TessellationState { patch_control_points, } = tessellation_state; let patch_control_points = match patch_control_points { StateMode::Fixed(patch_control_points) => { dynamic_state.insert(DynamicState::PatchControlPoints, false); patch_control_points } StateMode::Dynamic => { dynamic_state.insert(DynamicState::PatchControlPoints, true); Default::default() } }; let _ = tessellation_state_vk.insert(ash::vk::PipelineTessellationStateCreateInfo { flags: ash::vk::PipelineTessellationStateCreateFlags::empty(), patch_control_points, ..Default::default() }); } // Viewport state if has.viewport_state { let (viewport_count, scissor_count) = match viewport_state { ViewportState::Fixed { data } => { let count = data.len() as u32; viewports_vk.extend(data.iter().map(|e| e.0.clone().into())); dynamic_state.insert(DynamicState::Viewport, false); dynamic_state.insert(DynamicState::ViewportWithCount, false); scissors_vk.extend(data.iter().map(|e| e.1.into())); dynamic_state.insert(DynamicState::Scissor, false); dynamic_state.insert(DynamicState::ScissorWithCount, false); (count, count) } ViewportState::FixedViewport { viewports, scissor_count_dynamic, } => { let viewport_count = viewports.len() as u32; viewports_vk.extend(viewports.iter().map(|e| e.clone().into())); dynamic_state.insert(DynamicState::Viewport, false); dynamic_state.insert(DynamicState::ViewportWithCount, false); let scissor_count = if *scissor_count_dynamic { dynamic_state.insert(DynamicState::Scissor, false); dynamic_state.insert(DynamicState::ScissorWithCount, true); 0 } else { dynamic_state.insert(DynamicState::Scissor, true); dynamic_state.insert(DynamicState::ScissorWithCount, false); viewport_count }; (viewport_count, scissor_count) } ViewportState::FixedScissor { scissors, viewport_count_dynamic, } => { let scissor_count = scissors.len() as u32; scissors_vk.extend(scissors.iter().map(|&e| e.into())); dynamic_state.insert(DynamicState::Scissor, false); dynamic_state.insert(DynamicState::ScissorWithCount, false); let viewport_count = if *viewport_count_dynamic { dynamic_state.insert(DynamicState::Viewport, false); dynamic_state.insert(DynamicState::ViewportWithCount, true); 0 } else { dynamic_state.insert(DynamicState::Viewport, true); dynamic_state.insert(DynamicState::ViewportWithCount, false); scissor_count }; (viewport_count, scissor_count) } &ViewportState::Dynamic { count, viewport_count_dynamic, scissor_count_dynamic, } => { let viewport_count = if viewport_count_dynamic { dynamic_state.insert(DynamicState::Viewport, false); dynamic_state.insert(DynamicState::ViewportWithCount, true); 0 } else { dynamic_state.insert(DynamicState::Viewport, true); dynamic_state.insert(DynamicState::ViewportWithCount, false); count }; let scissor_count = if scissor_count_dynamic { dynamic_state.insert(DynamicState::Scissor, false); dynamic_state.insert(DynamicState::ScissorWithCount, true); 0 } else { dynamic_state.insert(DynamicState::Scissor, true); dynamic_state.insert(DynamicState::ScissorWithCount, false); count }; (viewport_count, scissor_count) } }; let _ = viewport_state_vk.insert(ash::vk::PipelineViewportStateCreateInfo { flags: ash::vk::PipelineViewportStateCreateFlags::empty(), viewport_count, p_viewports: if viewports_vk.is_empty() { ptr::null() } else { viewports_vk.as_ptr() }, // validation layer crashes if you just pass the pointer scissor_count, p_scissors: if scissors_vk.is_empty() { ptr::null() } else { scissors_vk.as_ptr() }, // validation layer crashes if you just pass the pointer ..Default::default() }); } /* Fragment shader state */ let mut fragment_shader_specialization_vk = None; let mut depth_stencil_state_vk = None; if has.fragment_shader_state { // Fragment shader if let Some((entry_point, specialization_data)) = fragment_shader { let specialization_map_entries = Fss::descriptors(); let specialization_data = slice::from_raw_parts( specialization_data as *const _ as *const u8, size_of_val(specialization_data), ); let specialization_info_vk = fragment_shader_specialization_vk.insert(ash::vk::SpecializationInfo { map_entry_count: specialization_map_entries.len() as u32, p_map_entries: specialization_map_entries.as_ptr() as *const _, data_size: specialization_data.len(), p_data: specialization_data.as_ptr() as *const _, }); for (loc, reqs) in entry_point.descriptor_binding_requirements() { match descriptor_binding_requirements.entry(loc) { Entry::Occupied(entry) => { entry.into_mut().merge(reqs).expect("Could not produce an intersection of the shader descriptor requirements"); } Entry::Vacant(entry) => { entry.insert(reqs.clone()); } } } stages.insert(ShaderStage::Fragment, ()); stages_vk.push(ash::vk::PipelineShaderStageCreateInfo { flags: ash::vk::PipelineShaderStageCreateFlags::empty(), stage: ash::vk::ShaderStageFlags::FRAGMENT, module: entry_point.module().handle(), p_name: entry_point.name().as_ptr(), p_specialization_info: specialization_info_vk as *const _, ..Default::default() }); fragment_tests_stages = match entry_point.execution() { ShaderExecution::Fragment(FragmentShaderExecution { fragment_tests_stages, .. }) => Some(*fragment_tests_stages), _ => unreachable!(), }; } } // Depth/stencil state if has.depth_stencil_state { let DepthStencilState { depth, depth_bounds, stencil, } = depth_stencil_state; let (depth_test_enable, depth_write_enable, depth_compare_op) = if let Some(depth_state) = depth { let &DepthState { enable_dynamic, write_enable, compare_op, } = depth_state; if enable_dynamic { dynamic_state.insert(DynamicState::DepthTestEnable, true); } else { dynamic_state.insert(DynamicState::DepthTestEnable, false); } let write_enable = match write_enable { StateMode::Fixed(write_enable) => { dynamic_state.insert(DynamicState::DepthWriteEnable, false); write_enable as ash::vk::Bool32 } StateMode::Dynamic => { dynamic_state.insert(DynamicState::DepthWriteEnable, true); ash::vk::TRUE } }; let compare_op = match compare_op { StateMode::Fixed(compare_op) => { dynamic_state.insert(DynamicState::DepthCompareOp, false); compare_op.into() } StateMode::Dynamic => { dynamic_state.insert(DynamicState::DepthCompareOp, true); ash::vk::CompareOp::ALWAYS } }; (ash::vk::TRUE, write_enable, compare_op) } else { (ash::vk::FALSE, ash::vk::FALSE, ash::vk::CompareOp::ALWAYS) }; let (depth_bounds_test_enable, min_depth_bounds, max_depth_bounds) = if let Some(depth_bounds_state) = depth_bounds { let DepthBoundsState { enable_dynamic, bounds, } = depth_bounds_state; if *enable_dynamic { dynamic_state.insert(DynamicState::DepthBoundsTestEnable, true); } else { dynamic_state.insert(DynamicState::DepthBoundsTestEnable, false); } let (min_bounds, max_bounds) = match bounds.clone() { StateMode::Fixed(bounds) => { dynamic_state.insert(DynamicState::DepthBounds, false); bounds.into_inner() } StateMode::Dynamic => { dynamic_state.insert(DynamicState::DepthBounds, true); (0.0, 1.0) } }; (ash::vk::TRUE, min_bounds, max_bounds) } else { (ash::vk::FALSE, 0.0, 1.0) }; let (stencil_test_enable, front, back) = if let Some(stencil_state) = stencil { let StencilState { enable_dynamic, front, back, } = stencil_state; if *enable_dynamic { dynamic_state.insert(DynamicState::StencilTestEnable, true); } else { dynamic_state.insert(DynamicState::StencilTestEnable, false); } match (front.ops, back.ops) { (StateMode::Fixed(_), StateMode::Fixed(_)) => { dynamic_state.insert(DynamicState::StencilOp, false); } (StateMode::Dynamic, StateMode::Dynamic) => { dynamic_state.insert(DynamicState::StencilOp, true); } _ => unreachable!(), }; match (front.compare_mask, back.compare_mask) { (StateMode::Fixed(_), StateMode::Fixed(_)) => { dynamic_state.insert(DynamicState::StencilCompareMask, false); } (StateMode::Dynamic, StateMode::Dynamic) => { dynamic_state.insert(DynamicState::StencilCompareMask, true); } _ => unreachable!(), }; match (front.write_mask, back.write_mask) { (StateMode::Fixed(_), StateMode::Fixed(_)) => { dynamic_state.insert(DynamicState::StencilWriteMask, false); } (StateMode::Dynamic, StateMode::Dynamic) => { dynamic_state.insert(DynamicState::StencilWriteMask, true); } _ => unreachable!(), }; match (front.reference, back.reference) { (StateMode::Fixed(_), StateMode::Fixed(_)) => { dynamic_state.insert(DynamicState::StencilReference, false); } (StateMode::Dynamic, StateMode::Dynamic) => { dynamic_state.insert(DynamicState::StencilReference, true); } _ => unreachable!(), }; let [front, back] = [front, back].map(|stencil_op_state| { let &StencilOpState { ops, compare_mask, write_mask, reference, } = stencil_op_state; let ops = match ops { StateMode::Fixed(x) => x, StateMode::Dynamic => Default::default(), }; let compare_mask = match compare_mask { StateMode::Fixed(x) => x, StateMode::Dynamic => Default::default(), }; let write_mask = match write_mask { StateMode::Fixed(x) => x, StateMode::Dynamic => Default::default(), }; let reference = match reference { StateMode::Fixed(x) => x, StateMode::Dynamic => Default::default(), }; ash::vk::StencilOpState { fail_op: ops.fail_op.into(), pass_op: ops.pass_op.into(), depth_fail_op: ops.depth_fail_op.into(), compare_op: ops.compare_op.into(), compare_mask, write_mask, reference, } }); (ash::vk::TRUE, front, back) } else { (ash::vk::FALSE, Default::default(), Default::default()) }; let _ = depth_stencil_state_vk.insert(ash::vk::PipelineDepthStencilStateCreateInfo { flags: ash::vk::PipelineDepthStencilStateCreateFlags::empty(), depth_test_enable, depth_write_enable, depth_compare_op, depth_bounds_test_enable, stencil_test_enable, front, back, min_depth_bounds, max_depth_bounds, ..Default::default() }); } /* Fragment output state */ let mut multisample_state_vk = None; let mut color_blend_attachments_vk: SmallVec<[_; 4]> = SmallVec::new(); let mut color_write_enables_vk: SmallVec<[_; 4]> = SmallVec::new(); let mut color_write_vk = None; let mut color_blend_state_vk = None; if has.fragment_output_state { // Multisample state { let &MultisampleState { rasterization_samples, sample_shading, ref sample_mask, alpha_to_coverage_enable, alpha_to_one_enable, } = multisample_state; let (sample_shading_enable, min_sample_shading) = if let Some(min_sample_shading) = sample_shading { (ash::vk::TRUE, min_sample_shading) } else { (ash::vk::FALSE, 0.0) }; let _ = multisample_state_vk.insert(ash::vk::PipelineMultisampleStateCreateInfo { flags: ash::vk::PipelineMultisampleStateCreateFlags::empty(), rasterization_samples: rasterization_samples.into(), sample_shading_enable, min_sample_shading, p_sample_mask: sample_mask as _, alpha_to_coverage_enable: alpha_to_coverage_enable as ash::vk::Bool32, alpha_to_one_enable: alpha_to_one_enable as ash::vk::Bool32, ..Default::default() }); } } // Color blend state if has.color_blend_state { let &ColorBlendState { logic_op, ref attachments, blend_constants, } = color_blend_state; color_blend_attachments_vk.extend(attachments.iter().map( |color_blend_attachment_state| { let &ColorBlendAttachmentState { blend, color_write_mask, color_write_enable: _, } = color_blend_attachment_state; let blend = if let Some(blend) = blend { blend.into() } else { Default::default() }; ash::vk::PipelineColorBlendAttachmentState { color_write_mask: color_write_mask.into(), ..blend } }, )); let (logic_op_enable, logic_op) = if let Some(logic_op) = logic_op { let logic_op = match logic_op { StateMode::Fixed(logic_op) => { dynamic_state.insert(DynamicState::LogicOp, false); logic_op.into() } StateMode::Dynamic => { dynamic_state.insert(DynamicState::LogicOp, true); Default::default() } }; (ash::vk::TRUE, logic_op) } else { (ash::vk::FALSE, Default::default()) }; let blend_constants = match blend_constants { StateMode::Fixed(blend_constants) => { dynamic_state.insert(DynamicState::BlendConstants, false); blend_constants } StateMode::Dynamic => { dynamic_state.insert(DynamicState::BlendConstants, true); Default::default() } }; let mut color_blend_state_vk = color_blend_state_vk.insert(ash::vk::PipelineColorBlendStateCreateInfo { flags: ash::vk::PipelineColorBlendStateCreateFlags::empty(), logic_op_enable, logic_op, attachment_count: color_blend_attachments_vk.len() as u32, p_attachments: color_blend_attachments_vk.as_ptr(), blend_constants, ..Default::default() }); if device.enabled_extensions().ext_color_write_enable { color_write_enables_vk.extend(attachments.iter().map( |color_blend_attachment_state| { let &ColorBlendAttachmentState { blend: _, color_write_mask: _, color_write_enable, } = color_blend_attachment_state; match color_write_enable { StateMode::Fixed(enable) => { dynamic_state.insert(DynamicState::ColorWriteEnable, false); enable as ash::vk::Bool32 } StateMode::Dynamic => { dynamic_state.insert(DynamicState::ColorWriteEnable, true); ash::vk::TRUE } } }, )); color_blend_state_vk.p_next = color_write_vk.insert(ash::vk::PipelineColorWriteCreateInfoEXT { attachment_count: color_write_enables_vk.len() as u32, p_color_write_enables: color_write_enables_vk.as_ptr(), ..Default::default() }) as *const _ as *const _; } } /* Dynamic state */ let mut dynamic_state_list: SmallVec<[_; 4]> = SmallVec::new(); let mut dynamic_state_vk = None; { dynamic_state_list.extend( dynamic_state .iter() .filter(|(_, d)| **d) .map(|(&state, _)| state.into()), ); if !dynamic_state_list.is_empty() { let _ = dynamic_state_vk.insert(ash::vk::PipelineDynamicStateCreateInfo { flags: ash::vk::PipelineDynamicStateCreateFlags::empty(), dynamic_state_count: dynamic_state_list.len() as u32, p_dynamic_states: dynamic_state_list.as_ptr(), ..Default::default() }); } } /* Create */ let mut create_info = ash::vk::GraphicsPipelineCreateInfo { flags: ash::vk::PipelineCreateFlags::empty(), // TODO: some flags are available but none are critical stage_count: stages_vk.len() as u32, p_stages: stages_vk.as_ptr(), p_vertex_input_state: vertex_input_state_vk .as_ref() .map(|p| p as *const _) .unwrap_or(ptr::null()), p_input_assembly_state: input_assembly_state_vk .as_ref() .map(|p| p as *const _) .unwrap_or(ptr::null()), p_tessellation_state: tessellation_state_vk .as_ref() .map(|p| p as *const _) .unwrap_or(ptr::null()), p_viewport_state: viewport_state_vk .as_ref() .map(|p| p as *const _) .unwrap_or(ptr::null()), p_rasterization_state: rasterization_state_vk .as_ref() .map(|p| p as *const _) .unwrap_or(ptr::null()), p_multisample_state: multisample_state_vk .as_ref() .map(|p| p as *const _) .unwrap_or(ptr::null()), p_depth_stencil_state: depth_stencil_state_vk .as_ref() .map(|p| p as *const _) .unwrap_or(ptr::null()), p_color_blend_state: color_blend_state_vk .as_ref() .map(|p| p as *const _) .unwrap_or(ptr::null()), p_dynamic_state: dynamic_state_vk .as_ref() .map(|s| s as *const _) .unwrap_or(ptr::null()), layout: pipeline_layout.handle(), render_pass: render_pass_vk, subpass: subpass_vk, base_pipeline_handle: ash::vk::Pipeline::null(), // TODO: base_pipeline_index: -1, // TODO: ..Default::default() }; if let Some(info) = discard_rectangle_state_vk.as_mut() { info.p_next = create_info.p_next; create_info.p_next = info as *const _ as *const _; } if let Some(info) = rendering_create_info_vk.as_mut() { info.p_next = create_info.p_next; create_info.p_next = info as *const _ as *const _; } let cache_handle = match cache.as_ref() { Some(cache) => cache.handle(), None => ash::vk::PipelineCache::null(), }; let handle = { let fns = device.fns(); let mut output = MaybeUninit::uninit(); (fns.v1_0.create_graphics_pipelines)( device.handle(), cache_handle, 1, &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; // Some drivers return `VK_SUCCESS` but provide a null handle if they // fail to create the pipeline (due to invalid shaders, etc) // This check ensures that we don't create an invalid `GraphicsPipeline` instance if handle == ash::vk::Pipeline::null() { panic!("vkCreateGraphicsPipelines provided a NULL handle"); } Ok(( handle, descriptor_binding_requirements, dynamic_state, stages, fragment_tests_stages, )) } // TODO: add build_with_cache method } struct ShaderStageInfo<'a> { entry_point: &'a EntryPoint<'a>, specialization_map_entries: &'a [SpecializationMapEntry], _specialization_data: &'a [u8], } impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> { // TODO: add pipeline derivate system /// Sets the vertex shader to use. // TODO: correct specialization constants pub fn vertex_shader<'vs2, Vss2>( self, shader: EntryPoint<'vs2>, specialization_constants: Vss2, ) -> GraphicsPipelineBuilder<'vs2, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss2, Tcss, Tess, Gss, Fss> where Vss2: SpecializationConstants, { GraphicsPipelineBuilder { render_pass: self.render_pass, cache: self.cache, vertex_shader: Some((shader, specialization_constants)), tessellation_shaders: self.tessellation_shaders, geometry_shader: self.geometry_shader, fragment_shader: self.fragment_shader, vertex_input_state: self.vertex_input_state, input_assembly_state: self.input_assembly_state, tessellation_state: self.tessellation_state, viewport_state: self.viewport_state, discard_rectangle_state: self.discard_rectangle_state, rasterization_state: self.rasterization_state, multisample_state: self.multisample_state, depth_stencil_state: self.depth_stencil_state, color_blend_state: self.color_blend_state, } } /// Sets the tessellation shaders to use. // TODO: correct specialization constants pub fn tessellation_shaders<'tcs2, 'tes2, Tcss2, Tess2>( self, control_shader: EntryPoint<'tcs2>, control_specialization_constants: Tcss2, evaluation_shader: EntryPoint<'tes2>, evaluation_specialization_constants: Tess2, ) -> GraphicsPipelineBuilder<'vs, 'tcs2, 'tes2, 'gs, 'fs, Vdef, Vss, Tcss2, Tess2, Gss, Fss> where Tcss2: SpecializationConstants, Tess2: SpecializationConstants, { GraphicsPipelineBuilder { render_pass: self.render_pass, cache: self.cache, vertex_shader: self.vertex_shader, tessellation_shaders: Some(TessellationShaders { control: (control_shader, control_specialization_constants), evaluation: (evaluation_shader, evaluation_specialization_constants), }), geometry_shader: self.geometry_shader, fragment_shader: self.fragment_shader, vertex_input_state: self.vertex_input_state, input_assembly_state: self.input_assembly_state, tessellation_state: self.tessellation_state, viewport_state: self.viewport_state, discard_rectangle_state: self.discard_rectangle_state, rasterization_state: self.rasterization_state, multisample_state: self.multisample_state, depth_stencil_state: self.depth_stencil_state, color_blend_state: self.color_blend_state, } } /// Sets the geometry shader to use. // TODO: correct specialization constants pub fn geometry_shader<'gs2, Gss2>( self, shader: EntryPoint<'gs2>, specialization_constants: Gss2, ) -> GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs2, 'fs, Vdef, Vss, Tcss, Tess, Gss2, Fss> where Gss2: SpecializationConstants, { GraphicsPipelineBuilder { render_pass: self.render_pass, cache: self.cache, vertex_shader: self.vertex_shader, tessellation_shaders: self.tessellation_shaders, geometry_shader: Some((shader, specialization_constants)), fragment_shader: self.fragment_shader, vertex_input_state: self.vertex_input_state, input_assembly_state: self.input_assembly_state, tessellation_state: self.tessellation_state, viewport_state: self.viewport_state, discard_rectangle_state: self.discard_rectangle_state, rasterization_state: self.rasterization_state, multisample_state: self.multisample_state, depth_stencil_state: self.depth_stencil_state, color_blend_state: self.color_blend_state, } } /// Sets the fragment shader to use. /// /// The fragment shader is run once for each pixel that is covered by each primitive. // TODO: correct specialization constants pub fn fragment_shader<'fs2, Fss2>( self, shader: EntryPoint<'fs2>, specialization_constants: Fss2, ) -> GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs, 'fs2, Vdef, Vss, Tcss, Tess, Gss, Fss2> where Fss2: SpecializationConstants, { GraphicsPipelineBuilder { render_pass: self.render_pass, cache: self.cache, vertex_shader: self.vertex_shader, tessellation_shaders: self.tessellation_shaders, geometry_shader: self.geometry_shader, fragment_shader: Some((shader, specialization_constants)), vertex_input_state: self.vertex_input_state, input_assembly_state: self.input_assembly_state, tessellation_state: self.tessellation_state, viewport_state: self.viewport_state, discard_rectangle_state: self.discard_rectangle_state, rasterization_state: self.rasterization_state, multisample_state: self.multisample_state, depth_stencil_state: self.depth_stencil_state, color_blend_state: self.color_blend_state, } } /// Sets the vertex input state. /// /// The default value is [`VertexInputState::default()`]. pub fn vertex_input_state( self, vertex_input_state: T, ) -> GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs, 'fs, T, Vss, Tcss, Tess, Gss, Fss> where T: VertexDefinition, { GraphicsPipelineBuilder { render_pass: self.render_pass, cache: self.cache, vertex_shader: self.vertex_shader, tessellation_shaders: self.tessellation_shaders, geometry_shader: self.geometry_shader, fragment_shader: self.fragment_shader, vertex_input_state, input_assembly_state: self.input_assembly_state, tessellation_state: self.tessellation_state, viewport_state: self.viewport_state, discard_rectangle_state: self.discard_rectangle_state, rasterization_state: self.rasterization_state, multisample_state: self.multisample_state, depth_stencil_state: self.depth_stencil_state, color_blend_state: self.color_blend_state, } } /// Sets the input assembly state. /// /// The default value is [`InputAssemblyState::default()`]. #[inline] pub fn input_assembly_state(mut self, input_assembly_state: InputAssemblyState) -> Self { self.input_assembly_state = input_assembly_state; self } /// Sets the tessellation state. This is required if the pipeline contains tessellation shaders, /// and ignored otherwise. /// /// The default value is [`TessellationState::default()`]. #[inline] pub fn tessellation_state(mut self, tessellation_state: TessellationState) -> Self { self.tessellation_state = tessellation_state; self } /// Sets the viewport state. /// /// The default value is [`ViewportState::default()`]. #[inline] pub fn viewport_state(mut self, viewport_state: ViewportState) -> Self { self.viewport_state = viewport_state; self } /// Sets the discard rectangle state. /// /// The default value is [`DiscardRectangleState::default()`]. #[inline] pub fn discard_rectangle_state( mut self, discard_rectangle_state: DiscardRectangleState, ) -> Self { self.discard_rectangle_state = discard_rectangle_state; self } /// Sets the rasterization state. /// /// The default value is [`RasterizationState::default()`]. #[inline] pub fn rasterization_state(mut self, rasterization_state: RasterizationState) -> Self { self.rasterization_state = rasterization_state; self } /// Sets the multisample state. /// /// The default value is [`MultisampleState::default()`]. #[inline] pub fn multisample_state(mut self, multisample_state: MultisampleState) -> Self { self.multisample_state = multisample_state; self } /// Sets the depth/stencil state. /// /// The default value is [`DepthStencilState::default()`]. #[inline] pub fn depth_stencil_state(mut self, depth_stencil_state: DepthStencilState) -> Self { self.depth_stencil_state = depth_stencil_state; self } /// Sets the color blend state. /// /// The default value is [`ColorBlendState::default()`]. #[inline] pub fn color_blend_state(mut self, color_blend_state: ColorBlendState) -> Self { self.color_blend_state = color_blend_state; self } /// Sets the tessellation shaders stage as disabled. This is the default. #[deprecated(since = "0.27.0")] #[inline] pub fn tessellation_shaders_disabled(mut self) -> Self { self.tessellation_shaders = None; self } /// Sets the geometry shader stage as disabled. This is the default. #[deprecated(since = "0.27.0")] #[inline] pub fn geometry_shader_disabled(mut self) -> Self { self.geometry_shader = None; self } /// Sets whether primitive restart is enabled. #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] #[inline] pub fn primitive_restart(mut self, enabled: bool) -> Self { self.input_assembly_state.primitive_restart_enable = StateMode::Fixed(enabled); self } /// Sets the topology of the primitives that are expected by the pipeline. #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] #[inline] pub fn primitive_topology(mut self, topology: PrimitiveTopology) -> Self { self.input_assembly_state.topology = PartialStateMode::Fixed(topology); self } /// Sets the topology of the primitives to a list of points. /// /// > **Note**: This is equivalent to /// > `self.primitive_topology(PrimitiveTopology::PointList)`. #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] #[inline] pub fn point_list(self) -> Self { self.primitive_topology(PrimitiveTopology::PointList) } /// Sets the topology of the primitives to a list of lines. /// /// > **Note**: This is equivalent to /// > `self.primitive_topology(PrimitiveTopology::LineList)`. #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] #[inline] pub fn line_list(self) -> Self { self.primitive_topology(PrimitiveTopology::LineList) } /// Sets the topology of the primitives to a line strip. /// /// > **Note**: This is equivalent to /// > `self.primitive_topology(PrimitiveTopology::LineStrip)`. #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] #[inline] pub fn line_strip(self) -> Self { self.primitive_topology(PrimitiveTopology::LineStrip) } /// Sets the topology of the primitives to a list of triangles. Note that this is the default. /// /// > **Note**: This is equivalent to /// > `self.primitive_topology(PrimitiveTopology::TriangleList)`. #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] #[inline] pub fn triangle_list(self) -> Self { self.primitive_topology(PrimitiveTopology::TriangleList) } /// Sets the topology of the primitives to a triangle strip. /// /// > **Note**: This is equivalent to /// > `self.primitive_topology(PrimitiveTopology::TriangleStrip)`. #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] #[inline] pub fn triangle_strip(self) -> Self { self.primitive_topology(PrimitiveTopology::TriangleStrip) } /// Sets the topology of the primitives to a fan of triangles. /// /// > **Note**: This is equivalent to /// > `self.primitive_topology(PrimitiveTopology::TriangleFan)`. #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] #[inline] pub fn triangle_fan(self) -> Self { self.primitive_topology(PrimitiveTopology::TriangleFan) } /// Sets the topology of the primitives to a list of lines with adjacency information. /// /// > **Note**: This is equivalent to /// > `self.primitive_topology(PrimitiveTopology::LineListWithAdjacency)`. #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] #[inline] pub fn line_list_with_adjacency(self) -> Self { self.primitive_topology(PrimitiveTopology::LineListWithAdjacency) } /// Sets the topology of the primitives to a line strip with adjacency information. /// /// > **Note**: This is equivalent to /// > `self.primitive_topology(PrimitiveTopology::LineStripWithAdjacency)`. #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] #[inline] pub fn line_strip_with_adjacency(self) -> Self { self.primitive_topology(PrimitiveTopology::LineStripWithAdjacency) } /// Sets the topology of the primitives to a list of triangles with adjacency information. /// /// > **Note**: This is equivalent to /// > `self.primitive_topology(PrimitiveTopology::TriangleListWithAdjacency)`. #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] #[inline] pub fn triangle_list_with_adjacency(self) -> Self { self.primitive_topology(PrimitiveTopology::TriangleListWithAdjacency) } /// Sets the topology of the primitives to a triangle strip with adjacency information` /// /// > **Note**: This is equivalent to /// > `self.primitive_topology(PrimitiveTopology::TriangleStripWithAdjacency)`. #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] #[inline] pub fn triangle_strip_with_adjacency(self) -> Self { self.primitive_topology(PrimitiveTopology::TriangleStripWithAdjacency) } /// Sets the topology of the primitives to a list of patches. Can only be used and must be used /// with a tessellation shader. /// /// > **Note**: This is equivalent to /// > `self.primitive_topology(PrimitiveTopology::PatchList)`. #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] #[inline] pub fn patch_list(self) -> Self { self.primitive_topology(PrimitiveTopology::PatchList) } /// Sets the viewports to some value, and the scissor boxes to boxes that always cover the /// whole viewport. #[deprecated(since = "0.27.0", note = "Use `viewport_state` instead")] pub fn viewports(self, viewports: I) -> Self where I: IntoIterator, { self.viewports_scissors(viewports.into_iter().map(|v| (v, Scissor::irrelevant()))) } /// Sets the characteristics of viewports and scissor boxes in advance. #[deprecated(since = "0.27.0", note = "Use `viewport_state` instead")] pub fn viewports_scissors(mut self, viewports: I) -> Self where I: IntoIterator, { self.viewport_state = ViewportState::Fixed { data: viewports.into_iter().collect(), }; self } /// Sets the scissor boxes to some values, and viewports to dynamic. The viewports will /// need to be set before drawing. #[deprecated(since = "0.27.0", note = "Use `viewport_state` instead")] pub fn viewports_dynamic_scissors_fixed(mut self, scissors: I) -> Self where I: IntoIterator, { self.viewport_state = ViewportState::FixedScissor { scissors: scissors.into_iter().collect(), viewport_count_dynamic: false, }; self } /// Sets the viewports to dynamic, and the scissor boxes to boxes that always cover the whole /// viewport. The viewports will need to be set before drawing. #[deprecated(since = "0.27.0", note = "Use `viewport_state` instead")] #[inline] pub fn viewports_dynamic_scissors_irrelevant(mut self, num: u32) -> Self { self.viewport_state = ViewportState::FixedScissor { scissors: (0..num).map(|_| Scissor::irrelevant()).collect(), viewport_count_dynamic: false, }; self } /// Sets the viewports to some values, and scissor boxes to dynamic. The scissor boxes will /// need to be set before drawing. #[deprecated(since = "0.27.0", note = "Use `viewport_state` instead")] pub fn viewports_fixed_scissors_dynamic(mut self, viewports: I) -> Self where I: IntoIterator, { self.viewport_state = ViewportState::FixedViewport { viewports: viewports.into_iter().collect(), scissor_count_dynamic: false, }; self } /// Sets the viewports and scissor boxes to dynamic. They will both need to be set before /// drawing. #[deprecated(since = "0.27.0", note = "Use `viewport_state` instead")] #[inline] pub fn viewports_scissors_dynamic(mut self, count: u32) -> Self { self.viewport_state = ViewportState::Dynamic { count, viewport_count_dynamic: false, scissor_count_dynamic: false, }; self } /// If true, then the depth value of the vertices will be clamped to the range `[0.0 ; 1.0]`. /// If false, fragments whose depth is outside of this range will be discarded before the /// fragment shader even runs. #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] #[inline] pub fn depth_clamp(mut self, clamp: bool) -> Self { self.rasterization_state.depth_clamp_enable = clamp; self } /// Sets the front-facing faces to counter-clockwise faces. This is the default. /// /// Triangles whose vertices are oriented counter-clockwise on the screen will be considered /// as facing their front. Otherwise they will be considered as facing their back. #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] #[inline] pub fn front_face_counter_clockwise(mut self) -> Self { self.rasterization_state.front_face = StateMode::Fixed(FrontFace::CounterClockwise); self } /// Sets the front-facing faces to clockwise faces. /// /// Triangles whose vertices are oriented clockwise on the screen will be considered /// as facing their front. Otherwise they will be considered as facing their back. #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] #[inline] pub fn front_face_clockwise(mut self) -> Self { self.rasterization_state.front_face = StateMode::Fixed(FrontFace::Clockwise); self } /// Sets backface culling as disabled. This is the default. #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] #[inline] pub fn cull_mode_disabled(mut self) -> Self { self.rasterization_state.cull_mode = StateMode::Fixed(CullMode::None); self } /// Sets backface culling to front faces. The front faces (as chosen with the `front_face_*` /// methods) will be discarded by the GPU when drawing. #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] #[inline] pub fn cull_mode_front(mut self) -> Self { self.rasterization_state.cull_mode = StateMode::Fixed(CullMode::Front); self } /// Sets backface culling to back faces. Faces that are not facing the front (as chosen with /// the `front_face_*` methods) will be discarded by the GPU when drawing. #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] #[inline] pub fn cull_mode_back(mut self) -> Self { self.rasterization_state.cull_mode = StateMode::Fixed(CullMode::Back); self } /// Sets backface culling to both front and back faces. All the faces will be discarded. /// /// > **Note**: This option exists for the sake of completeness. It has no known practical /// > usage. #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] #[inline] pub fn cull_mode_front_and_back(mut self) -> Self { self.rasterization_state.cull_mode = StateMode::Fixed(CullMode::FrontAndBack); self } /// Sets the polygon mode to "fill". This is the default. #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] #[inline] pub fn polygon_mode_fill(mut self) -> Self { self.rasterization_state.polygon_mode = PolygonMode::Fill; self } /// Sets the polygon mode to "line". Triangles will each be turned into three lines. #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] #[inline] pub fn polygon_mode_line(mut self) -> Self { self.rasterization_state.polygon_mode = PolygonMode::Line; self } /// Sets the polygon mode to "point". Triangles and lines will each be turned into three points. #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] #[inline] pub fn polygon_mode_point(mut self) -> Self { self.rasterization_state.polygon_mode = PolygonMode::Point; self } /// Sets the width of the lines, if the GPU needs to draw lines. The default is `1.0`. #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] #[inline] pub fn line_width(mut self, value: f32) -> Self { self.rasterization_state.line_width = StateMode::Fixed(value); self } /// Sets the width of the lines as dynamic, which means that you will need to set this value /// when drawing. #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] #[inline] pub fn line_width_dynamic(mut self) -> Self { self.rasterization_state.line_width = StateMode::Dynamic; self } /// Disables sample shading. The fragment shader will only be run once per fragment (ie. per /// pixel) and not once by sample. The output will then be copied in all of the covered /// samples. /// /// Sample shading is disabled by default. #[deprecated(since = "0.27.0", note = "Use `multisample_state` instead")] #[inline] pub fn sample_shading_disabled(mut self) -> Self { self.multisample_state.sample_shading = None; self } /// Enables sample shading. The fragment shader will be run once per sample at the borders of /// the object you're drawing. /// /// Enabling sampling shading requires the `sample_rate_shading` feature to be enabled on the /// device. /// /// The `min_fract` parameter is the minimum fraction of samples shading. For example if its /// value is 0.5, then the fragment shader will run for at least half of the samples. The other /// half of the samples will get their values determined automatically. /// /// Sample shading is disabled by default. /// /// # Panics /// /// - Panics if `min_fract` is not between 0.0 and 1.0. #[deprecated(since = "0.27.0", note = "Use `multisample_state` instead")] #[inline] pub fn sample_shading_enabled(mut self, min_fract: f32) -> Self { assert!((0.0..=1.0).contains(&min_fract)); self.multisample_state.sample_shading = Some(min_fract); self } // TODO: doc #[deprecated(since = "0.27.0", note = "Use `multisample_state` instead")] pub fn alpha_to_coverage_disabled(mut self) -> Self { self.multisample_state.alpha_to_coverage_enable = false; self } // TODO: doc #[deprecated(since = "0.27.0", note = "Use `multisample_state` instead")] pub fn alpha_to_coverage_enabled(mut self) -> Self { self.multisample_state.alpha_to_coverage_enable = true; self } /// Disables alpha-to-one. /// /// Alpha-to-one is disabled by default. #[deprecated(since = "0.27.0", note = "Use `multisample_state` instead")] #[inline] pub fn alpha_to_one_disabled(mut self) -> Self { self.multisample_state.alpha_to_one_enable = false; self } /// Enables alpha-to-one. The alpha component of the first color output of the fragment shader /// will be replaced by the value `1.0`. /// /// Enabling alpha-to-one requires the `alpha_to_one` feature to be enabled on the device. /// /// Alpha-to-one is disabled by default. #[deprecated(since = "0.27.0", note = "Use `multisample_state` instead")] #[inline] pub fn alpha_to_one_enabled(mut self) -> Self { self.multisample_state.alpha_to_one_enable = true; self } /// Sets the depth/stencil state. #[deprecated(since = "0.27.0", note = "Use `depth_stencil_state` instead")] #[inline] pub fn depth_stencil(self, depth_stencil_state: DepthStencilState) -> Self { self.depth_stencil_state(depth_stencil_state) } /// Sets the depth/stencil tests as disabled. /// /// > **Note**: This is a shortcut for all the other `depth_*` and `depth_stencil_*` methods /// > of the builder. #[deprecated(since = "0.27.0", note = "Use `depth_stencil_state` instead")] #[inline] pub fn depth_stencil_disabled(mut self) -> Self { self.depth_stencil_state = DepthStencilState::disabled(); self } /// Sets the depth/stencil tests as a simple depth test and no stencil test. /// /// > **Note**: This is a shortcut for setting the depth test to `Less`, the depth write Into /// > ` true` and disable the stencil test. #[deprecated(since = "0.27.0", note = "Use `depth_stencil_state` instead")] #[inline] pub fn depth_stencil_simple_depth(mut self) -> Self { self.depth_stencil_state = DepthStencilState::simple_depth_test(); self } /// Sets whether the depth buffer will be written. #[deprecated(since = "0.27.0", note = "Use `depth_stencil_state` instead")] #[inline] pub fn depth_write(mut self, write: bool) -> Self { let depth_state = self .depth_stencil_state .depth .get_or_insert(Default::default()); depth_state.write_enable = StateMode::Fixed(write); self } #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] // TODO: When this method is removed, also remove the special casing in `with_pipeline_layout` // for self.color_blend_state.attachments.len() == 1 #[inline] pub fn blend_collective(mut self, blend: AttachmentBlend) -> Self { self.color_blend_state.attachments = vec![ColorBlendAttachmentState { blend: Some(blend), color_write_mask: ColorComponents::all(), color_write_enable: StateMode::Fixed(true), }]; self } #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] pub fn blend_individual(mut self, blend: I) -> Self where I: IntoIterator, { self.color_blend_state.attachments = blend .into_iter() .map(|x| ColorBlendAttachmentState { blend: Some(x), color_write_mask: ColorComponents::all(), color_write_enable: StateMode::Fixed(true), }) .collect(); self } /// Each fragment shader output will have its value directly written to the framebuffer /// attachment. This is the default. #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] // TODO: When this method is removed, also remove the special casing in `with_pipeline_layout` // for self.color_blend_state.attachments.len() == 1 #[inline] pub fn blend_pass_through(mut self) -> Self { self.color_blend_state.attachments = vec![ColorBlendAttachmentState { blend: None, color_write_mask: ColorComponents::all(), color_write_enable: StateMode::Fixed(true), }]; self } #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] // TODO: When this method is removed, also remove the special casing in `with_pipeline_layout` // for self.color_blend_state.attachments.len() == 1 #[inline] pub fn blend_alpha_blending(mut self) -> Self { self.color_blend_state.attachments = vec![ColorBlendAttachmentState { blend: Some(AttachmentBlend::alpha()), color_write_mask: ColorComponents::all(), color_write_enable: StateMode::Fixed(true), }]; self } #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] #[inline] pub fn blend_logic_op(mut self, logic_op: LogicOp) -> Self { self.color_blend_state.logic_op = Some(StateMode::Fixed(logic_op)); self } /// Sets the logic operation as disabled. This is the default. #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] #[inline] pub fn blend_logic_op_disabled(mut self) -> Self { self.color_blend_state.logic_op = None; self } /// Sets the blend constant. The default is `[0.0, 0.0, 0.0, 0.0]`. /// /// The blend constant is used for some blending calculations. It is irrelevant otherwise. #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] #[inline] pub fn blend_constants(mut self, constants: [f32; 4]) -> Self { self.color_blend_state.blend_constants = StateMode::Fixed(constants); self } /// Sets the blend constant value as dynamic. Its value will need to be set before drawing. /// /// The blend constant is used for some blending calculations. It is irrelevant otherwise. #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] #[inline] pub fn blend_constants_dynamic(mut self) -> Self { self.color_blend_state.blend_constants = StateMode::Dynamic; self } /// Sets the render pass subpass to use. pub fn render_pass(self, render_pass: impl Into) -> Self { GraphicsPipelineBuilder { render_pass: Some(render_pass.into()), cache: self.cache, vertex_shader: self.vertex_shader, tessellation_shaders: self.tessellation_shaders, geometry_shader: self.geometry_shader, fragment_shader: self.fragment_shader, vertex_input_state: self.vertex_input_state, input_assembly_state: self.input_assembly_state, tessellation_state: self.tessellation_state, viewport_state: self.viewport_state, rasterization_state: self.rasterization_state, multisample_state: self.multisample_state, depth_stencil_state: self.depth_stencil_state, color_blend_state: self.color_blend_state, discard_rectangle_state: self.discard_rectangle_state, } } /// Enable caching of this pipeline via a PipelineCache object. /// /// If this pipeline already exists in the cache it will be used, if this is a new /// pipeline it will be inserted into the cache. The implementation handles the /// PipelineCache. #[inline] pub fn build_with_cache(mut self, pipeline_cache: Arc) -> Self { self.cache = Some(pipeline_cache); self } } impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> Clone for GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> where Vdef: Clone, Vss: Clone, Tcss: Clone, Tess: Clone, Gss: Clone, Fss: Clone, { fn clone(&self) -> Self { GraphicsPipelineBuilder { render_pass: self.render_pass.clone(), cache: self.cache.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_input_state: self.vertex_input_state.clone(), input_assembly_state: self.input_assembly_state, tessellation_state: self.tessellation_state, viewport_state: self.viewport_state.clone(), rasterization_state: self.rasterization_state.clone(), multisample_state: self.multisample_state, depth_stencil_state: self.depth_stencil_state.clone(), color_blend_state: self.color_blend_state.clone(), discard_rectangle_state: self.discard_rectangle_state.clone(), } } }