diff options
Diffstat (limited to 'src/pipeline/graphics/builder.rs')
-rw-r--r-- | src/pipeline/graphics/builder.rs | 4585 |
1 files changed, 4585 insertions, 0 deletions
diff --git a/src/pipeline/graphics/builder.rs b/src/pipeline/graphics/builder.rs new file mode 100644 index 0000000..3ef98da --- /dev/null +++ b/src/pipeline/graphics/builder.rs @@ -0,0 +1,4585 @@ +// Copyright (c) 2017 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +// 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<PipelineRenderPassType>, + cache: Option<Arc<PipelineCache>>, + + vertex_shader: Option<(EntryPoint<'vs>, Vss)>, + tessellation_shaders: Option<TessellationShaders<'tcs, 'tes, Tcss, Tess>>, + 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<Device>, + ) -> Result<Arc<GraphicsPipeline>, 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<F>( + self, + device: Arc<Device>, + func: F, + ) -> Result<Arc<GraphicsPipeline>, 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::<Result<Vec<_>, _>>()?; + 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<Device>, + pipeline_layout: Arc<PipelineLayout>, + ) -> Result<Arc<GraphicsPipeline>, 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<DynamicState, bool>, + HashMap<ShaderStage, ()>, + Option<FragmentTestsStages>, + ), + 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<DynamicState, bool> = 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<T>( + 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<I>(self, viewports: I) -> Self + where + I: IntoIterator<Item = Viewport>, + { + 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<I>(mut self, viewports: I) -> Self + where + I: IntoIterator<Item = (Viewport, Scissor)>, + { + 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<I>(mut self, scissors: I) -> Self + where + I: IntoIterator<Item = Scissor>, + { + 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<I>(mut self, viewports: I) -> Self + where + I: IntoIterator<Item = Viewport>, + { + 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<I>(mut self, blend: I) -> Self + where + I: IntoIterator<Item = AttachmentBlend>, + { + 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<PipelineRenderPassType>) -> 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<PipelineCache>) -> 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(), + } + } +} |