From b7679f8bbbcb62b41793dfaca243546ccf2879a0 Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 18 Apr 2023 20:53:08 +0200 Subject: [PATCH] Rewrite shader and specialization handling in pipelines (#2181) * Rewrite shader and specialization handling in pipelines * Make the shader loading in examples a bit cleaner * Forgot some * Fix incorrect color blend states in examples * Nicer fix * Use mem::discriminant Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com> --------- Co-authored-by: marc0246 <40955683+marc0246@users.noreply.github.com> --- examples/src/bin/basic-compute-shader.rs | 9 +- examples/src/bin/buffer-allocator.rs | 29 +- .../deferred/frame/ambient_lighting_system.rs | 23 +- .../frame/directional_lighting_system.rs | 23 +- .../deferred/frame/point_lighting_system.rs | 23 +- .../src/bin/deferred/triangle_draw_system.rs | 25 +- examples/src/bin/dynamic-buffers.rs | 9 +- examples/src/bin/dynamic-local-size.rs | 29 +- examples/src/bin/gl-interop.rs | 25 +- examples/src/bin/image-self-copy-blit/main.rs | 23 +- examples/src/bin/image/main.rs | 22 +- examples/src/bin/immutable-sampler/main.rs | 22 +- examples/src/bin/indirect.rs | 37 +- examples/src/bin/instancing.rs | 29 +- .../fractal_compute_pipeline.rs | 9 +- .../pixels_draw_pipeline.rs | 26 +- examples/src/bin/msaa-renderpass.rs | 24 +- examples/src/bin/multi-window.rs | 29 +- .../multi_window_game_of_life/game_of_life.rs | 9 +- .../multi_window_game_of_life/pixels_draw.rs | 26 +- examples/src/bin/multiview.rs | 29 +- examples/src/bin/occlusion-query.rs | 29 +- examples/src/bin/pipeline-caching.rs | 9 +- examples/src/bin/push-constants.rs | 9 +- examples/src/bin/push-descriptors/main.rs | 22 +- examples/src/bin/runtime-shader/main.rs | 24 +- examples/src/bin/runtime_array/main.rs | 38 +- examples/src/bin/self-copy-buffer.rs | 9 +- examples/src/bin/shader-include/main.rs | 9 +- examples/src/bin/shader-types-sharing.rs | 35 +- examples/src/bin/simple-particles.rs | 35 +- examples/src/bin/specialization-constants.rs | 20 +- examples/src/bin/teapot/main.rs | 48 +- examples/src/bin/tessellation.rs | 46 +- examples/src/bin/texture_array/main.rs | 22 +- examples/src/bin/triangle-v1_3.rs | 71 +- examples/src/bin/triangle.rs | 55 +- vulkano-shaders/src/codegen.rs | 15 +- vulkano-shaders/src/entry_point.rs | 93 +- vulkano-shaders/src/lib.rs | 43 +- vulkano-shaders/src/structs.rs | 154 - vulkano/src/pipeline/cache.rs | 85 +- vulkano/src/pipeline/compute.rs | 408 +- vulkano/src/pipeline/graphics/builder.rs | 5117 +++++++---------- .../src/pipeline/graphics/creation_error.rs | 100 +- vulkano/src/pipeline/graphics/mod.rs | 14 +- vulkano/src/shader/mod.rs | 819 +-- vulkano/src/shader/reflect.rs | 161 +- 48 files changed, 3882 insertions(+), 4088 deletions(-) diff --git a/examples/src/bin/basic-compute-shader.rs b/examples/src/bin/basic-compute-shader.rs index a6124f89..f5b6e03d 100644 --- a/examples/src/bin/basic-compute-shader.rs +++ b/examples/src/bin/basic-compute-shader.rs @@ -28,6 +28,7 @@ use vulkano::{ instance::{Instance, InstanceCreateInfo}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, + shader::PipelineShaderStageCreateInfo, sync::{self, GpuFuture}, VulkanLibrary, }; @@ -134,12 +135,14 @@ fn main() { ", } } - let shader = cs::load(device.clone()).unwrap(); + let shader = cs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); ComputePipeline::new( device.clone(), - shader.entry_point("main").unwrap(), - &(), + PipelineShaderStageCreateInfo::entry_point(shader), None, |_| {}, ) diff --git a/examples/src/bin/buffer-allocator.rs b/examples/src/bin/buffer-allocator.rs index ad4e64c1..a50e4bcb 100644 --- a/examples/src/bin/buffer-allocator.rs +++ b/examples/src/bin/buffer-allocator.rs @@ -31,13 +31,17 @@ use vulkano::{ memory::allocator::StandardMemoryAllocator, pipeline::{ graphics::{ + color_blend::ColorBlendState, input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, GraphicsPipeline, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, + shader::PipelineShaderStageCreateInfo, swapchain::{ acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo, @@ -202,9 +206,6 @@ fn main() { } } - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let render_pass = vulkano::single_pass_renderpass!( device.clone(), attachments: { @@ -222,13 +223,27 @@ fn main() { ) .unwrap(); + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(Vertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .input_assembly_state(InputAssemblyState::new()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) - .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments())) + .render_pass(subpass) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/deferred/frame/ambient_lighting_system.rs b/examples/src/bin/deferred/frame/ambient_lighting_system.rs index f362e003..18d33144 100644 --- a/examples/src/bin/deferred/frame/ambient_lighting_system.rs +++ b/examples/src/bin/deferred/frame/ambient_lighting_system.rs @@ -24,12 +24,15 @@ use vulkano::{ graphics::{ color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState}, input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, GraphicsPipeline, Pipeline, PipelineBindPoint, }, render_pass::Subpass, + shader::PipelineShaderStageCreateInfo, }; use super::LightingVertex; @@ -81,15 +84,25 @@ impl AmbientLightingSystem { .expect("failed to create buffer"); let pipeline = { - let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module"); - let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); + let vs = vs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .expect("shader entry point not found"); + let fs = fs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .expect("shader entry point not found"); GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(LightingVertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .input_assembly_state(InputAssemblyState::new()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend( AttachmentBlend { color_op: BlendOp::Add, diff --git a/examples/src/bin/deferred/frame/directional_lighting_system.rs b/examples/src/bin/deferred/frame/directional_lighting_system.rs index 2c62da49..919a4f73 100644 --- a/examples/src/bin/deferred/frame/directional_lighting_system.rs +++ b/examples/src/bin/deferred/frame/directional_lighting_system.rs @@ -25,12 +25,15 @@ use vulkano::{ graphics::{ color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState}, input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, GraphicsPipeline, Pipeline, PipelineBindPoint, }, render_pass::Subpass, + shader::PipelineShaderStageCreateInfo, }; use super::LightingVertex; @@ -84,15 +87,25 @@ impl DirectionalLightingSystem { }; let pipeline = { - let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module"); - let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); + let vs = vs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .expect("shader entry point not found"); + let fs = fs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .expect("shader entry point not found"); GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(LightingVertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .input_assembly_state(InputAssemblyState::new()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend( AttachmentBlend { color_op: BlendOp::Add, diff --git a/examples/src/bin/deferred/frame/point_lighting_system.rs b/examples/src/bin/deferred/frame/point_lighting_system.rs index b16a5abf..ab654c92 100644 --- a/examples/src/bin/deferred/frame/point_lighting_system.rs +++ b/examples/src/bin/deferred/frame/point_lighting_system.rs @@ -25,12 +25,15 @@ use vulkano::{ graphics::{ color_blend::{AttachmentBlend, BlendFactor, BlendOp, ColorBlendState}, input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, GraphicsPipeline, Pipeline, PipelineBindPoint, }, render_pass::Subpass, + shader::PipelineShaderStageCreateInfo, }; use super::LightingVertex; @@ -81,15 +84,25 @@ impl PointLightingSystem { .expect("failed to create buffer"); let pipeline = { - let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module"); - let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); + let vs = vs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .expect("shader entry point not found"); + let fs = fs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .expect("shader entry point not found"); GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(LightingVertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .input_assembly_state(InputAssemblyState::new()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend( AttachmentBlend { color_op: BlendOp::Add, diff --git a/examples/src/bin/deferred/triangle_draw_system.rs b/examples/src/bin/deferred/triangle_draw_system.rs index 845adfb9..a9e54a06 100644 --- a/examples/src/bin/deferred/triangle_draw_system.rs +++ b/examples/src/bin/deferred/triangle_draw_system.rs @@ -18,14 +18,18 @@ use vulkano::{ memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ graphics::{ + color_blend::ColorBlendState, depth_stencil::DepthStencilState, input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, GraphicsPipeline, }, render_pass::Subpass, + shader::PipelineShaderStageCreateInfo, }; pub struct TriangleDrawSystem { @@ -70,16 +74,27 @@ impl TriangleDrawSystem { .expect("failed to create buffer"); let pipeline = { - let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module"); - let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); + let vs = vs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .expect("shader entry point not found"); + let fs = fs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .expect("shader entry point not found"); GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(TriangleVertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .input_assembly_state(InputAssemblyState::new()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) + .rasterization_state(RasterizationState::default()) .depth_stencil_state(DepthStencilState::simple_depth_test()) + .multisample_state(MultisampleState::default()) + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments())) .render_pass(subpass.clone()) .build(gfx_queue.device().clone()) .unwrap() diff --git a/examples/src/bin/dynamic-buffers.rs b/examples/src/bin/dynamic-buffers.rs index 33f2c974..16cc00b2 100644 --- a/examples/src/bin/dynamic-buffers.rs +++ b/examples/src/bin/dynamic-buffers.rs @@ -30,6 +30,7 @@ use vulkano::{ instance::{Instance, InstanceCreateInfo}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, + shader::PipelineShaderStageCreateInfo, sync::{self, GpuFuture}, DeviceSize, VulkanLibrary, }; @@ -118,11 +119,13 @@ fn main() { } } - let shader = shader::load(device.clone()).unwrap(); + let shader = shader::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); let pipeline = ComputePipeline::new( device.clone(), - shader.entry_point("main").unwrap(), - &(), + PipelineShaderStageCreateInfo::entry_point(shader), None, |layout_create_infos| { let binding = layout_create_infos[0].bindings.get_mut(&0).unwrap(); diff --git a/examples/src/bin/dynamic-local-size.rs b/examples/src/bin/dynamic-local-size.rs index b25c0ba4..5e35bc86 100644 --- a/examples/src/bin/dynamic-local-size.rs +++ b/examples/src/bin/dynamic-local-size.rs @@ -32,6 +32,7 @@ use vulkano::{ instance::{Instance, InstanceCreateInfo, InstanceExtensions}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, + shader::PipelineShaderStageCreateInfo, sync::{self, GpuFuture}, VulkanLibrary, }; @@ -149,8 +150,6 @@ fn main() { } } - let shader = cs::load(device.clone()).unwrap(); - // Fetching subgroup size from the physical device properties to determine an appropriate // compute shader local size. // @@ -175,18 +174,24 @@ fn main() { println!("Local size will be set to: ({local_size_x}, {local_size_y}, 1)"); - let spec_consts = cs::SpecializationConstants { - red: 0.2, - green: 0.5, - blue: 1.0, - // Specify the local size constants. - constant_1: local_size_x, - constant_2: local_size_y, - }; + let shader = cs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); let pipeline = ComputePipeline::new( device.clone(), - shader.entry_point("main").unwrap(), - &spec_consts, + PipelineShaderStageCreateInfo { + specialization_info: [ + (0, 0.2f32.into()), + (1, local_size_x.into()), + (2, local_size_y.into()), + (3, 0.5f32.into()), + (4, 1.0f32.into()), + ] + .into_iter() + .collect(), + ..PipelineShaderStageCreateInfo::entry_point(shader) + }, None, |_| {}, ) diff --git a/examples/src/bin/gl-interop.rs b/examples/src/bin/gl-interop.rs index dccc8775..77fe8825 100644 --- a/examples/src/bin/gl-interop.rs +++ b/examples/src/bin/gl-interop.rs @@ -38,6 +38,8 @@ mod linux { graphics::{ color_blend::ColorBlendState, input_assembly::{InputAssemblyState, PrimitiveTopology}, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Scissor, Viewport, ViewportState}, }, @@ -45,6 +47,7 @@ mod linux { }, render_pass::{Framebuffer, RenderPass, Subpass}, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + shader::PipelineShaderStageCreateInfo, swapchain::{ AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo, @@ -582,9 +585,6 @@ mod linux { ) .unwrap(); - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let render_pass = vulkano::single_pass_renderpass!(device.clone(), attachments: { color: { @@ -612,11 +612,21 @@ mod linux { ) .unwrap(); + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); - let pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(MyVertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state( InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip), ) @@ -624,8 +634,9 @@ mod linux { scissors: (0..1).map(|_| Scissor::irrelevant()).collect(), viewport_count_dynamic: false, }) - .fragment_shader(fs.entry_point("main").unwrap(), ()) - .color_blend_state(ColorBlendState::new(1).blend_alpha()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .render_pass(subpass) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/image-self-copy-blit/main.rs b/examples/src/bin/image-self-copy-blit/main.rs index a0e61a03..9d684d2b 100644 --- a/examples/src/bin/image-self-copy-blit/main.rs +++ b/examples/src/bin/image-self-copy-blit/main.rs @@ -34,6 +34,8 @@ use vulkano::{ graphics::{ color_blend::ColorBlendState, input_assembly::{InputAssemblyState, PrimitiveTopology}, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, @@ -41,6 +43,7 @@ use vulkano::{ }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + shader::PipelineShaderStageCreateInfo, swapchain::{ acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo, @@ -194,9 +197,6 @@ fn main() { ) .unwrap(); - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let render_pass = vulkano::single_pass_renderpass!(device.clone(), attachments: { color: { @@ -334,13 +334,26 @@ fn main() { ) .unwrap(); + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(Vertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip)) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .render_pass(subpass) .build(device.clone()) diff --git a/examples/src/bin/image/main.rs b/examples/src/bin/image/main.rs index 1a10610e..457fb8a5 100644 --- a/examples/src/bin/image/main.rs +++ b/examples/src/bin/image/main.rs @@ -32,6 +32,8 @@ use vulkano::{ graphics::{ color_blend::ColorBlendState, input_assembly::{InputAssemblyState, PrimitiveTopology}, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, @@ -39,6 +41,7 @@ use vulkano::{ }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + shader::PipelineShaderStageCreateInfo, swapchain::{ acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo, @@ -192,9 +195,6 @@ fn main() { ) .unwrap(); - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let render_pass = vulkano::single_pass_renderpass!( device.clone(), attachments: { @@ -260,13 +260,25 @@ fn main() { ) .unwrap(); + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(Vertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip)) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .render_pass(subpass) .build(device.clone()) diff --git a/examples/src/bin/immutable-sampler/main.rs b/examples/src/bin/immutable-sampler/main.rs index 02f6beb6..92798d7b 100644 --- a/examples/src/bin/immutable-sampler/main.rs +++ b/examples/src/bin/immutable-sampler/main.rs @@ -41,6 +41,8 @@ use vulkano::{ graphics::{ color_blend::ColorBlendState, input_assembly::{InputAssemblyState, PrimitiveTopology}, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, @@ -48,6 +50,7 @@ use vulkano::{ }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + shader::PipelineShaderStageCreateInfo, swapchain::{ acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo, @@ -198,9 +201,6 @@ fn main() { ) .unwrap(); - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let render_pass = vulkano::single_pass_renderpass!( device.clone(), attachments: { @@ -266,13 +266,25 @@ fn main() { ) .unwrap(); + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(Vertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip)) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .render_pass(subpass) .with_auto_layout(device.clone(), |layout_create_infos| { diff --git a/examples/src/bin/indirect.rs b/examples/src/bin/indirect.rs index 4254b05f..558391dd 100644 --- a/examples/src/bin/indirect.rs +++ b/examples/src/bin/indirect.rs @@ -45,13 +45,17 @@ use vulkano::{ memory::allocator::StandardMemoryAllocator, pipeline::{ graphics::{ + color_blend::ColorBlendState, input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, ComputePipeline, GraphicsPipeline, Pipeline, PipelineBindPoint, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, + shader::PipelineShaderStageCreateInfo, single_pass_renderpass, swapchain::{ acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, @@ -242,10 +246,6 @@ fn main() { } } - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let cs = cs::load(device.clone()).unwrap(); - let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone())); // Each frame we generate a new set of vertices and each frame we need a new @@ -265,10 +265,13 @@ fn main() { }, ); + let cs = cs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); let compute_pipeline = ComputePipeline::new( device.clone(), - cs.entry_point("main").unwrap(), - &(), + PipelineShaderStageCreateInfo::entry_point(cs), None, |_| {}, ) @@ -300,13 +303,27 @@ fn main() { position: [f32; 2], } + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let render_pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(Vertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .input_assembly_state(InputAssemblyState::new()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) - .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments())) + .render_pass(subpass) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/instancing.rs b/examples/src/bin/instancing.rs index 369a2b9c..b3360402 100644 --- a/examples/src/bin/instancing.rs +++ b/examples/src/bin/instancing.rs @@ -28,13 +28,17 @@ use vulkano::{ memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ graphics::{ + color_blend::ColorBlendState, input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, GraphicsPipeline, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, + shader::PipelineShaderStageCreateInfo, single_pass_renderpass, swapchain::{ acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, @@ -272,9 +276,6 @@ fn main() { } } - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let render_pass = single_pass_renderpass!( device.clone(), attachments: { @@ -292,15 +293,29 @@ fn main() { ) .unwrap(); + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) // Use the implementations of the `Vertex` trait to describe to vulkano how the two vertex // types are expected to be used. .vertex_input_state([TriangleVertex::per_vertex(), InstanceData::per_instance()]) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .input_assembly_state(InputAssemblyState::new()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) - .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments())) + .render_pass(subpass) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/interactive_fractal/fractal_compute_pipeline.rs b/examples/src/bin/interactive_fractal/fractal_compute_pipeline.rs index 2252e221..e59d5e6b 100644 --- a/examples/src/bin/interactive_fractal/fractal_compute_pipeline.rs +++ b/examples/src/bin/interactive_fractal/fractal_compute_pipeline.rs @@ -23,6 +23,7 @@ use vulkano::{ image::ImageAccess, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, + shader::PipelineShaderStageCreateInfo, sync::GpuFuture, }; use vulkano_util::renderer::DeviceImageView; @@ -71,11 +72,13 @@ impl FractalComputePipeline { let end_color = [0.0; 4]; let pipeline = { - let shader = cs::load(queue.device().clone()).unwrap(); + let shader = cs::load(queue.device().clone()) + .unwrap() + .entry_point("main") + .unwrap(); ComputePipeline::new( queue.device().clone(), - shader.entry_point("main").unwrap(), - &(), + PipelineShaderStageCreateInfo::entry_point(shader), None, |_| {}, ) diff --git a/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs b/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs index d4df5821..825699bf 100644 --- a/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs +++ b/examples/src/bin/interactive_fractal/pixels_draw_pipeline.rs @@ -22,7 +22,10 @@ use vulkano::{ memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryUsage}, pipeline::{ graphics::{ + color_blend::ColorBlendState, input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, @@ -30,6 +33,7 @@ use vulkano::{ }, render_pass::Subpass, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerMipmapMode}, + shader::PipelineShaderStageCreateInfo, }; /// Vertex for textured quads. @@ -114,14 +118,26 @@ impl PixelsDrawPipeline { .unwrap(); let pipeline = { - let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module"); - let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); + let vs = vs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .expect("shader entry point not found"); + let fs = fs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .expect("shader entry point not found"); + GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(TexturedVertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .input_assembly_state(InputAssemblyState::new()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments())) .render_pass(subpass.clone()) .build(gfx_queue.device().clone()) .unwrap() diff --git a/examples/src/bin/msaa-renderpass.rs b/examples/src/bin/msaa-renderpass.rs index 7ef21c77..02e3948b 100644 --- a/examples/src/bin/msaa-renderpass.rs +++ b/examples/src/bin/msaa-renderpass.rs @@ -79,13 +79,17 @@ use vulkano::{ memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ graphics::{ + color_blend::ColorBlendState, + input_assembly::InputAssemblyState, multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, GraphicsPipeline, }, render_pass::{Framebuffer, FramebufferCreateInfo, Subpass}, + shader::PipelineShaderStageCreateInfo, sync::GpuFuture, VulkanLibrary, }; @@ -263,9 +267,6 @@ fn main() { } } - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - #[derive(BufferContents, Vertex)] #[repr(C)] struct Vertex { @@ -298,16 +299,29 @@ fn main() { ) .unwrap(); + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); let subpass = Subpass::from(render_pass, 0).unwrap(); let pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(Vertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) + .rasterization_state(RasterizationState::default()) .multisample_state(MultisampleState { rasterization_samples: subpass.num_samples().unwrap(), ..Default::default() }) + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments())) .render_pass(subpass) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/multi-window.rs b/examples/src/bin/multi-window.rs index fae36b7a..ddcf2e25 100644 --- a/examples/src/bin/multi-window.rs +++ b/examples/src/bin/multi-window.rs @@ -32,13 +32,17 @@ use vulkano::{ memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ graphics::{ + color_blend::ColorBlendState, input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, GraphicsPipeline, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, + shader::PipelineShaderStageCreateInfo, swapchain::{ acquire_next_image, AcquireError, Surface, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo, @@ -241,9 +245,6 @@ fn main() { } } - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let render_pass = vulkano::single_pass_renderpass!( device.clone(), attachments: { @@ -261,13 +262,27 @@ fn main() { ) .unwrap(); + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(Vertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .input_assembly_state(InputAssemblyState::new()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) - .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments())) + .render_pass(subpass) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/multi_window_game_of_life/game_of_life.rs b/examples/src/bin/multi_window_game_of_life/game_of_life.rs index 41f6ea5d..12f9151f 100644 --- a/examples/src/bin/multi_window_game_of_life/game_of_life.rs +++ b/examples/src/bin/multi_window_game_of_life/game_of_life.rs @@ -25,6 +25,7 @@ use vulkano::{ image::{ImageAccess, ImageUsage, StorageImage}, memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryUsage}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, + shader::PipelineShaderStageCreateInfo, sync::GpuFuture, }; use vulkano_util::renderer::DeviceImageView; @@ -68,11 +69,13 @@ impl GameOfLifeComputePipeline { let life_out = rand_grid(memory_allocator, size); let compute_life_pipeline = { - let shader = compute_life_cs::load(compute_queue.device().clone()).unwrap(); + let shader = compute_life_cs::load(compute_queue.device().clone()) + .unwrap() + .entry_point("main") + .unwrap(); ComputePipeline::new( compute_queue.device().clone(), - shader.entry_point("main").unwrap(), - &(), + PipelineShaderStageCreateInfo::entry_point(shader), None, |_| {}, ) diff --git a/examples/src/bin/multi_window_game_of_life/pixels_draw.rs b/examples/src/bin/multi_window_game_of_life/pixels_draw.rs index f821b341..10460d2c 100644 --- a/examples/src/bin/multi_window_game_of_life/pixels_draw.rs +++ b/examples/src/bin/multi_window_game_of_life/pixels_draw.rs @@ -23,7 +23,10 @@ use vulkano::{ memory::allocator::{AllocationCreateInfo, MemoryUsage}, pipeline::{ graphics::{ + color_blend::ColorBlendState, input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, @@ -31,6 +34,7 @@ use vulkano::{ }, render_pass::Subpass, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerMipmapMode}, + shader::PipelineShaderStageCreateInfo, }; /// Vertex for textured quads. @@ -110,14 +114,26 @@ impl PixelsDrawPipeline { .unwrap(); let pipeline = { - let vs = vs::load(gfx_queue.device().clone()).expect("failed to create shader module"); - let fs = fs::load(gfx_queue.device().clone()).expect("failed to create shader module"); + let vs = vs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .expect("shader entry point not found"); + let fs = fs::load(gfx_queue.device().clone()) + .expect("failed to create shader module") + .entry_point("main") + .expect("shader entry point not found"); + GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(TexturedVertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .input_assembly_state(InputAssemblyState::new()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments())) .render_pass(subpass.clone()) .build(gfx_queue.device().clone()) .unwrap() diff --git a/examples/src/bin/multiview.rs b/examples/src/bin/multiview.rs index bfcd815b..f62fc8f5 100644 --- a/examples/src/bin/multiview.rs +++ b/examples/src/bin/multiview.rs @@ -32,7 +32,10 @@ use vulkano::{ memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ graphics::{ + color_blend::ColorBlendState, input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, @@ -42,6 +45,7 @@ use vulkano::{ AttachmentDescription, AttachmentReference, Framebuffer, FramebufferCreateInfo, LoadOp, RenderPass, RenderPassCreateInfo, StoreOp, Subpass, SubpassDescription, }, + shader::PipelineShaderStageCreateInfo, sync::{self, GpuFuture}, VulkanLibrary, }; @@ -214,9 +218,6 @@ fn main() { } } - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let render_pass_description = RenderPassCreateInfo { attachments: vec![AttachmentDescription { format: Some(image.format()), @@ -257,10 +258,22 @@ fn main() { ) .unwrap(); + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let subpass = Subpass::from(render_pass, 0).unwrap(); let pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(Vertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .input_assembly_state(InputAssemblyState::new()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([ Viewport { origin: [0.0, 0.0], @@ -271,8 +284,10 @@ fn main() { depth_range: 0.0..1.0, }, ])) - .fragment_shader(fs.entry_point("main").unwrap(), ()) - .render_pass(Subpass::from(render_pass, 0).unwrap()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments())) + .render_pass(subpass) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/occlusion-query.rs b/examples/src/bin/occlusion-query.rs index 230c5f40..ae91163e 100644 --- a/examples/src/bin/occlusion-query.rs +++ b/examples/src/bin/occlusion-query.rs @@ -28,8 +28,11 @@ use vulkano::{ memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ graphics::{ + color_blend::ColorBlendState, depth_stencil::DepthStencilState, input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, @@ -37,6 +40,7 @@ use vulkano::{ }, query::{QueryControlFlags, QueryPool, QueryPoolCreateInfo, QueryResultFlags, QueryType}, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, + shader::PipelineShaderStageCreateInfo, swapchain::{ acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo, @@ -276,9 +280,6 @@ fn main() { } } - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let render_pass = vulkano::single_pass_renderpass!( device.clone(), attachments: { @@ -302,17 +303,31 @@ fn main() { ) .unwrap(); + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(Vertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .input_assembly_state(InputAssemblyState::new()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) - .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) // Enable depth testing, which is needed for occlusion queries to make sense at all. If you // disable depth testing, every pixel is considered to pass the depth test, so every query // will return a nonzero result. .depth_stencil_state(DepthStencilState::simple_depth_test()) + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments())) + .render_pass(subpass) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/pipeline-caching.rs b/examples/src/bin/pipeline-caching.rs index f89b23de..5d228ff3 100644 --- a/examples/src/bin/pipeline-caching.rs +++ b/examples/src/bin/pipeline-caching.rs @@ -35,6 +35,7 @@ use vulkano::{ }, instance::{Instance, InstanceCreateInfo}, pipeline::{cache::PipelineCache, ComputePipeline}, + shader::PipelineShaderStageCreateInfo, VulkanLibrary, }; @@ -127,11 +128,13 @@ fn main() { ", } } - let shader = cs::load(device.clone()).unwrap(); + let shader = cs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); ComputePipeline::new( device.clone(), - shader.entry_point("main").unwrap(), - &(), + PipelineShaderStageCreateInfo::entry_point(shader), Some(pipeline_cache.clone()), |_| {}, ) diff --git a/examples/src/bin/push-constants.rs b/examples/src/bin/push-constants.rs index 42816685..affe1152 100644 --- a/examples/src/bin/push-constants.rs +++ b/examples/src/bin/push-constants.rs @@ -27,6 +27,7 @@ use vulkano::{ instance::{Instance, InstanceCreateInfo}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, + shader::PipelineShaderStageCreateInfo, sync::{self, GpuFuture}, VulkanLibrary, }; @@ -115,11 +116,13 @@ fn main() { } } - let shader = cs::load(device.clone()).unwrap(); + let shader = cs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); let pipeline = ComputePipeline::new( device.clone(), - shader.entry_point("main").unwrap(), - &(), + PipelineShaderStageCreateInfo::entry_point(shader), None, |_| {}, ) diff --git a/examples/src/bin/push-descriptors/main.rs b/examples/src/bin/push-descriptors/main.rs index 4672caec..9a623e56 100644 --- a/examples/src/bin/push-descriptors/main.rs +++ b/examples/src/bin/push-descriptors/main.rs @@ -30,6 +30,8 @@ use vulkano::{ graphics::{ color_blend::ColorBlendState, input_assembly::{InputAssemblyState, PrimitiveTopology}, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, @@ -37,6 +39,7 @@ use vulkano::{ }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + shader::PipelineShaderStageCreateInfo, swapchain::{ acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo, @@ -188,9 +191,6 @@ fn main() { ) .unwrap(); - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let render_pass = vulkano::single_pass_renderpass!( device.clone(), attachments: { @@ -255,13 +255,25 @@ fn main() { ) .unwrap(); + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(Vertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip)) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .render_pass(subpass) .with_auto_layout(device.clone(), |layout_create_infos| { diff --git a/examples/src/bin/runtime-shader/main.rs b/examples/src/bin/runtime-shader/main.rs index 88dcd4ab..b4d67716 100644 --- a/examples/src/bin/runtime-shader/main.rs +++ b/examples/src/bin/runtime-shader/main.rs @@ -37,7 +37,9 @@ use vulkano::{ memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ graphics::{ + color_blend::ColorBlendState, input_assembly::InputAssemblyState, + multisample::MultisampleState, rasterization::{CullMode, FrontFace, RasterizationState}, vertex_input::Vertex, viewport::{Viewport, ViewportState}, @@ -45,7 +47,7 @@ use vulkano::{ GraphicsPipeline, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, - shader::ShaderModule, + shader::{PipelineShaderStageCreateInfo, ShaderModule}, swapchain::{ acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo, @@ -186,7 +188,8 @@ fn main() { // Create a ShaderModule on a device the same Shader::load does it. // NOTE: You will have to verify correctness of the data by yourself! - unsafe { ShaderModule::from_bytes(device.clone(), &v) }.unwrap() + let module = unsafe { ShaderModule::from_bytes(device.clone(), &v).unwrap() }; + module.entry_point("main").unwrap() }; let fs = { @@ -195,21 +198,26 @@ fn main() { let mut v = vec![]; f.read_to_end(&mut v).unwrap(); - unsafe { ShaderModule::from_bytes(device.clone(), &v) }.unwrap() + let module = unsafe { ShaderModule::from_bytes(device.clone(), &v).unwrap() }; + module.entry_point("main").unwrap() }; - + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let graphics_pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(Vertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .input_assembly_state(InputAssemblyState::new()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) .rasterization_state( RasterizationState::new() .cull_mode(CullMode::Front) .front_face(FrontFace::CounterClockwise), ) - .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .multisample_state(MultisampleState::default()) + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments())) + .render_pass(subpass) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/runtime_array/main.rs b/examples/src/bin/runtime_array/main.rs index 1cbc9c01..8d5b00ab 100644 --- a/examples/src/bin/runtime_array/main.rs +++ b/examples/src/bin/runtime_array/main.rs @@ -35,6 +35,9 @@ use vulkano::{ pipeline::{ graphics::{ color_blend::ColorBlendState, + input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, @@ -43,6 +46,7 @@ use vulkano::{ }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + shader::PipelineShaderStageCreateInfo, swapchain::{ acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo, @@ -255,9 +259,6 @@ fn main() { ) .unwrap(); - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let render_pass = vulkano::single_pass_renderpass!( device.clone(), attachments: { @@ -352,11 +353,20 @@ fn main() { ) .unwrap(); + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); let pipeline_layout = { let mut layout_create_infos: Vec<_> = DescriptorSetLayoutCreateInfo::from_requirements( - fs.entry_point("main") - .unwrap() - .descriptor_binding_requirements(), + fs.info() + .descriptor_binding_requirements + .iter() + .map(|(k, v)| (*k, v)), ); // Set 0, Binding 0. @@ -375,11 +385,10 @@ fn main() { PipelineLayoutCreateInfo { set_layouts, push_constant_ranges: fs - .entry_point("main") - .unwrap() - .push_constant_requirements() + .info() + .push_constant_requirements + .iter() .cloned() - .into_iter() .collect(), ..Default::default() }, @@ -389,10 +398,15 @@ fn main() { let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(Vertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .render_pass(subpass) .with_pipeline_layout(device.clone(), pipeline_layout) diff --git a/examples/src/bin/self-copy-buffer.rs b/examples/src/bin/self-copy-buffer.rs index fe0070df..1fac8033 100644 --- a/examples/src/bin/self-copy-buffer.rs +++ b/examples/src/bin/self-copy-buffer.rs @@ -27,6 +27,7 @@ use vulkano::{ instance::{Instance, InstanceCreateInfo}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, + shader::PipelineShaderStageCreateInfo, sync::{self, GpuFuture}, VulkanLibrary, }; @@ -107,12 +108,14 @@ fn main() { ", } } - let shader = cs::load(device.clone()).unwrap(); + let shader = cs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); ComputePipeline::new( device.clone(), - shader.entry_point("main").unwrap(), - &(), + PipelineShaderStageCreateInfo::entry_point(shader), None, |_| {}, ) diff --git a/examples/src/bin/shader-include/main.rs b/examples/src/bin/shader-include/main.rs index 48c15288..03351838 100644 --- a/examples/src/bin/shader-include/main.rs +++ b/examples/src/bin/shader-include/main.rs @@ -26,6 +26,7 @@ use vulkano::{ instance::{Instance, InstanceCreateInfo}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, + shader::PipelineShaderStageCreateInfo, sync::{self, GpuFuture}, VulkanLibrary, }; @@ -115,11 +116,13 @@ fn main() { "#, } } - let shader = cs::load(device.clone()).unwrap(); + let shader = cs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); ComputePipeline::new( device.clone(), - shader.entry_point("main").unwrap(), - &(), + PipelineShaderStageCreateInfo::entry_point(shader), None, |_| {}, ) diff --git a/examples/src/bin/shader-types-sharing.rs b/examples/src/bin/shader-types-sharing.rs index 36d1164d..12e5405d 100644 --- a/examples/src/bin/shader-types-sharing.rs +++ b/examples/src/bin/shader-types-sharing.rs @@ -41,6 +41,7 @@ use vulkano::{ instance::{Instance, InstanceCreateInfo}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, + shader::PipelineShaderStageCreateInfo, sync::{self, GpuFuture}, VulkanLibrary, }; @@ -118,11 +119,6 @@ fn main() { // such types, and include it in each shader entry-point file using the `#include` // directive. shaders: { - // Generate single unique `SpecializationConstants` struct for all shaders, since - // their specialization interfaces are the same. This option is turned off by - // default and the macro by default produces unique structs - // (`MultSpecializationConstants` and `AddSpecializationConstants` in this case). - shared_constants: true, mult: { ty: "compute", src: r" @@ -179,7 +175,6 @@ fn main() { // The macro will create the following things in this module: // - `load_mult` for the first shader loader/entry-point. // - `load_add` for the second shader loader/entry-point. - // - `SpecializationConstants` struct for both shaders' specialization constants. // - `Parameters` struct common for both shaders. } @@ -251,26 +246,32 @@ fn main() { .unwrap(); // Load the first shader, and create a pipeline for the shader. + let mult_shader = shaders::load_mult(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); let mult_pipeline = ComputePipeline::new( device.clone(), - shaders::load_mult(device.clone()) - .unwrap() - .entry_point("main") - .unwrap(), - &shaders::SpecializationConstants { enabled: 1 }, + PipelineShaderStageCreateInfo { + specialization_info: [(0, true.into())].into_iter().collect(), + ..PipelineShaderStageCreateInfo::entry_point(mult_shader) + }, None, |_| {}, ) .unwrap(); // Load the second shader, and create a pipeline for the shader. + let add_shader = shaders::load_add(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); let add_pipeline = ComputePipeline::new( - device.clone(), - shaders::load_add(device) - .unwrap() - .entry_point("main") - .unwrap(), - &shaders::SpecializationConstants { enabled: 1 }, + device, + PipelineShaderStageCreateInfo { + specialization_info: [(0, true.into())].into_iter().collect(), + ..PipelineShaderStageCreateInfo::entry_point(add_shader) + }, None, |_| {}, ) diff --git a/examples/src/bin/simple-particles.rs b/examples/src/bin/simple-particles.rs index a6d50abe..d9197e30 100644 --- a/examples/src/bin/simple-particles.rs +++ b/examples/src/bin/simple-particles.rs @@ -31,13 +31,17 @@ use vulkano::{ memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ graphics::{ + color_blend::ColorBlendState, input_assembly::{InputAssemblyState, PrimitiveTopology}, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, GraphicsPipeline, PipelineBindPoint, }, render_pass::{Framebuffer, FramebufferCreateInfo, Subpass}, + shader::PipelineShaderStageCreateInfo, swapchain::{ acquire_next_image, PresentMode, Swapchain, SwapchainCreateInfo, SwapchainPresentInfo, }, @@ -317,10 +321,6 @@ fn main() { } } - let cs = cs::load(device.clone()).unwrap(); - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let memory_allocator = StandardMemoryAllocator::new_default(device.clone()); let descriptor_set_allocator = StandardDescriptorSetAllocator::new(device.clone()); let command_buffer_allocator = @@ -409,10 +409,13 @@ fn main() { }; // Create a compute-pipeline for applying the compute shader to vertices. + let cs = cs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); let compute_pipeline = vulkano::pipeline::ComputePipeline::new( device.clone(), - cs.entry_point("main").unwrap(), - &(), + PipelineShaderStageCreateInfo::entry_point(cs), None, |_| {}, ) @@ -444,14 +447,28 @@ fn main() { }; // Create a basic graphics pipeline for rendering particles. + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let subpass = Subpass::from(render_pass, 0).unwrap(); let graphics_pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(Vertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) // Vertices will be rendered as a list of points. .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::PointList)) .viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([viewport])) - .fragment_shader(fs.entry_point("main").unwrap(), ()) - .render_pass(Subpass::from(render_pass, 0).unwrap()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments())) + .render_pass(subpass) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/specialization-constants.rs b/examples/src/bin/specialization-constants.rs index 23631991..c99aad4f 100644 --- a/examples/src/bin/specialization-constants.rs +++ b/examples/src/bin/specialization-constants.rs @@ -24,6 +24,7 @@ use vulkano::{ instance::{Instance, InstanceCreateInfo}, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, + shader::PipelineShaderStageCreateInfo, sync::{self, GpuFuture}, VulkanLibrary, }; @@ -110,17 +111,18 @@ fn main() { } } - let shader = cs::load(device.clone()).unwrap(); - - let spec_consts = cs::SpecializationConstants { - enable: 1, - multiple: 1, - addend: 1.0, - }; + let shader = cs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); let pipeline = ComputePipeline::new( device.clone(), - shader.entry_point("main").unwrap(), - &spec_consts, + PipelineShaderStageCreateInfo { + specialization_info: [(0, 1i32.into()), (1, 1.0f32.into()), (2, true.into())] + .into_iter() + .collect(), + ..PipelineShaderStageCreateInfo::entry_point(shader) + }, None, |_| {}, ) diff --git a/examples/src/bin/teapot/main.rs b/examples/src/bin/teapot/main.rs index fa2fb777..fe4b3c6a 100644 --- a/examples/src/bin/teapot/main.rs +++ b/examples/src/bin/teapot/main.rs @@ -32,15 +32,18 @@ use vulkano::{ memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ graphics::{ + color_blend::ColorBlendState, depth_stencil::DepthStencilState, input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, GraphicsPipeline, Pipeline, PipelineBindPoint, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, - shader::ShaderModule, + shader::{EntryPoint, PipelineShaderStageCreateInfo}, swapchain::{ acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo, @@ -208,9 +211,6 @@ fn main() { }, ); - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let render_pass = vulkano::single_pass_renderpass!( device.clone(), attachments: { @@ -234,8 +234,22 @@ fn main() { ) .unwrap(); - let (mut pipeline, mut framebuffers) = - window_size_dependent_setup(&memory_allocator, &vs, &fs, &images, render_pass.clone()); + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + + let (mut pipeline, mut framebuffers) = window_size_dependent_setup( + &memory_allocator, + vs.clone(), + fs.clone(), + &images, + render_pass.clone(), + ); let mut recreate_swapchain = false; let mut previous_frame_end = Some(sync::now(device.clone()).boxed()); @@ -282,8 +296,8 @@ fn main() { swapchain = new_swapchain; let (new_pipeline, new_framebuffers) = window_size_dependent_setup( &memory_allocator, - &vs, - &fs, + vs.clone(), + fs.clone(), &new_images, render_pass.clone(), ); @@ -418,8 +432,8 @@ fn main() { /// This function is called once during initialization, then again whenever the window is resized. fn window_size_dependent_setup( memory_allocator: &StandardMemoryAllocator, - vs: &ShaderModule, - fs: &ShaderModule, + vs: EntryPoint, + fs: EntryPoint, images: &[Arc], render_pass: Arc, ) -> (Arc, Vec>) { @@ -449,10 +463,14 @@ fn window_size_dependent_setup( // teapot example, we recreate the pipelines with a hardcoded viewport instead. This allows the // driver to optimize things, at the cost of slower window resizes. // https://computergraphics.stackexchange.com/questions/5742/vulkan-best-way-of-updating-pipeline-viewport + let subpass = Subpass::from(render_pass, 0).unwrap(); let pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state([Position::per_vertex(), Normal::per_vertex()]) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - .input_assembly_state(InputAssemblyState::new()) + .input_assembly_state(InputAssemblyState::default()) .viewport_state(ViewportState::viewport_fixed_scissor_irrelevant([ Viewport { origin: [0.0, 0.0], @@ -460,9 +478,11 @@ fn window_size_dependent_setup( depth_range: 0.0..1.0, }, ])) - .fragment_shader(fs.entry_point("main").unwrap(), ()) + .rasterization_state(RasterizationState::default()) .depth_stencil_state(DepthStencilState::simple_depth_test()) - .render_pass(Subpass::from(render_pass, 0).unwrap()) + .multisample_state(MultisampleState::default()) + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments())) + .render_pass(subpass) .build(memory_allocator.device().clone()) .unwrap(); diff --git a/examples/src/bin/tessellation.rs b/examples/src/bin/tessellation.rs index 8f1ea2c0..500b11e7 100644 --- a/examples/src/bin/tessellation.rs +++ b/examples/src/bin/tessellation.rs @@ -37,7 +37,9 @@ use vulkano::{ memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ graphics::{ + color_blend::ColorBlendState, input_assembly::{InputAssemblyState, PrimitiveTopology}, + multisample::MultisampleState, rasterization::{PolygonMode, RasterizationState}, tessellation::TessellationState, vertex_input::Vertex, @@ -46,6 +48,7 @@ use vulkano::{ GraphicsPipeline, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, + shader::PipelineShaderStageCreateInfo, swapchain::{ acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo, @@ -315,11 +318,6 @@ fn main() { ) .unwrap(); - let vs = vs::load(device.clone()).unwrap(); - let tcs = tcs::load(device.clone()).unwrap(); - let tes = tes::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let render_pass = vulkano::single_pass_renderpass!( device.clone(), attachments: { @@ -337,18 +335,32 @@ fn main() { ) .unwrap(); + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let tcs = tcs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let tes = tes::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(tcs), + PipelineShaderStageCreateInfo::entry_point(tes), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(Vertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) - // Actually use the tessellation shaders. - .tessellation_shaders( - tcs.entry_point("main").unwrap(), - (), - tes.entry_point("main").unwrap(), - (), - ) .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::PatchList)) - .rasterization_state(RasterizationState::new().polygon_mode(PolygonMode::Line)) .tessellation_state( TessellationState::new() // Use a patch_control_points of 3, because we want to convert one triangle into @@ -357,8 +369,10 @@ fn main() { .patch_control_points(3), ) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) - .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) + .rasterization_state(RasterizationState::new().polygon_mode(PolygonMode::Line)) + .multisample_state(MultisampleState::default()) + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments())) + .render_pass(subpass) .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/texture_array/main.rs b/examples/src/bin/texture_array/main.rs index b9532c71..8213e62f 100644 --- a/examples/src/bin/texture_array/main.rs +++ b/examples/src/bin/texture_array/main.rs @@ -32,6 +32,8 @@ use vulkano::{ graphics::{ color_blend::ColorBlendState, input_assembly::{InputAssemblyState, PrimitiveTopology}, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, @@ -39,6 +41,7 @@ use vulkano::{ }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, sampler::{Sampler, SamplerCreateInfo}, + shader::PipelineShaderStageCreateInfo, swapchain::{ acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo, @@ -194,9 +197,6 @@ fn main() { ) .unwrap(); - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - let render_pass = vulkano::single_pass_renderpass!( device.clone(), attachments: { @@ -265,13 +265,25 @@ fn main() { let sampler = Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear()).unwrap(); + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); let pipeline = GraphicsPipeline::start() + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) .vertex_input_state(Vertex::per_vertex()) - .vertex_shader(vs.entry_point("main").unwrap(), ()) .input_assembly_state(InputAssemblyState::new().topology(PrimitiveTopology::TriangleStrip)) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - .fragment_shader(fs.entry_point("main").unwrap(), ()) + .rasterization_state(RasterizationState::default()) + .multisample_state(MultisampleState::default()) .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend_alpha()) .render_pass(subpass) .build(device.clone()) diff --git a/examples/src/bin/triangle-v1_3.rs b/examples/src/bin/triangle-v1_3.rs index 8de8a032..8f326bd1 100644 --- a/examples/src/bin/triangle-v1_3.rs +++ b/examples/src/bin/triangle-v1_3.rs @@ -37,7 +37,10 @@ use vulkano::{ memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ graphics::{ + color_blend::ColorBlendState, input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, render_pass::PipelineRenderingCreateInfo, vertex_input::Vertex, viewport::{Viewport, ViewportState}, @@ -45,6 +48,7 @@ use vulkano::{ GraphicsPipeline, }, render_pass::{LoadOp, StoreOp}, + shader::PipelineShaderStageCreateInfo, swapchain::{ acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo, @@ -369,37 +373,60 @@ fn main() { } } - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - // At this point, OpenGL initialization would be finished. However in Vulkan it is not. OpenGL // implicitly does a lot of computation whenever you draw. In Vulkan, you have to do all this // manually. + // A Vulkan shader can in theory contain multiple entry points, so we have to specify which + // one. + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + + // We describe the formats of attachment images where the colors, depth and/or stencil + // information will be written. The pipeline will only be usable with this particular + // configuration of the attachment images. + let subpass = PipelineRenderingCreateInfo { + // We specify a single color attachment that will be rendered to. When we begin + // rendering, we will specify a swapchain image to be used as this attachment, so here + // we set its format to be the same format as the swapchain. + color_attachment_formats: vec![Some(swapchain.image_format())], + ..Default::default() + }; + // Before we draw we have to create what is called a pipeline. This is similar to an OpenGL // program, but much more specific. let pipeline = GraphicsPipeline::start() - // We describe the formats of attachment images where the colors, depth and/or stencil - // information will be written. The pipeline will only be usable with this particular - // configuration of the attachment images. - .render_pass(PipelineRenderingCreateInfo { - // We specify a single color attachment that will be rendered to. When we begin - // rendering, we will specify a swapchain image to be used as this attachment, so here - // we set its format to be the same format as the swapchain. - color_attachment_formats: vec![Some(swapchain.image_format())], - ..Default::default() - }) - // We need to indicate the layout of the vertices. + // Specify the shader stages that the pipeline will have. + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) + // How vertex data is read from the vertex buffers into the vertex shader. .vertex_input_state(Vertex::per_vertex()) - // The content of the vertex buffer describes a list of triangles. - .input_assembly_state(InputAssemblyState::new()) - // A Vulkan shader can in theory contain multiple entry points, so we have to specify which - // one. - .vertex_shader(vs.entry_point("main").unwrap(), ()) - // Use a resizable viewport set to draw over the entire window + // How vertices are arranged into primitive shapes. + // The default primitive shape is a triangle. + .input_assembly_state(InputAssemblyState::default()) + // How primitives are transformed and clipped to fit the framebuffer. + // We use a resizable viewport, set to draw over the entire window. .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - // See `vertex_shader`. - .fragment_shader(fs.entry_point("main").unwrap(), ()) + // How polygons are culled and converted into a raster of pixels. + // The default value does not perform any culling. + .rasterization_state(RasterizationState::default()) + // How multiple fragment shader samples are converted to a single pixel value. + // The default value does not perform any multisampling. + .multisample_state(MultisampleState::default()) + // How pixel values are combined with the values already present in the framebuffer. + // The default value overwrites the old value with the new one, without any blending. + .color_blend_state(ColorBlendState::new( + subpass.color_attachment_formats.len() as u32 + )) + .render_pass(subpass) // Now that our builder is filled, we call `build()` to obtain an actual pipeline. .build(device.clone()) .unwrap(); diff --git a/examples/src/bin/triangle.rs b/examples/src/bin/triangle.rs index 6567fc24..1ae5b0b4 100644 --- a/examples/src/bin/triangle.rs +++ b/examples/src/bin/triangle.rs @@ -32,13 +32,17 @@ use vulkano::{ memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ graphics::{ + color_blend::ColorBlendState, input_assembly::InputAssemblyState, + multisample::MultisampleState, + rasterization::RasterizationState, vertex_input::Vertex, viewport::{Viewport, ViewportState}, }, GraphicsPipeline, }, render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}, + shader::PipelineShaderStageCreateInfo, swapchain::{ acquire_next_image, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, SwapchainPresentInfo, @@ -338,9 +342,6 @@ fn main() { } } - let vs = vs::load(device.clone()).unwrap(); - let fs = fs::load(device.clone()).unwrap(); - // At this point, OpenGL initialization would be finished. However in Vulkan it is not. OpenGL // implicitly does a lot of computation whenever you draw. In Vulkan, you have to do all this // manually. @@ -380,23 +381,47 @@ fn main() { ) .unwrap(); + // A Vulkan shader can in theory contain multiple entry points, so we have to specify which + // one. + let vs = vs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + let fs = fs::load(device.clone()) + .unwrap() + .entry_point("main") + .unwrap(); + + // We have to indicate which subpass of which render pass this pipeline is going to be used + // in. The pipeline will only be usable from this particular subpass. + let subpass = Subpass::from(render_pass.clone(), 0).unwrap(); + // Before we draw we have to create what is called a pipeline. This is similar to an OpenGL // program, but much more specific. let pipeline = GraphicsPipeline::start() - // We have to indicate which subpass of which render pass this pipeline is going to be used - // in. The pipeline will only be usable from this particular subpass. - .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) - // We need to indicate the layout of the vertices. + // Specify the shader stages that the pipeline will have. + .stages([ + PipelineShaderStageCreateInfo::entry_point(vs), + PipelineShaderStageCreateInfo::entry_point(fs), + ]) + // How vertex data is read from the vertex buffers into the vertex shader. .vertex_input_state(Vertex::per_vertex()) - // The content of the vertex buffer describes a list of triangles. - .input_assembly_state(InputAssemblyState::new()) - // A Vulkan shader can in theory contain multiple entry points, so we have to specify - // which one. - .vertex_shader(vs.entry_point("main").unwrap(), ()) - // Use a resizable viewport set to draw over the entire window + // How vertices are arranged into primitive shapes. + // The default primitive shape is a triangle. + .input_assembly_state(InputAssemblyState::default()) + // How primitives are transformed and clipped to fit the framebuffer. + // We use a resizable viewport, set to draw over the entire window. .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) - // See `vertex_shader`. - .fragment_shader(fs.entry_point("main").unwrap(), ()) + // How polygons are culled and converted into a raster of pixels. + // The default value does not perform any culling. + .rasterization_state(RasterizationState::default()) + // How multiple fragment shader samples are converted to a single pixel value. + // The default value does not perform any multisampling. + .multisample_state(MultisampleState::default()) + // How pixel values are combined with the values already present in the framebuffer. + // The default value overwrites the old value with the new one, without any blending. + .color_blend_state(ColorBlendState::new(subpass.num_color_attachments())) + .render_pass(subpass) // Now that our builder is filled, we call `build()` to obtain an actual pipeline. .build(device.clone()) .unwrap(); diff --git a/vulkano-shaders/src/codegen.rs b/vulkano-shaders/src/codegen.rs index c9482b7f..0b5cd120 100644 --- a/vulkano-shaders/src/codegen.rs +++ b/vulkano-shaders/src/codegen.rs @@ -258,11 +258,8 @@ pub(super) fn reflect( quote! { &::vulkano::shader::spirv::Capability::#name } }); let spirv_extensions = reflect::spirv_extensions(&shader.spirv); - let entry_points = reflect::entry_points(&shader.spirv) - .map(|(name, model, info)| entry_point::write_entry_point(&name, model, &info)); - - let specialization_constants = - structs::write_specialization_constants(input, &shader, type_registry)?; + let entry_points = + reflect::entry_points(&shader.spirv).map(|info| entry_point::write_entry_point(&info)); let load_name = if shader.name.is_empty() { format_ident!("load") @@ -278,7 +275,7 @@ pub(super) fn reflect( device: ::std::sync::Arc<::vulkano::device::Device>, ) -> ::std::result::Result< ::std::sync::Arc<::vulkano::shader::ShaderModule>, - ::vulkano::shader::ShaderCreationError, + ::vulkano::shader::ShaderModuleCreationError, > { let _bytes = ( #( #include_bytes ),* ); @@ -295,8 +292,6 @@ pub(super) fn reflect( ) } } - - #specialization_constants }; let structs = structs::write_structs(input, &shader, type_registry)?; @@ -589,7 +584,7 @@ mod tests { let spirv = Spirv::new(&instructions).unwrap(); let mut descriptors = Vec::new(); - for (_, _, info) in reflect::entry_points(&spirv) { + for info in reflect::entry_points(&spirv) { descriptors.push(info.descriptor_binding_requirements); } @@ -657,7 +652,7 @@ mod tests { .unwrap(); let spirv = Spirv::new(comp.as_binary()).unwrap(); - if let Some((_, _, info)) = reflect::entry_points(&spirv).next() { + if let Some(info) = reflect::entry_points(&spirv).next() { let mut bindings = Vec::new(); for (loc, _reqs) in info.descriptor_binding_requirements { bindings.push(loc); diff --git a/vulkano-shaders/src/entry_point.rs b/vulkano-shaders/src/entry_point.rs index 76866ebd..41b3b12a 100644 --- a/vulkano-shaders/src/entry_point.rs +++ b/vulkano-shaders/src/entry_point.rs @@ -8,46 +8,37 @@ // according to those terms. use ahash::HashMap; -use proc_macro2::{Ident, Span, TokenStream}; +use proc_macro2::TokenStream; use vulkano::{ pipeline::layout::PushConstantRange, shader::{ - spirv::ExecutionModel, DescriptorBindingRequirements, DescriptorIdentifier, - DescriptorRequirements, EntryPointInfo, ShaderExecution, ShaderInterface, - ShaderInterfaceEntry, ShaderInterfaceEntryType, ShaderStages, - SpecializationConstantRequirements, + DescriptorBindingRequirements, DescriptorIdentifier, DescriptorRequirements, + EntryPointInfo, ShaderExecution, ShaderInterface, ShaderInterfaceEntry, + ShaderInterfaceEntryType, ShaderStages, SpecializationConstant, }, }; -pub(super) fn write_entry_point( - name: &str, - model: ExecutionModel, - info: &EntryPointInfo, -) -> TokenStream { +pub(super) fn write_entry_point(info: &EntryPointInfo) -> TokenStream { + let name = &info.name; let execution = write_shader_execution(&info.execution); - let model = Ident::new(&format!("{:?}", model), Span::call_site()); let descriptor_binding_requirements = write_descriptor_binding_requirements(&info.descriptor_binding_requirements); let push_constant_requirements = write_push_constant_requirements(&info.push_constant_requirements); - let specialization_constant_requirements = - write_specialization_constant_requirements(&info.specialization_constant_requirements); + let specialization_constants = write_specialization_constants(&info.specialization_constants); let input_interface = write_interface(&info.input_interface); let output_interface = write_interface(&info.output_interface); quote! { - ( - #name.to_owned(), - ::vulkano::shader::spirv::ExecutionModel::#model, - ::vulkano::shader::EntryPointInfo { - execution: #execution, - descriptor_binding_requirements: #descriptor_binding_requirements.into_iter().collect(), - push_constant_requirements: #push_constant_requirements, - specialization_constant_requirements: #specialization_constant_requirements.into_iter().collect(), - input_interface: #input_interface, - output_interface: #output_interface, - }, - ) + ::vulkano::shader::EntryPointInfo { + name: #name.to_owned(), + execution: #execution, + descriptor_binding_requirements: #descriptor_binding_requirements.into_iter().collect(), + push_constant_requirements: #push_constant_requirements, + specialization_constants: #specialization_constants.into_iter().collect(), + input_interface: #input_interface, + output_interface: #output_interface, + }, } } @@ -248,27 +239,47 @@ fn write_push_constant_requirements( } } -fn write_specialization_constant_requirements( - specialization_constant_requirements: &HashMap, +fn write_specialization_constants( + specialization_constants: &HashMap, ) -> TokenStream { - let specialization_constant_requirements = - specialization_constant_requirements - .iter() - .map(|(&constant_id, reqs)| { - let SpecializationConstantRequirements { size } = reqs; - quote! { - ( - #constant_id, - ::vulkano::shader::SpecializationConstantRequirements { - size: #size, - }, - ) + let specialization_constants = specialization_constants + .iter() + .map(|(&constant_id, value)| { + let value = match value { + SpecializationConstant::Bool(value) => quote! { Bool(#value) }, + SpecializationConstant::I8(value) => quote! { I8(#value) }, + SpecializationConstant::I16(value) => quote! { I16(#value) }, + SpecializationConstant::I32(value) => quote! { I32(#value) }, + SpecializationConstant::I64(value) => quote! { I64(#value) }, + SpecializationConstant::U8(value) => quote! { U8(#value) }, + SpecializationConstant::U16(value) => quote! { U16(#value) }, + SpecializationConstant::U32(value) => quote! { U32(#value) }, + SpecializationConstant::U64(value) => quote! { U64(#value) }, + SpecializationConstant::F16(value) => { + let bits = value.to_bits(); + quote! { F16(f16::from_bits(#bits)) } } - }); + SpecializationConstant::F32(value) => { + let bits = value.to_bits(); + quote! { F32(f32::from_bits(#bits)) } + } + SpecializationConstant::F64(value) => { + let bits = value.to_bits(); + quote! { F64(f64::from_bits(#bits)) } + } + }; + + quote! { + ( + #constant_id, + ::vulkano::shader::SpecializationConstant::#value, + ) + } + }); quote! { [ - #( #specialization_constant_requirements ),* + #( #specialization_constants ),* ] } } diff --git a/vulkano-shaders/src/lib.rs b/vulkano-shaders/src/lib.rs index 47a32cfd..a1de9906 100644 --- a/vulkano-shaders/src/lib.rs +++ b/vulkano-shaders/src/lib.rs @@ -33,18 +33,15 @@ //! //! - The `load` constructor. This function takes an `Arc`, calls //! [`ShaderModule::from_words_with_data`] with the passed-in device and the shader data provided -//! via the macro, and returns `Result, ShaderCreationError>`. Before doing so, -//! it loops through every capability instruction in the shader data, verifying that the -//! passed-in `Device` has the appropriate features enabled. +//! via the macro, and returns `Result, ShaderModuleCreationError>`. +//! Before doing so, it loops through every capability instruction in the shader data, +//! verifying that the passed-in `Device` has the appropriate features enabled. //! - If the `shaders` option is used, then instead of one `load` constructor, there is one for //! each shader. They are named based on the provided names, `load_first`, `load_second` etc. //! - A Rust struct translated from each struct contained in the shader data. By default each //! structure has a `Clone` and a `Copy` implementation. This behavior could be customized //! through the `custom_derives` macro option (see below for details). Each struct also has an //! implementation of [`BufferContents`], so that it can be read from/written to a buffer. -//! - The `SpecializationConstants` struct. This contains a field for every specialization constant -//! found in the shader data. Implementations of [`Default`] and [`SpecializationConstants`] are -//! also generated for the struct. //! //! All of these generated items will be accessed through the module where the macro was invoked. //! If you wanted to store the `ShaderModule` in a struct of your own, you could do something like @@ -53,7 +50,7 @@ //! ``` //! # fn main() {} //! # use std::sync::Arc; -//! # use vulkano::shader::{ShaderCreationError, ShaderModule}; +//! # use vulkano::shader::{ShaderModuleCreationError, ShaderModule}; //! # use vulkano::device::Device; //! # //! # mod vs { @@ -78,7 +75,7 @@ //! } //! //! impl Shaders { -//! pub fn load(device: Arc) -> Result { +//! pub fn load(device: Arc) -> Result { //! Ok(Self { //! vs: vs::load(device)?, //! }) @@ -143,18 +140,13 @@ //! ## `shaders: { first: { src: "...", ty: "..." }, ... }` //! //! With these options the user can compile several shaders in a single macro invocation. Each -//! entry key will be the suffix of the generated `load` function (`load_first` in this case) and -//! the prefix of the `SpecializationConstants` struct (`FirstSpecializationConstants` in this -//! case). However all other Rust structs translated from the shader source will be shared between +//! entry key will be the suffix of the generated `load` function (`load_first` in this case). +//! However all other Rust structs translated from the shader source will be shared between //! shaders. The macro checks that the source structs with the same names between different shaders //! have the same declaration signature, and throws a compile-time error if they don't. //! //! Each entry expects a `src`, `path`, `bytes`, and `ty` pairs same as above. //! -//! Also, `SpecializationConstants` can be shared between all shaders by specifying the -//! `shared_constants: true,` entry-flag in the `shaders` map. This feature is turned off by -//! default. -//! //! ## `include: ["...", "...", ...]` //! //! Specifies the standard include directories to be searched through when using the @@ -217,7 +209,6 @@ //! [`cargo-env-vars`]: https://doc.rust-lang.org/cargo/reference/environment-variables.html //! [cargo-expand]: https://github.com/dtolnay/cargo-expand //! [`ShaderModule::from_words_with_data`]: vulkano::shader::ShaderModule::from_words_with_data -//! [`SpecializationConstants`]: vulkano::shader::SpecializationConstants //! [pipeline]: vulkano::pipeline //! [`set_target_env`]: shaderc::CompileOptions::set_target_env //! [`set_target_spirv`]: shaderc::CompileOptions::set_target_spirv @@ -390,7 +381,6 @@ struct MacroInput { root_path_env: Option, include_directories: Vec, macro_defines: Vec<(String, String)>, - shared_constants: bool, shaders: HashMap, spirv_version: Option, vulkan_version: Option, @@ -406,7 +396,6 @@ impl MacroInput { root_path_env: None, include_directories: Vec::new(), macro_defines: Vec::new(), - shared_constants: false, shaders: HashMap::default(), vulkan_version: None, spirv_version: None, @@ -424,7 +413,6 @@ impl Parse for MacroInput { let mut root_path_env = None; let mut include_directories = Vec::new(); let mut macro_defines = Vec::new(); - let mut shared_constants = None; let mut shaders = HashMap::default(); let mut vulkan_version = None; let mut spirv_version = None; @@ -535,22 +523,6 @@ impl Parse for MacroInput { let name_ident = in_braces.parse::()?; let name = name_ident.to_string(); - if &name == "shared_constants" { - in_braces.parse::()?; - - let lit = in_braces.parse::()?; - if shared_constants.is_some() { - bail!(lit, "field `shared_constants` is already defined"); - } - shared_constants = Some(lit.value); - - if !in_braces.is_empty() { - in_braces.parse::()?; - } - - continue; - } - if shaders.contains_key(&name) { bail!(name_ident, "shader entry `{name}` is already defined"); } @@ -759,7 +731,6 @@ impl Parse for MacroInput { root_path_env, include_directories, macro_defines, - shared_constants: shared_constants.unwrap_or(false), shaders: shaders .into_iter() .map(|(key, (shader_kind, shader_source))| { diff --git a/vulkano-shaders/src/structs.rs b/vulkano-shaders/src/structs.rs index e4cb053c..9d6dd519 100644 --- a/vulkano-shaders/src/structs.rs +++ b/vulkano-shaders/src/structs.rs @@ -9,7 +9,6 @@ use crate::{bail, codegen::Shader, LinAlgType, MacroInput}; use ahash::HashMap; -use heck::ToUpperCamelCase; use proc_macro2::{Span, TokenStream}; use quote::{ToTokens, TokenStreamExt}; use std::{cmp::Ordering, num::NonZeroUsize}; @@ -173,159 +172,6 @@ fn has_defined_layout(shader: &Shader, struct_id: Id) -> bool { true } -/// Writes the `SpecializationConstants` struct that contains the specialization constants and -/// implements the `Default` and the `vulkano::shader::SpecializationConstants` traits. -pub(super) fn write_specialization_constants( - input: &MacroInput, - shader: &Shader, - type_registry: &mut TypeRegistry, -) -> Result { - let mut members = Vec::new(); - let mut member_defaults = Vec::new(); - let mut map_entries = Vec::new(); - let mut current_offset = 0; - - for instruction in shader.spirv.iter_global() { - let (result_type_id, result_id, default_value) = match *instruction { - Instruction::SpecConstantTrue { - result_type_id, - result_id, - } => (result_type_id, result_id, quote! { 1u32 }), - Instruction::SpecConstantFalse { - result_type_id, - result_id, - } => (result_type_id, result_id, quote! { 0u32 }), - Instruction::SpecConstant { - result_type_id, - result_id, - ref value, - } => { - let def_val = quote! { - unsafe { ::std::mem::transmute([ #( #value ),* ]) } - }; - - (result_type_id, result_id, def_val) - } - Instruction::SpecConstantComposite { - result_type_id, - result_id, - ref constituents, - } => { - let constituents = constituents.iter().map(|&id| u32::from(id)); - let def_val = quote! { - unsafe { ::std::mem::transmute([ #( #constituents ),* ]) } - }; - - (result_type_id, result_id, def_val) - } - _ => continue, - }; - - let id_info = shader.spirv.id(result_id); - - let constant_id = id_info - .iter_decoration() - .find_map(|instruction| match *instruction { - Instruction::Decorate { - decoration: - Decoration::SpecId { - specialization_constant_id, - }, - .. - } => Some(specialization_constant_id), - _ => None, - }); - - if let Some(constant_id) = constant_id { - let ident = id_info - .iter_name() - .find_map(|instruction| match instruction { - Instruction::Name { name, .. } => { - Some(Ident::new(name.as_str(), Span::call_site())) - } - _ => None, - }) - .unwrap_or_else(|| format_ident!("constant_{}", constant_id)); - - let ty = match *shader.spirv.id(result_type_id).instruction() { - // Translate bool to u32. - Instruction::TypeBool { .. } => Type::Scalar(TypeScalar::Int(TypeInt { - width: IntWidth::W32, - signed: false, - })), - _ => Type::new(shader, result_type_id)?, - }; - - let offset = current_offset; - let size = ty.size().ok_or_else(|| { - Error::new_spanned( - &shader.source, - "found runtime-sized specialization constant", - ) - })?; - current_offset = align_up(current_offset + size, ty.scalar_alignment()); - - member_defaults.push(quote! { #ident: #default_value }); - members.push(Member { ident, ty, offset }); - map_entries.push(quote! { - ::vulkano::shader::SpecializationMapEntry { - constant_id: #constant_id, - offset: #offset as u32, - size: #size, - } - }); - } - } - - let struct_ty = TypeStruct { - ident: if input.shared_constants { - format_ident!("SpecializationConstants") - } else { - format_ident!( - "{}SpecializationConstants", - shader.name.to_upper_camel_case(), - ) - }, - members, - }; - - // For multi-constants mode, the registration mechanism is skipped. - if input.shared_constants && !type_registry.register_struct(shader, &struct_ty)? { - return Ok(TokenStream::new()); - } - - let struct_ser = Serializer(&struct_ty, input); - let struct_ident = &struct_ty.ident; - let num_map_entries = map_entries.len(); - - Ok(quote! { - #[allow(non_snake_case)] - #[derive(::std::clone::Clone, ::std::marker::Copy, ::std::fmt::Debug)] - #[repr(C)] - #struct_ser - - impl ::std::default::Default for #struct_ident { - #[inline] - fn default() -> #struct_ident { - #struct_ident { - #( #member_defaults ),* - } - } - } - - #[allow(unsafe_code)] - unsafe impl ::vulkano::shader::SpecializationConstants for #struct_ident { - #[inline(always)] - fn descriptors() -> &'static [::vulkano::shader::SpecializationMapEntry] { - static DESCRIPTORS: [::vulkano::shader::SpecializationMapEntry; #num_map_entries] = - [ #( #map_entries ),* ]; - - &DESCRIPTORS - } - } - }) -} - #[derive(Clone, Copy, PartialEq, Eq)] #[repr(u8)] enum Alignment { diff --git a/vulkano/src/pipeline/cache.rs b/vulkano/src/pipeline/cache.rs index a33321a1..308a9f42 100644 --- a/vulkano/src/pipeline/cache.rs +++ b/vulkano/src/pipeline/cache.rs @@ -21,7 +21,10 @@ //! of [`get_data`](crate::pipeline::cache::PipelineCache::get_data) for example of how to store the data //! on the disk, and [`with_data`](crate::pipeline::cache::PipelineCache::with_data) for how to reload it. -use crate::{device::Device, OomError, VulkanError, VulkanObject}; +use crate::{ + device::{Device, DeviceOwned}, + OomError, VulkanError, VulkanObject, +}; use std::{mem::MaybeUninit, ptr, sync::Arc}; /// Opaque cache that contains pipeline objects. @@ -30,7 +33,7 @@ use std::{mem::MaybeUninit, ptr, sync::Arc}; #[derive(Debug)] pub struct PipelineCache { device: Arc, - cache: ash::vk::PipelineCache, + handle: ash::vk::PipelineCache, } impl PipelineCache { @@ -131,7 +134,7 @@ impl PipelineCache { Ok(Arc::new(PipelineCache { device: device.clone(), - cache, + handle: cache, })) } @@ -156,13 +159,13 @@ impl PipelineCache { .into_iter() .map(|pipeline| { assert!(&***pipeline as *const _ != self as *const _); - pipeline.cache + pipeline.handle }) .collect::>(); (fns.v1_0.merge_pipeline_caches)( self.device.handle(), - self.cache, + self.handle, pipelines.len() as u32, pipelines.as_ptr(), ) @@ -210,7 +213,7 @@ impl PipelineCache { let mut count = 0; (fns.v1_0.get_pipeline_cache_data)( self.device.handle(), - self.cache, + self.handle, &mut count, ptr::null_mut(), ) @@ -220,7 +223,7 @@ impl PipelineCache { let mut data: Vec = Vec::with_capacity(count); let result = (fns.v1_0.get_pipeline_cache_data)( self.device.handle(), - self.cache, + self.handle, &mut count, data.as_mut_ptr() as *mut _, ); @@ -240,30 +243,37 @@ impl PipelineCache { } } -unsafe impl VulkanObject for PipelineCache { - type Handle = ash::vk::PipelineCache; - - #[inline] - fn handle(&self) -> Self::Handle { - self.cache - } -} - impl Drop for PipelineCache { #[inline] fn drop(&mut self) { unsafe { let fns = self.device.fns(); - (fns.v1_0.destroy_pipeline_cache)(self.device.handle(), self.cache, ptr::null()); + (fns.v1_0.destroy_pipeline_cache)(self.device.handle(), self.handle, ptr::null()); } } } +unsafe impl VulkanObject for PipelineCache { + type Handle = ash::vk::PipelineCache; + + #[inline] + fn handle(&self) -> Self::Handle { + self.handle + } +} + +unsafe impl DeviceOwned for PipelineCache { + #[inline] + fn device(&self) -> &Arc { + &self.device + } +} + #[cfg(test)] mod tests { use crate::{ pipeline::{cache::PipelineCache, ComputePipeline}, - shader::ShaderModule, + shader::{PipelineShaderStageCreateInfo, ShaderModule}, }; #[test] @@ -281,7 +291,7 @@ mod tests { let cache = PipelineCache::empty(device.clone()).unwrap(); - let module = unsafe { + let shader = unsafe { /* * #version 450 * void main() { @@ -297,13 +307,13 @@ mod tests { 0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0, 2, 0, 5, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0, ]; - ShaderModule::from_bytes(device.clone(), &MODULE).unwrap() + let module = ShaderModule::from_bytes(device.clone(), &MODULE).unwrap(); + module.entry_point("main").unwrap() }; let _pipeline = ComputePipeline::new( device, - module.entry_point("main").unwrap(), - &(), + PipelineShaderStageCreateInfo::entry_point(shader), Some(cache.clone()), |_| {}, ) @@ -321,7 +331,7 @@ mod tests { let cache = PipelineCache::empty(device.clone()).unwrap(); - let first_module = unsafe { + let first_shader = unsafe { /* * #version 450 * void main() { @@ -337,10 +347,11 @@ mod tests { 0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0, 2, 0, 5, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0, ]; - ShaderModule::from_bytes(device.clone(), &MODULE).unwrap() + let module = ShaderModule::from_bytes(device.clone(), &MODULE).unwrap(); + module.entry_point("main").unwrap() }; - let second_module = unsafe { + let second_shader = unsafe { /* * #version 450 * @@ -348,7 +359,7 @@ mod tests { * uint idx = gl_GlobalInvocationID.x; * } */ - const SECOND_MODULE: [u8; 432] = [ + const MODULE: [u8; 432] = [ 3, 2, 35, 7, 0, 0, 1, 0, 10, 0, 8, 0, 16, 0, 0, 0, 0, 0, 0, 0, 17, 0, 2, 0, 1, 0, 0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76, 46, 115, 116, 100, 46, 52, 53, 48, 0, 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0, 6, 0, 5, 0, 0, 0, 4, 0, 0, @@ -368,13 +379,13 @@ mod tests { 0, 15, 0, 0, 0, 14, 0, 0, 0, 62, 0, 3, 0, 8, 0, 0, 0, 15, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0, ]; - ShaderModule::from_bytes(device.clone(), &SECOND_MODULE).unwrap() + let module = ShaderModule::from_bytes(device.clone(), &MODULE).unwrap(); + module.entry_point("main").unwrap() }; - let _pipeline = ComputePipeline::new( + let _first_pipeline = ComputePipeline::new( device.clone(), - first_module.entry_point("main").unwrap(), - &(), + PipelineShaderStageCreateInfo::entry_point(first_shader), Some(cache.clone()), |_| {}, ) @@ -384,8 +395,7 @@ mod tests { let _second_pipeline = ComputePipeline::new( device, - second_module.entry_point("main").unwrap(), - &(), + PipelineShaderStageCreateInfo::entry_point(second_shader), Some(cache.clone()), |_| {}, ) @@ -406,7 +416,7 @@ mod tests { let cache = PipelineCache::empty(device.clone()).unwrap(); - let module = unsafe { + let shader = unsafe { /* * #version 450 * void main() { @@ -422,13 +432,13 @@ mod tests { 0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0, 2, 0, 5, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0, ]; - ShaderModule::from_bytes(device.clone(), &MODULE).unwrap() + let module = ShaderModule::from_bytes(device.clone(), &MODULE).unwrap(); + module.entry_point("main").unwrap() }; let _pipeline = ComputePipeline::new( device.clone(), - module.entry_point("main").unwrap(), - &(), + PipelineShaderStageCreateInfo::entry_point(shader.clone()), Some(cache.clone()), |_| {}, ) @@ -438,8 +448,7 @@ mod tests { let _second_pipeline = ComputePipeline::new( device, - module.entry_point("main").unwrap(), - &(), + PipelineShaderStageCreateInfo::entry_point(shader), Some(cache.clone()), |_| {}, ) diff --git a/vulkano/src/pipeline/compute.rs b/vulkano/src/pipeline/compute.rs index eb1549ac..38baae32 100644 --- a/vulkano/src/pipeline/compute.rs +++ b/vulkano/src/pipeline/compute.rs @@ -34,14 +34,17 @@ use crate::{ layout::{PipelineLayout, PipelineLayoutCreationError, PipelineLayoutSupersetError}, Pipeline, PipelineBindPoint, }, - shader::{DescriptorBindingRequirements, EntryPoint, SpecializationConstants}, - DeviceSize, OomError, VulkanError, VulkanObject, + shader::{ + DescriptorBindingRequirements, PipelineShaderStageCreateInfo, ShaderExecution, ShaderStage, + SpecializationConstant, + }, + OomError, RequirementNotMet, RequiresOneOf, VulkanError, VulkanObject, }; use ahash::HashMap; use std::{ error::Error, + ffi::CString, fmt::{Debug, Display, Error as FmtError, Formatter}, - mem, mem::MaybeUninit, num::NonZeroU64, ptr, @@ -71,149 +74,192 @@ impl ComputePipeline { /// `func` is 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 new( + pub fn new( device: Arc, - shader: EntryPoint<'_>, - specialization_constants: &Css, + stage: PipelineShaderStageCreateInfo, cache: Option>, func: F, ) -> Result, ComputePipelineCreationError> where - Css: SpecializationConstants, F: FnOnce(&mut [DescriptorSetLayoutCreateInfo]), { - let mut set_layout_create_infos = DescriptorSetLayoutCreateInfo::from_requirements( - shader.descriptor_binding_requirements(), - ); - func(&mut set_layout_create_infos); - let set_layouts = set_layout_create_infos - .iter() - .map(|desc| DescriptorSetLayout::new(device.clone(), desc.clone())) - .collect::, _>>()?; + let layout = { + let entry_point_info = stage.entry_point.info(); + let mut set_layout_create_infos = DescriptorSetLayoutCreateInfo::from_requirements( + entry_point_info + .descriptor_binding_requirements + .iter() + .map(|(k, v)| (*k, v)), + ); + func(&mut set_layout_create_infos); + let set_layouts = set_layout_create_infos + .iter() + .map(|desc| DescriptorSetLayout::new(device.clone(), desc.clone())) + .collect::, _>>()?; - let layout = PipelineLayout::new( - device.clone(), - PipelineLayoutCreateInfo { - set_layouts, - push_constant_ranges: shader - .push_constant_requirements() - .cloned() - .into_iter() - .collect(), - ..Default::default() - }, - )?; + PipelineLayout::new( + device.clone(), + PipelineLayoutCreateInfo { + set_layouts, + push_constant_ranges: entry_point_info + .push_constant_requirements + .iter() + .cloned() + .collect(), + ..Default::default() + }, + )? + }; - unsafe { - ComputePipeline::with_unchecked_pipeline_layout( - device, - shader, - specialization_constants, - layout, - cache, - ) - } + Self::with_pipeline_layout(device, stage, layout, cache) } /// Builds a new `ComputePipeline` with a specific pipeline layout. /// /// An error will be returned if the pipeline layout isn't a superset of what the shader /// uses. - pub fn with_pipeline_layout( + pub fn with_pipeline_layout( device: Arc, - shader: EntryPoint<'_>, - specialization_constants: &Css, + stage: PipelineShaderStageCreateInfo, layout: Arc, cache: Option>, - ) -> Result, ComputePipelineCreationError> - where - Css: SpecializationConstants, - { - let spec_descriptors = Css::descriptors(); + ) -> Result, ComputePipelineCreationError> { + // VUID-vkCreateComputePipelines-pipelineCache-parent + if let Some(cache) = &cache { + assert_eq!(&device, cache.device()); + } - for (constant_id, reqs) in shader.specialization_constant_requirements() { - let map_entry = spec_descriptors - .iter() - .find(|desc| desc.constant_id == constant_id) - .ok_or(ComputePipelineCreationError::IncompatibleSpecializationConstants)?; + let &PipelineShaderStageCreateInfo { + flags, + ref entry_point, + ref specialization_info, + _ne: _, + } = &stage; - if map_entry.size as DeviceSize != reqs.size { - return Err(ComputePipelineCreationError::IncompatibleSpecializationConstants); + // VUID-VkPipelineShaderStageCreateInfo-flags-parameter + flags.validate_device(&device)?; + + let entry_point_info = entry_point.info(); + + // VUID-VkComputePipelineCreateInfo-stage-00701 + // VUID-VkPipelineShaderStageCreateInfo-stage-parameter + if !matches!(entry_point_info.execution, ShaderExecution::Compute) { + return Err(ComputePipelineCreationError::ShaderStageInvalid { + stage: ShaderStage::from(&entry_point_info.execution), + }); + } + + for (&constant_id, provided_value) in specialization_info { + // Per `VkSpecializationMapEntry` spec: + // "If a constantID value is not a specialization constant ID used in the shader, + // that map entry does not affect the behavior of the pipeline." + // We *may* want to be stricter than this for the sake of catching user errors? + if let Some(default_value) = entry_point_info.specialization_constants.get(&constant_id) + { + // VUID-VkSpecializationMapEntry-constantID-00776 + // Check for equal types rather than only equal size. + if !provided_value.eq_type(default_value) { + return Err( + ComputePipelineCreationError::ShaderSpecializationConstantTypeMismatch { + constant_id, + default_value: *default_value, + provided_value: *provided_value, + }, + ); + } } } + // VUID-VkComputePipelineCreateInfo-layout-07987 + // VUID-VkComputePipelineCreateInfo-layout-07988 + // VUID-VkComputePipelineCreateInfo-layout-07990 + // VUID-VkComputePipelineCreateInfo-layout-07991 + // TODO: Make sure that all of these are indeed checked. layout.ensure_compatible_with_shader( - shader.descriptor_binding_requirements(), - shader.push_constant_requirements(), + entry_point_info + .descriptor_binding_requirements + .iter() + .map(|(k, v)| (*k, v)), + entry_point_info.push_constant_requirements.as_ref(), )?; - unsafe { - ComputePipeline::with_unchecked_pipeline_layout( - device, - shader, - specialization_constants, - layout, - cache, - ) - } + // VUID-VkComputePipelineCreateInfo-stage-00702 + // VUID-VkComputePipelineCreateInfo-layout-01687 + // TODO: + + unsafe { Self::new_unchecked(device, stage, layout, cache) } } - /// Same as `with_pipeline_layout`, but doesn't check whether the pipeline layout is a - /// superset of what the shader expects. - pub unsafe fn with_unchecked_pipeline_layout( + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn new_unchecked( device: Arc, - shader: EntryPoint<'_>, - specialization_constants: &Css, + stage: PipelineShaderStageCreateInfo, layout: Arc, cache: Option>, - ) -> Result, ComputePipelineCreationError> - where - Css: SpecializationConstants, - { - let fns = device.fns(); + ) -> Result, ComputePipelineCreationError> { + let &PipelineShaderStageCreateInfo { + flags, + ref entry_point, + ref specialization_info, + _ne: _, + } = &stage; + + let entry_point_info = entry_point.info(); + let name_vk = CString::new(entry_point_info.name.as_str()).unwrap(); + + let mut specialization_data_vk: Vec = Vec::new(); + let specialization_map_entries_vk: Vec<_> = specialization_info + .iter() + .map(|(&constant_id, value)| { + let data = value.as_bytes(); + let offset = specialization_data_vk.len() as u32; + let size = data.len(); + specialization_data_vk.extend(data); + + ash::vk::SpecializationMapEntry { + constant_id, + offset, + size, + } + }) + .collect(); + + let specialization_info_vk = ash::vk::SpecializationInfo { + map_entry_count: specialization_map_entries_vk.len() as u32, + p_map_entries: specialization_map_entries_vk.as_ptr(), + data_size: specialization_data_vk.len(), + p_data: specialization_data_vk.as_ptr() as *const _, + }; + let stage_vk = ash::vk::PipelineShaderStageCreateInfo { + flags: flags.into(), + stage: ShaderStage::from(&entry_point_info.execution).into(), + module: entry_point.module().handle(), + p_name: name_vk.as_ptr(), + p_specialization_info: if specialization_info_vk.data_size == 0 { + ptr::null() + } else { + &specialization_info_vk + }, + ..Default::default() + }; + + let create_infos_vk = ash::vk::ComputePipelineCreateInfo { + flags: ash::vk::PipelineCreateFlags::empty(), + stage: stage_vk, + layout: layout.handle(), + base_pipeline_handle: ash::vk::Pipeline::null(), + base_pipeline_index: 0, + ..Default::default() + }; let handle = { - let spec_descriptors = Css::descriptors(); - let specialization = ash::vk::SpecializationInfo { - map_entry_count: spec_descriptors.len() as u32, - p_map_entries: spec_descriptors.as_ptr() as *const _, - data_size: mem::size_of_val(specialization_constants), - p_data: specialization_constants as *const Css as *const _, - }; - - let stage = ash::vk::PipelineShaderStageCreateInfo { - flags: ash::vk::PipelineShaderStageCreateFlags::empty(), - stage: ash::vk::ShaderStageFlags::COMPUTE, - module: shader.module().handle(), - p_name: shader.name().as_ptr(), - p_specialization_info: if specialization.data_size == 0 { - ptr::null() - } else { - &specialization - }, - ..Default::default() - }; - - let infos = ash::vk::ComputePipelineCreateInfo { - flags: ash::vk::PipelineCreateFlags::empty(), - stage, - layout: layout.handle(), - base_pipeline_handle: ash::vk::Pipeline::null(), - base_pipeline_index: 0, - ..Default::default() - }; - - let cache_handle = match cache { - Some(ref cache) => cache.handle(), - None => ash::vk::PipelineCache::null(), - }; - + let fns = device.fns(); let mut output = MaybeUninit::uninit(); (fns.v1_0.create_compute_pipelines)( device.handle(), - cache_handle, + cache.as_ref().map_or(Default::default(), |c| c.handle()), 1, - &infos, + &create_infos_vk, ptr::null(), output.as_mut_ptr(), ) @@ -222,9 +268,28 @@ impl ComputePipeline { output.assume_init() }; - let descriptor_binding_requirements: HashMap<_, _> = shader - .descriptor_binding_requirements() - .map(|(loc, reqs)| (loc, reqs.clone())) + Ok(Self::from_handle(device, handle, stage, layout)) + } + + /// Creates a new `ComputePipeline` from a raw object handle. + /// + /// # Safety + /// + /// - `handle` must be a valid Vulkan object handle created from `device`. + /// - `create_info` must match the info used to create the object. + #[inline] + pub unsafe fn from_handle( + device: Arc, + handle: ash::vk::Pipeline, + stage: PipelineShaderStageCreateInfo, + layout: Arc, + ) -> Arc { + let descriptor_binding_requirements: HashMap<_, _> = stage + .entry_point + .info() + .descriptor_binding_requirements + .iter() + .map(|(&loc, reqs)| (loc, reqs.clone())) .collect(); let num_used_descriptor_sets = descriptor_binding_requirements .keys() @@ -233,14 +298,14 @@ impl ComputePipeline { .map(|x| x + 1) .unwrap_or(0); - Ok(Arc::new(ComputePipeline { + Arc::new(ComputePipeline { handle, - device: device.clone(), + device, id: Self::next_id(), layout, descriptor_binding_requirements, num_used_descriptor_sets, - })) + }) } /// Returns the `Device` this compute pipeline was created with. @@ -309,18 +374,35 @@ impl Drop for ComputePipeline { } /// Error that can happen when creating a compute pipeline. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq)] pub enum ComputePipelineCreationError { /// Not enough memory. OomError(OomError), + + RequirementNotMet { + required_for: &'static str, + requires_one_of: RequiresOneOf, + }, + /// Error while creating a descriptor set layout object. DescriptorSetLayoutCreationError(DescriptorSetLayoutCreationError), + /// Error while creating the pipeline layout object. PipelineLayoutCreationError(PipelineLayoutCreationError), + /// The pipeline layout is not compatible with what the shader expects. IncompatiblePipelineLayout(PipelineLayoutSupersetError), - /// The provided specialization constants are not compatible with what the shader expects. - IncompatibleSpecializationConstants, + + /// The value provided for a shader specialization constant has a + /// different type than the constant's default value. + ShaderSpecializationConstantTypeMismatch { + constant_id: u32, + default_value: SpecializationConstant, + provided_value: SpecializationConstant, + }, + + /// The provided shader stage is not a compute shader. + ShaderStageInvalid { stage: ShaderStage }, } impl Error for ComputePipelineCreationError { @@ -330,33 +412,49 @@ impl Error for ComputePipelineCreationError { Self::DescriptorSetLayoutCreationError(err) => Some(err), Self::PipelineLayoutCreationError(err) => Some(err), Self::IncompatiblePipelineLayout(err) => Some(err), - Self::IncompatibleSpecializationConstants => None, + _ => None, } } } impl Display for ComputePipelineCreationError { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - write!( - f, - "{}", - match self { - ComputePipelineCreationError::OomError(_) => "not enough memory available", - ComputePipelineCreationError::DescriptorSetLayoutCreationError(_) => { - "error while creating a descriptor set layout object" - } - ComputePipelineCreationError::PipelineLayoutCreationError(_) => { - "error while creating the pipeline layout object" - } - ComputePipelineCreationError::IncompatiblePipelineLayout(_) => { - "the pipeline layout is not compatible with what the shader expects" - } - ComputePipelineCreationError::IncompatibleSpecializationConstants => { - "the provided specialization constants are not compatible with what the shader \ - expects" - } + match self { + Self::OomError(_) => write!(f, "not enough memory available"), + Self::RequirementNotMet { + required_for, + requires_one_of, + } => write!( + f, + "a requirement was not met for: {}; requires one of: {}", + required_for, requires_one_of, + ), + Self::DescriptorSetLayoutCreationError(_) => { + write!(f, "error while creating a descriptor set layout object",) } - ) + Self::PipelineLayoutCreationError(_) => { + write!(f, "error while creating the pipeline layout object",) + } + Self::IncompatiblePipelineLayout(_) => write!( + f, + "the pipeline layout is not compatible with what the shader expects", + ), + Self::ShaderSpecializationConstantTypeMismatch { + constant_id, + default_value, + provided_value, + } => write!( + f, + "the value provided for shader specialization constant id {} ({:?}) has a \ + different type than the constant's default value ({:?})", + constant_id, provided_value, default_value, + ), + Self::ShaderStageInvalid { stage } => write!( + f, + "the provided shader stage ({:?}) is not a compute shader", + stage, + ), + } } } @@ -366,6 +464,15 @@ impl From for ComputePipelineCreationError { } } +impl From for ComputePipelineCreationError { + fn from(err: RequirementNotMet) -> Self { + Self::RequirementNotMet { + required_for: err.required_for, + requires_one_of: err.requires_one_of, + } + } +} + impl From for ComputePipelineCreationError { fn from(err: DescriptorSetLayoutCreationError) -> Self { Self::DescriptorSetLayoutCreationError(err) @@ -406,7 +513,7 @@ mod tests { }, memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator}, pipeline::{ComputePipeline, Pipeline, PipelineBindPoint}, - shader::{ShaderModule, SpecializationConstants, SpecializationMapEntry}, + shader::{PipelineShaderStageCreateInfo, ShaderModule}, sync::{now, GpuFuture}, }; @@ -461,27 +568,12 @@ mod tests { ShaderModule::from_bytes(device.clone(), &MODULE).unwrap() }; - #[derive(Debug, Copy, Clone)] - #[allow(non_snake_case)] - #[repr(C)] - struct SpecConsts { - VALUE: i32, - } - unsafe impl SpecializationConstants for SpecConsts { - fn descriptors() -> &'static [SpecializationMapEntry] { - static DESCRIPTORS: [SpecializationMapEntry; 1] = [SpecializationMapEntry { - constant_id: 83, - offset: 0, - size: 4, - }]; - &DESCRIPTORS - } - } - let pipeline = ComputePipeline::new( device.clone(), - module.entry_point("main").unwrap(), - &SpecConsts { VALUE: 0x12345678 }, + PipelineShaderStageCreateInfo { + specialization_info: [(83, 0x12345678i32.into())].into_iter().collect(), + ..PipelineShaderStageCreateInfo::entry_point(module.entry_point("main").unwrap()) + }, None, |_| {}, ) diff --git a/vulkano/src/pipeline/graphics/builder.rs b/vulkano/src/pipeline/graphics/builder.rs index 3ef98da3..fd3f2bac 100644 --- a/vulkano/src/pipeline/graphics/builder.rs +++ b/vulkano/src/pipeline/graphics/builder.rs @@ -7,14 +7,8 @@ // 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, - }, + color_blend::{AttachmentBlend, ColorBlendAttachmentState, ColorBlendState}, depth_stencil::{DepthStencilState, StencilOps}, discard_rectangle::DiscardRectangleState, input_assembly::{InputAssemblyState, PrimitiveTopology, PrimitiveTopologyClass}, @@ -28,7 +22,7 @@ use super::{ VertexDefinition, VertexInputAttributeDescription, VertexInputBindingDescription, VertexInputState, }, - viewport::{Scissor, Viewport, ViewportState}, + viewport::ViewportState, GraphicsPipeline, GraphicsPipelineCreationError, }; use crate::{ @@ -47,110 +41,58 @@ use crate::{ DynamicState, PartialStateMode, PipelineLayout, StateMode, }, shader::{ - DescriptorBindingRequirements, EntryPoint, FragmentShaderExecution, FragmentTestsStages, - ShaderExecution, ShaderScalarType, ShaderStage, SpecializationConstants, - SpecializationMapEntry, + DescriptorBindingRequirements, FragmentShaderExecution, FragmentTestsStages, + PipelineShaderStageCreateInfo, ShaderExecution, ShaderScalarType, ShaderStage, + ShaderStages, }, 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, -}; +use std::{collections::hash_map::Entry, ffi::CString, mem::MaybeUninit, ptr, sync::Arc}; /// Prototype for a `GraphicsPipeline`. -#[derive(Debug)] -pub struct GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> { +#[derive(Clone, Debug)] +pub struct GraphicsPipelineBuilder { render_pass: Option, cache: Option>, + stages: SmallVec<[PipelineShaderStageCreateInfo; 5]>, - vertex_shader: Option<(EntryPoint<'vs>, Vss)>, - tessellation_shaders: Option>, - geometry_shader: Option<(EntryPoint<'gs>, Gss)>, - fragment_shader: Option<(EntryPoint<'fs>, Fss)>, - - vertex_input_state: Vdef, - input_assembly_state: InputAssemblyState, - tessellation_state: TessellationState, - viewport_state: ViewportState, - discard_rectangle_state: DiscardRectangleState, - rasterization_state: RasterizationState, - multisample_state: MultisampleState, - depth_stencil_state: DepthStencilState, - color_blend_state: ColorBlendState, + vertex_input_state: Option, + input_assembly_state: Option, + tessellation_state: Option, + viewport_state: Option, + discard_rectangle_state: Option, + rasterization_state: Option, + multisample_state: Option, + depth_stencil_state: Option, + color_blend_state: Option, } -// 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, - (), - (), - (), - (), - (), - > -{ +impl GraphicsPipelineBuilder { /// Builds a new empty builder. pub(super) fn new() -> Self { GraphicsPipelineBuilder { render_pass: None, cache: None, + stages: SmallVec::new(), - 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(), + vertex_input_state: None, + input_assembly_state: None, + tessellation_state: None, + viewport_state: None, + discard_rectangle_state: None, + rasterization_state: None, + multisample_state: None, + depth_stencil_state: None, + color_blend_state: None, } } } -#[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> +impl GraphicsPipelineBuilder where Vdef: VertexDefinition, - Vss: SpecializationConstants, - Tcss: SpecializationConstants, - Tess: SpecializationConstants, - Gss: SpecializationConstants, - Fss: SpecializationConstants, { /// Builds the graphics pipeline, using an inferred a pipeline layout. #[inline] @@ -173,17 +115,6 @@ 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< @@ -191,9 +122,10 @@ where DescriptorBindingRequirements, > = HashMap::default(); - for (loc, reqs) in stages + for (&loc, reqs) in self + .stages .iter() - .flat_map(|shader| shader.descriptor_binding_requirements()) + .flat_map(|stage| &stage.entry_point.info().descriptor_binding_requirements) { match descriptor_binding_requirements.entry(loc) { Entry::Occupied(entry) => { @@ -222,8 +154,8 @@ where // 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() { + for info in self.stages.iter() { + if let Some(range) = &info.entry_point.info().push_constant_requirements { match range_map.entry((range.offset, range.size)) { Entry::Vacant(entry) => { entry.insert(range.stages); @@ -269,113 +201,29 @@ where /// the pipeline layout. #[inline] pub fn with_pipeline_layout( - mut self, + self, device: Arc, pipeline_layout: Arc, ) -> Result, GraphicsPipelineCreationError> { let vertex_input_state = self - .vertex_input_state - .definition(self.vertex_shader.as_ref().unwrap().0.input_interface())?; + .stages + .iter() + .find(|stage| matches!(stage.entry_point.info().execution, ShaderExecution::Vertex)) + .zip(self.vertex_input_state.as_ref()) + .map(|(stage, vertex_input_state)| { + vertex_input_state.definition(&stage.entry_point.info().input_interface) + }) + .transpose()?; - // 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)?; + self.validate_create(&device, &pipeline_layout, vertex_input_state.as_ref())?; let (handle, descriptor_requirements, dynamic_state, shaders, fragment_tests_stages) = - unsafe { self.record_create(&device, &pipeline_layout, &vertex_input_state, has)? }; + unsafe { self.record_create(&device, &pipeline_layout, vertex_input_state.as_ref())? }; let Self { mut render_pass, cache: _, - - vertex_shader: _, - tessellation_shaders: _, - geometry_shader: _, - fragment_shader: _, + stages: _, vertex_input_state: _, input_assembly_state, @@ -407,17 +255,15 @@ where 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), + vertex_input_state: vertex_input_state.unwrap(), // Can be None if there's a mesh shader, but we don't support that yet + input_assembly_state: input_assembly_state.unwrap(), // Can be None if there's a mesh shader, but we don't support that yet + tessellation_state, + viewport_state, + discard_rectangle_state, + rasterization_state: rasterization_state.unwrap(), // Can be None for pipeline libraries, but we don't support that yet + multisample_state, + depth_stencil_state, + color_blend_state, dynamic_state, })) } @@ -426,8 +272,7 @@ where &self, device: &Device, pipeline_layout: &PipelineLayout, - vertex_input_state: &VertexInputState, - has: Has, + vertex_input_state: Option<&VertexInputState>, ) -> Result<(), GraphicsPipelineCreationError> { let physical_device = device.physical_device(); let properties = physical_device.properties(); @@ -435,11 +280,7 @@ where let Self { render_pass, cache: _, - - vertex_shader, - tessellation_shaders, - geometry_shader, - fragment_shader, + stages, vertex_input_state: _, input_assembly_state, @@ -452,900 +293,843 @@ where color_blend_state, } = self; - let render_pass = render_pass.as_ref().expect("Missing render pass"); + /* + Gather shader stages + */ - let mut shader_stages: SmallVec<[_; 5]> = SmallVec::new(); + let mut stages_present = ShaderStages::empty(); + let mut vertex_stage = None; + let mut tessellation_control_stage = None; + let mut tessellation_evaluation_stage = None; + let mut geometry_stage = None; + let mut fragment_stage = None; + + for (stage_index, stage) in stages.iter().enumerate() { + let entry_point_info = stage.entry_point.info(); + let stage_enum = ShaderStage::from(&entry_point_info.execution); + let stage_flag = ShaderStages::from(stage_enum); + + // VUID-VkGraphicsPipelineCreateInfo-stage-06897 + if stages_present.intersects(stage_flag) { + return Err(GraphicsPipelineCreationError::ShaderStageDuplicate { + stage_index, + stage: stage_enum, + }); + } + + // VUID-VkGraphicsPipelineCreateInfo-pStages-02095 + // VUID-VkGraphicsPipelineCreateInfo-pStages-06896 + // VUID-VkPipelineShaderStageCreateInfo-stage-parameter + let stage_slot = match stage_enum { + ShaderStage::Vertex => &mut vertex_stage, + ShaderStage::TessellationControl => &mut tessellation_control_stage, + ShaderStage::TessellationEvaluation => &mut tessellation_evaluation_stage, + ShaderStage::Geometry => &mut geometry_stage, + ShaderStage::Fragment => &mut fragment_stage, + _ => { + return Err(GraphicsPipelineCreationError::ShaderStageInvalid { + stage_index, + stage: stage_enum, + }) + } + }; + + *stage_slot = Some(stage); + stages_present |= stage_flag; + } + + /* + Validate needed/unused state + */ + + let need_pre_rasterization_shader_state = true; + + // Check this first because everything else depends on it. + // VUID? + match ( + rasterization_state.is_some(), + need_pre_rasterization_shader_state, + ) { + (true, false) => { + return Err(GraphicsPipelineCreationError::StateUnused { + state: "rasterization_state", + }) + } + (false, true) => { + return Err(GraphicsPipelineCreationError::StateMissing { + state: "rasterization_state", + }) + } + _ => (), + } + + let need_vertex_input_state = need_pre_rasterization_shader_state + && stages + .iter() + .any(|stage| matches!(stage.entry_point.info().execution, ShaderExecution::Vertex)); + let need_fragment_shader_state = need_pre_rasterization_shader_state + && rasterization_state + .as_ref() + .unwrap() + .rasterizer_discard_enable + != StateMode::Fixed(true); + let need_fragment_output_state = need_pre_rasterization_shader_state + && rasterization_state + .as_ref() + .unwrap() + .rasterizer_discard_enable + != StateMode::Fixed(true); + + // VUID-VkGraphicsPipelineCreateInfo-stage-02096 + // VUID-VkGraphicsPipelineCreateInfo-pStages-06895 + match (vertex_stage.is_some(), need_pre_rasterization_shader_state) { + (true, false) => { + return Err(GraphicsPipelineCreationError::ShaderStageUnused { + stage: ShaderStage::Vertex, + }) + } + (false, true) => return Err(GraphicsPipelineCreationError::VertexShaderStageMissing), + _ => (), + } + + // VUID-VkGraphicsPipelineCreateInfo-pStages-06895 + match ( + tessellation_control_stage.is_some(), + need_pre_rasterization_shader_state, + ) { + (true, false) => { + return Err(GraphicsPipelineCreationError::ShaderStageUnused { + stage: ShaderStage::Vertex, + }) + } + (false, true) => (), + _ => (), + } + + // VUID-VkGraphicsPipelineCreateInfo-pStages-06895 + match ( + tessellation_evaluation_stage.is_some(), + need_pre_rasterization_shader_state, + ) { + (true, false) => { + return Err(GraphicsPipelineCreationError::ShaderStageUnused { + stage: ShaderStage::Vertex, + }) + } + (false, true) => (), + _ => (), + } + + // VUID-VkGraphicsPipelineCreateInfo-pStages-00729 + // VUID-VkGraphicsPipelineCreateInfo-pStages-00730 + if stages_present + .intersects(ShaderStages::TESSELLATION_CONTROL | ShaderStages::TESSELLATION_EVALUATION) + && !stages_present.contains( + ShaderStages::TESSELLATION_CONTROL | ShaderStages::TESSELLATION_EVALUATION, + ) + { + return Err(GraphicsPipelineCreationError::OtherTessellationShaderStageMissing); + } + + // VUID-VkGraphicsPipelineCreateInfo-pStages-06895 + match ( + geometry_stage.is_some(), + need_pre_rasterization_shader_state, + ) { + (true, false) => { + return Err(GraphicsPipelineCreationError::ShaderStageUnused { + stage: ShaderStage::Vertex, + }) + } + (false, true) => (), + _ => (), + } + + // VUID-VkGraphicsPipelineCreateInfo-pStages-06894 + match (fragment_stage.is_some(), need_fragment_shader_state) { + (true, false) => { + return Err(GraphicsPipelineCreationError::ShaderStageUnused { + stage: ShaderStage::Vertex, + }) + } + (false, true) => (), + _ => (), + } + + // VUID-VkGraphicsPipelineCreateInfo-pVertexInputState-04910 + match (vertex_input_state.is_some(), need_vertex_input_state) { + (true, false) => { + return Err(GraphicsPipelineCreationError::StateUnused { + state: "vertex_input_state", + }) + } + (false, true) => { + return Err(GraphicsPipelineCreationError::StateMissing { + state: "vertex_input_state", + }) + } + _ => (), + } + + // VUID-VkGraphicsPipelineCreateInfo-pStages-02098 + match (input_assembly_state.is_some(), need_vertex_input_state) { + (true, false) => { + return Err(GraphicsPipelineCreationError::StateUnused { + state: "input_assembly_state", + }) + } + (false, true) => { + return Err(GraphicsPipelineCreationError::StateMissing { + state: "input_assembly_state", + }) + } + _ => (), + } + + // VUID-VkGraphicsPipelineCreateInfo-pStages-00731 + match ( + tessellation_state.is_some(), + need_pre_rasterization_shader_state + && stages_present.contains( + ShaderStages::TESSELLATION_CONTROL | ShaderStages::TESSELLATION_EVALUATION, + ), + ) { + (true, false) => { + return Err(GraphicsPipelineCreationError::StateUnused { + state: "tessellation_state", + }) + } + (false, true) => { + return Err(GraphicsPipelineCreationError::StateMissing { + state: "tessellation_state", + }) + } + _ => (), + } + + // VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00750 + // VUID-VkGraphicsPipelineCreateInfo-pViewportState-04892 + match ( + viewport_state.is_some(), + need_pre_rasterization_shader_state + && rasterization_state + .as_ref() + .unwrap() + .rasterizer_discard_enable + != StateMode::Fixed(true), + ) { + (true, false) => { + return Err(GraphicsPipelineCreationError::StateUnused { + state: "viewport_state", + }) + } + (false, true) => { + return Err(GraphicsPipelineCreationError::StateMissing { + state: "viewport_state", + }) + } + _ => (), + } + + // VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00751 + match (multisample_state.is_some(), need_fragment_output_state) { + (true, false) => { + return Err(GraphicsPipelineCreationError::StateUnused { + state: "multisample_state", + }) + } + (false, true) => { + return Err(GraphicsPipelineCreationError::StateMissing { + state: "multisample_state", + }) + } + _ => (), + } + + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06590 + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06043 + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06053 + match ( + depth_stencil_state.is_some(), + !need_fragment_output_state + || match render_pass { + Some(PipelineRenderPassType::BeginRenderPass(subpass)) => { + subpass.subpass_desc().depth_stencil_attachment.is_some() + } + Some(PipelineRenderPassType::BeginRendering(rendering_info)) => { + rendering_info.depth_attachment_format.is_some() + || rendering_info.stencil_attachment_format.is_some() + } + None => false, + }, + ) { + (true, false) => { + return Err(GraphicsPipelineCreationError::StateUnused { + state: "depth_stencil_state", + }) + } + (false, true) => { + return Err(GraphicsPipelineCreationError::StateMissing { + state: "depth_stencil_state", + }) + } + _ => (), + } + + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06044 + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06054 + match ( + color_blend_state.is_some(), + need_fragment_output_state + && match render_pass { + Some(PipelineRenderPassType::BeginRenderPass(subpass)) => { + !subpass.subpass_desc().color_attachments.is_empty() + } + Some(PipelineRenderPassType::BeginRendering(rendering_info)) => { + !rendering_info.color_attachment_formats.is_empty() + } + None => false, + }, + ) { + (true, false) => { + return Err(GraphicsPipelineCreationError::StateUnused { + state: "color_blend_state", + }) + } + (false, true) => { + return Err(GraphicsPipelineCreationError::StateMissing { + state: "color_blend_state", + }) + } + _ => (), + } + + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06575 + match ( + render_pass.is_some(), + need_pre_rasterization_shader_state + || need_fragment_shader_state + || need_fragment_output_state, + ) { + (true, false) => { + return Err(GraphicsPipelineCreationError::StateUnused { + state: "render_pass", + }) + } + (false, true) => { + return Err(GraphicsPipelineCreationError::StateMissing { + state: "render_pass", + }) + } + _ => (), + } + + // VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-04058 (partly) + match ( + discard_rectangle_state.is_some(), + need_pre_rasterization_shader_state, + ) { + (true, false) => { + return Err(GraphicsPipelineCreationError::StateUnused { + state: "discard_rectangle_state", + }) + } + (false, true) => (), + _ => (), + } + + /* + Validate shader stages individually + */ + + for (stage_index, stage) in stages.iter().enumerate() { + let &PipelineShaderStageCreateInfo { + flags, + ref entry_point, + ref specialization_info, + _ne: _, + } = stage; + + // VUID-VkPipelineShaderStageCreateInfo-flags-parameter + flags.validate_device(device)?; + + let entry_point_info = entry_point.info(); + let stage_enum = ShaderStage::from(&entry_point_info.execution); + + // VUID-VkPipelineShaderStageCreateInfo-pName-00707 + // Guaranteed by definition of `EntryPoint`. + + // TODO: + // VUID-VkPipelineShaderStageCreateInfo-maxClipDistances-00708 + // VUID-VkPipelineShaderStageCreateInfo-maxCullDistances-00709 + // VUID-VkPipelineShaderStageCreateInfo-maxCombinedClipAndCullDistances-00710 + // VUID-VkPipelineShaderStageCreateInfo-maxSampleMaskWords-00711 + + match stage_enum { + ShaderStage::Vertex => { + vertex_stage = Some(stage); + + // VUID-VkPipelineShaderStageCreateInfo-stage-00712 + // TODO: + } + ShaderStage::TessellationControl | ShaderStage::TessellationEvaluation => { + // VUID-VkPipelineShaderStageCreateInfo-stage-00705 + if !device.enabled_features().tessellation_shader { + return Err(GraphicsPipelineCreationError::RequirementNotMet { + required_for: "`stages` contains a `TessellationControl` or \ + `TessellationEvaluation` shader stage", + requires_one_of: RequiresOneOf { + features: &["tessellation_shader"], + ..Default::default() + }, + }); + } + + // VUID-VkPipelineShaderStageCreateInfo-stage-00713 + // TODO: + } + ShaderStage::Geometry => { + // VUID-VkPipelineShaderStageCreateInfo-stage-00704 + if !device.enabled_features().geometry_shader { + return Err(GraphicsPipelineCreationError::RequirementNotMet { + required_for: "`stages` contains a `Geometry` shader stage", + requires_one_of: RequiresOneOf { + features: &["geometry_shader"], + ..Default::default() + }, + }); + } + + // VUID-VkPipelineShaderStageCreateInfo-stage-00714 + // VUID-VkPipelineShaderStageCreateInfo-stage-00715 + // TODO: + } + ShaderStage::Fragment => { + fragment_stage = Some(stage); + + // VUID-VkPipelineShaderStageCreateInfo-stage-00718 + // VUID-VkPipelineShaderStageCreateInfo-stage-06685 + // VUID-VkPipelineShaderStageCreateInfo-stage-06686 + // TODO: + } + _ => unreachable!(), + } + + // TODO: + // VUID-VkPipelineShaderStageCreateInfo-stage-02596 + // VUID-VkPipelineShaderStageCreateInfo-stage-02597 + + for (&constant_id, provided_value) in specialization_info { + // Per `VkSpecializationMapEntry` spec: + // "If a constantID value is not a specialization constant ID used in the shader, + // that map entry does not affect the behavior of the pipeline." + // We *may* want to be stricter than this for the sake of catching user errors? + if let Some(default_value) = + entry_point_info.specialization_constants.get(&constant_id) + { + // VUID-VkSpecializationMapEntry-constantID-00776 + // Check for equal types rather than only equal size. + if !provided_value.eq_type(default_value) { + return Err( + GraphicsPipelineCreationError::ShaderSpecializationConstantTypeMismatch { + stage_index, + constant_id, + default_value: *default_value, + provided_value: *provided_value, + }, + ); + } + } + } + + // VUID-VkGraphicsPipelineCreateInfo-layout-00756 + pipeline_layout.ensure_compatible_with_shader( + entry_point_info + .descriptor_binding_requirements + .iter() + .map(|(k, v)| (*k, v)), + entry_point_info.push_constant_requirements.as_ref(), + )?; + } + + let ordered_stages: SmallVec<[_; 5]> = [ + vertex_stage, + tessellation_control_stage, + tessellation_evaluation_stage, + geometry_stage, + fragment_stage, + ] + .into_iter() + .flatten() + .collect(); + + // 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 ordered_stages.iter().zip(ordered_stages.iter().skip(1)) { + if let Err(err) = (input.entry_point.info().input_interface) + .matches(&output.entry_point.info().output_interface) + { + return Err(GraphicsPipelineCreationError::ShaderStagesMismatch(err)); + } + } // VUID-VkGraphicsPipelineCreateInfo-layout-01688 // Checked at pipeline layout creation time. /* - Render pass + Validate states individually */ - match render_pass { - PipelineRenderPassType::BeginRenderPass(subpass) => { - // VUID-VkGraphicsPipelineCreateInfo-commonparent - assert_eq!(device, subpass.render_pass().device().as_ref()); + if let Some(vertex_input_state) = vertex_input_state { + 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, + }, + ); } - 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-VkPipelineVertexInputStateCreateInfo-pVertexBindingDescriptions-00616 + // Ensured by HashMap. - // 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() - }, - }); - } + for (&binding, binding_desc) in bindings { + let &VertexInputBindingDescription { stride, input_rate } = binding_desc; - 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 { + // VUID-VkVertexInputBindingDescription-binding-00618 + if binding >= properties.max_vertex_input_bindings { return Err( GraphicsPipelineCreationError::MaxVertexInputBindingsExceeded { max: properties.max_vertex_input_bindings, - obtained: bindings.len() as u32, + obtained: binding, }, ); } - // VUID-VkPipelineVertexInputStateCreateInfo-pVertexBindingDescriptions-00616 - // Ensured by HashMap. + // 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, + }, + ); + } - 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 { + // 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 + match input_rate { + VertexInputRate::Instance { divisor } if divisor != 1 => { + // VUID-VkVertexInputBindingDivisorDescriptionEXT-vertexAttributeInstanceRateDivisor-02229 + if !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) + .vertex_attribute_instance_rate_divisor { return Err(GraphicsPipelineCreationError::RequirementNotMet { - required_for: "`input_assembly_state.topology` is \ - `PartialStateMode::Dynamic`", + required_for: "`vertex_input_state.bindings` has an element \ + where `input_rate` is `VertexInputRate::Instance`, where \ + `divisor` is not `1`", requires_one_of: RequiresOneOf { - api_version: Some(Version::V1_3), - features: &["extended_dynamic_state"], + features: &["vertex_attribute_instance_rate_divisor"], ..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) + // VUID-VkVertexInputBindingDivisorDescriptionEXT-vertexAttributeInstanceRateZeroDivisor-02228 + if divisor == 0 + && !device + .enabled_features() + .vertex_attribute_instance_rate_zero_divisor { return Err(GraphicsPipelineCreationError::RequirementNotMet { - required_for: "`input_assembly_state.primitive_restart_enable` is \ - `StateMode::Dynamic`", + required_for: "`vertex_input_state.bindings` has an element \ + where `input_rate` is `VertexInputRate::Instance`, where \ + `divisor` is `0`", requires_one_of: RequiresOneOf { - api_version: Some(Version::V1_3), - features: &["extended_dynamic_state2"], + features: &["vertex_attribute_instance_rate_zero_divisor"], ..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) - ) { + // VUID-VkVertexInputBindingDivisorDescriptionEXT-divisor-01870 + if divisor > properties.max_vertex_attrib_divisor.unwrap() { return Err( - GraphicsPipelineCreationError::VertexInputAttributeIncompatibleFormat { - location, - shader_type, - attribute_type, + GraphicsPipelineCreationError::MaxVertexAttribDivisorExceeded { + binding, + max: properties.max_vertex_attrib_divisor.unwrap(), + obtained: divisor, }, ); } } - } - - // 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 + // 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: "`rasterization_state.polygon_mode` is not \ - `PolygonMode::Fill`", + 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: &["fill_mode_non_solid"], + features: &["vertex_attribute_access_beyond_stride"], ..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() - }, - }); + if let Some(input_assembly_state) = input_assembly_state { + 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 + } + _ => (), } } - - 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; + PartialStateMode::Dynamic(topology_class) => { + // VUID-VkPipelineInputAssemblyStateCreateInfo-topology-parameter + topology_class.example().validate_device(device)?; // VUID? - if enable_dynamic - && !(device.api_version() >= Version::V1_3 - || device.enabled_features().extended_dynamic_state2) + if !(device.api_version() >= Version::V1_3 + || device.enabled_features().extended_dynamic_state) { return Err(GraphicsPipelineCreationError::RequirementNotMet { - required_for: "`rasterization_state.depth_bias` is \ - `Some(depth_bias_state)`, where `depth_bias_state.enable_dynamic` \ - is set", + 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"], @@ -1353,250 +1137,15 @@ where }, }); } - - // 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 { + if let Some(tessellation_state) = 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 @@ -1625,10 +1174,7 @@ where }; } - // Viewport state - // VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00750 - // VUID-VkGraphicsPipelineCreateInfo-pViewportState-04892 - if has.viewport_state { + if let Some(viewport_state) = viewport_state { let (viewport_count, scissor_count) = match viewport_state { ViewportState::Fixed { data } => { let count = data.len() as u32; @@ -1841,64 +1387,379 @@ where // VUID-VkGraphicsPipelineCreateInfo-primitiveFragmentShadingRateWithMultipleViewports-04504 } - /* - Fragment shader state - */ + if let Some(rasterization_state) = 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; - 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), - ) + // 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 entry_point.execution() { - ShaderExecution::Fragment(_) => (), - _ => return Err(GraphicsPipelineCreationError::WrongShaderType), + 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() + }, + }); } - // 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, - ); + // 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() + }, + }); } } - PipelineRenderPassType::BeginRendering(_) => { - // TODO: + 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() + }, + }); + } } } - // 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 + 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 { - // TODO: should probably error out here at least under some circumstances? - // VUID? + 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() + }, + }); + } } // TODO: - // VUID-VkGraphicsPipelineCreateInfo-renderPass-06038 + // VUID-VkGraphicsPipelineCreateInfo-pStages-00740 + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06049 + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06050 + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06059 } - // Depth/stencil state - // VUID-VkGraphicsPipelineCreateInfo-renderPass-06043 - // VUID-VkGraphicsPipelineCreateInfo-renderPass-06053 - // VUID-VkGraphicsPipelineCreateInfo-renderPass-06590 - if has.depth_stencil_state { + if let Some(multisample_state) = multisample_state { + 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)?; + + 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 + } + + if let Some(depth_stencil_state) = depth_stencil_state { let DepthStencilState { depth, depth_bounds, @@ -1912,18 +1773,6 @@ where 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 @@ -1941,19 +1790,7 @@ where } 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::Fixed(_) => (), StateMode::Dynamic => { // VUID? if !(device.api_version() >= Version::V1_3 @@ -2058,17 +1895,6 @@ where 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 @@ -2144,33 +1970,12 @@ where 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), + if !matches!( + (front.reference, back.reference), + (StateMode::Fixed(_), StateMode::Fixed(_)) + | (StateMode::Dynamic, StateMode::Dynamic) + ) { + return Err(GraphicsPipelineCreationError::WrongStencilState); } // TODO: @@ -2178,83 +1983,7 @@ where } } - /* - 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 { + if let Some(color_blend_state) = color_blend_state { let ColorBlendState { logic_op, attachments, @@ -2294,22 +2023,6 @@ where } } - 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 @@ -2330,7 +2043,7 @@ where } } - for (attachment_index, state) in attachments.iter().enumerate() { + for state in attachments { let &ColorBlendAttachmentState { blend, color_write_mask: _, @@ -2396,37 +2109,6 @@ where }); } - 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 @@ -2485,60 +2167,520 @@ where } } - /* - Generic shader checks - */ + if let Some(render_pass) = render_pass { + match render_pass { + PipelineRenderPassType::BeginRenderPass(subpass) => { + // VUID-VkGraphicsPipelineCreateInfo-commonparent + assert_eq!(device, subpass.render_pass().device().as_ref()); - 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(), - )?; + if subpass.render_pass().views_used() != 0 { + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06047 + if stages_present.intersects( + ShaderStages::TESSELLATION_CONTROL + | ShaderStages::TESSELLATION_EVALUATION, + ) && !device.enabled_features().multiview_tessellation_shader + { + 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() + }, + }); + } - 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)?; + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06048 + if stages_present.intersects(ShaderStages::GEOMETRY) + && !device.enabled_features().multiview_geometry_shader + { + 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() + }, + }); + } + } + } + PipelineRenderPassType::BeginRendering(rendering_info) => { + let &PipelineRenderingCreateInfo { + view_mask, + ref color_attachment_formats, + depth_attachment_format, + stencil_attachment_format, + _ne: _, + } = rendering_info; - if map_entry.size as DeviceSize != reqs.size { - return Err(GraphicsPipelineCreationError::IncompatibleSpecializationConstants); + // 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 need_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, + ); + } + } + } + + if view_mask != 0 { + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06057 + if stages_present.intersects( + ShaderStages::TESSELLATION_CONTROL + | ShaderStages::TESSELLATION_EVALUATION, + ) && !device.enabled_features().multiview_tessellation_shader + { + 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() + }, + }); + } + + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06058 + if stages_present.intersects(ShaderStages::GEOMETRY) + && !device.enabled_features().multiview_geometry_shader + { + 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() + }, + }); + } + } } } } - // 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)); + if let Some(discard_rectangle_state) = discard_rectangle_state { + if !device.enabled_extensions().ext_discard_rectangles { + return Err(GraphicsPipelineCreationError::RequirementNotMet { + required_for: "`discard_rectangle_state` is `Some`", + requires_one_of: RequiresOneOf { + device_extensions: &["ext_discard_rectangles"], + ..Default::default() + }, + }); + } + + let DiscardRectangleState { mode, rectangles } = discard_rectangle_state; + + // 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, + }, + ); } } - // TODO: - // VUID-VkPipelineShaderStageCreateInfo-maxClipDistances-00708 - // VUID-VkPipelineShaderStageCreateInfo-maxCullDistances-00709 - // VUID-VkPipelineShaderStageCreateInfo-maxCombinedClipAndCullDistances-00710 - // VUID-VkPipelineShaderStageCreateInfo-maxSampleMaskWords-00711 + /* + Checks that rely on multiple pieces of state + */ - // Dynamic states not handled yet: - // - ViewportWScaling (VkPipelineViewportWScalingStateCreateInfoNV) - // - SampleLocations (VkPipelineSampleLocationsStateCreateInfoEXT) - // - ViewportShadingRatePalette (VkPipelineViewportShadingRateImageStateCreateInfoNV) - // - ViewportCoarseSampleOrder (VkPipelineViewportCoarseSampleOrderStateCreateInfoNV) - // - ExclusiveScissor (VkPipelineViewportExclusiveScissorStateCreateInfoNV) - // - FragmentShadingRate (VkPipelineFragmentShadingRateStateCreateInfoKHR) + if let (Some(vertex_stage), Some(vertex_input_state)) = (vertex_stage, vertex_input_state) { + for element in vertex_stage.entry_point.info().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 { + // VUID-VkGraphicsPipelineCreateInfo-Input-07905 + 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(); + + // VUID? + 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, + }, + ); + } + } + } + } + + if let (Some(_), Some(_)) = (tessellation_control_stage, tessellation_evaluation_stage) { + // FIXME: must check that the control shader and evaluation shader are compatible + + // TODO: + // VUID-VkGraphicsPipelineCreateInfo-pStages-00732 + // VUID-VkGraphicsPipelineCreateInfo-pStages-00733 + // VUID-VkGraphicsPipelineCreateInfo-pStages-00734 + // VUID-VkGraphicsPipelineCreateInfo-pStages-00735 + } + + if let (Some(_), Some(_)) = (tessellation_evaluation_stage, geometry_stage) { + // TODO: + // VUID-VkGraphicsPipelineCreateInfo-pStages-00739 + } + + if let (None, Some(geometry_stage), Some(input_assembly_state)) = ( + tessellation_evaluation_stage, + geometry_stage, + input_assembly_state, + ) { + let entry_point_info = geometry_stage.entry_point.info(); + let input = match entry_point_info.execution { + ShaderExecution::Geometry(execution) => execution.input, + _ => unreachable!(), + }; + + if let PartialStateMode::Fixed(topology) = input_assembly_state.topology { + // VUID-VkGraphicsPipelineCreateInfo-pStages-00738 + if !input.is_compatible_with(topology) { + return Err(GraphicsPipelineCreationError::TopologyNotMatchingGeometryShader); + } + } + } + + if let (Some(fragment_stage), Some(render_pass)) = (fragment_stage, render_pass) { + let entry_point_info = fragment_stage.entry_point.info(); + + // 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_info.output_interface) { + return Err( + GraphicsPipelineCreationError::FragmentShaderRenderPassIncompatible, + ); + } + } + PipelineRenderPassType::BeginRendering(_) => { + // TODO: + } + } + + // TODO: + // VUID-VkGraphicsPipelineCreateInfo-pStages-01565 + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06038 + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06056 + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06061 + } + + if let (Some(input_assembly_state), Some(_)) = (input_assembly_state, tessellation_state) { + // VUID-VkGraphicsPipelineCreateInfo-pStages-00736 + if !matches!( + input_assembly_state.topology, + PartialStateMode::Dynamic(PrimitiveTopologyClass::Patch) + | PartialStateMode::Fixed(PrimitiveTopology::PatchList) + ) { + return Err(GraphicsPipelineCreationError::InvalidPrimitiveTopology); + } + } + + if let (Some(rasterization_state), Some(depth_stencil_state)) = + (rasterization_state, depth_stencil_state) + { + if let Some(stencil_state) = &depth_stencil_state.stencil { + if let (StateMode::Fixed(front_reference), StateMode::Fixed(back_reference)) = + (stencil_state.front.reference, stencil_state.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() + }, + }); + } + } + } + } + + if let (Some(multisample_state), Some(render_pass)) = (multisample_state, render_pass) { + match render_pass { + PipelineRenderPassType::BeginRenderPass(subpass) => { + if let Some(samples) = subpass.num_samples() { + // VUID-VkGraphicsPipelineCreateInfo-subpass-00757 + if multisample_state.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(depth_stencil_state), Some(render_pass)) = (depth_stencil_state, render_pass) { + if let Some(depth_state) = &depth_stencil_state.depth { + 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); + } + + if let StateMode::Fixed(true) = depth_state.write_enable { + match render_pass { + PipelineRenderPassType::BeginRenderPass(subpass) => { + // VUID-VkGraphicsPipelineCreateInfo-renderPass-06039 + if !subpass.has_writable_depth() { + return Err(GraphicsPipelineCreationError::NoDepthAttachment); + } + } + PipelineRenderPassType::BeginRendering(_) => { + // No VUID? + } + } + } + } + + if depth_stencil_state.stencil.is_some() { + 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); + } + } + } + + if let (Some(color_blend_state), Some(render_pass)) = (color_blend_state, render_pass) { + 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 != color_blend_state.attachments.len() { + return Err(GraphicsPipelineCreationError::MismatchBlendingAttachmentsCount); + } + + for (attachment_index, state) in color_blend_state.attachments.iter().enumerate() { + if state.blend.is_some() { + 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, + }, + ); + } + } + } + } Ok(()) } @@ -2547,8 +2689,7 @@ where &self, device: &Device, pipeline_layout: &PipelineLayout, - vertex_input_state: &VertexInputState, - has: Has, + vertex_input_state: Option<&VertexInputState>, ) -> Result< ( ash::vk::Pipeline, @@ -2562,11 +2703,7 @@ where let Self { render_pass, cache, - - vertex_shader, - tessellation_shaders, - geometry_shader, - fragment_shader, + stages, vertex_input_state: _, input_assembly_state, @@ -2579,520 +2716,233 @@ where color_blend_state, } = self; - let render_pass = render_pass.as_ref().unwrap(); - let mut descriptor_binding_requirements: HashMap< (u32, u32), DescriptorBindingRequirements, > = HashMap::default(); - let mut dynamic_state: HashMap = HashMap::default(); - let mut stages = HashMap::default(); - let mut stages_vk: SmallVec<[_; 5]> = SmallVec::new(); + let mut stages2 = HashMap::default(); let mut fragment_tests_stages = None; + let mut dynamic_state: HashMap = HashMap::default(); - /* - 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() - }); - } + struct PerPipelineShaderStageCreateInfo { + name_vk: CString, + specialization_info_vk: ash::vk::SpecializationInfo, + specialization_map_entries_vk: Vec, + specialization_data_vk: Vec, } - /* - Vertex input state - */ + let (mut stages_vk, mut per_stage_vk): (SmallVec<[_; 5]>, SmallVec<[_; 5]>) = stages + .iter() + .map(|stage| { + let &PipelineShaderStageCreateInfo { + flags, + ref entry_point, + ref specialization_info, + _ne: _, + } = stage; + let entry_point_info = entry_point.info(); + let stage = ShaderStage::from(&entry_point_info.execution); + stages2.insert(stage, ()); + + if let ShaderExecution::Fragment( + FragmentShaderExecution { + fragment_tests_stages: s, + .. + } + ) = entry_point_info.execution { + fragment_tests_stages = Some(s) + } + + for (&loc, reqs) in &entry_point_info.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()); + } + } + } + + let mut specialization_data_vk: Vec = Vec::new(); + let specialization_map_entries_vk: Vec<_> = specialization_info + .iter() + .map(|(&constant_id, value)| { + let data = value.as_bytes(); + let offset = specialization_data_vk.len() as u32; + let size = data.len(); + specialization_data_vk.extend(data); + + ash::vk::SpecializationMapEntry { + constant_id, + offset, + size, + } + }) + .collect(); + + ( + ash::vk::PipelineShaderStageCreateInfo { + flags: flags.into(), + stage: stage.into(), + module: entry_point.module().handle(), + p_name: ptr::null(), + p_specialization_info: ptr::null(), + ..Default::default() + }, + PerPipelineShaderStageCreateInfo { + name_vk: CString::new(entry_point_info.name.as_str()).unwrap(), + specialization_info_vk: ash::vk::SpecializationInfo { + map_entry_count: specialization_map_entries_vk.len() as u32, + p_map_entries: ptr::null(), + data_size: specialization_data_vk.len(), + p_data: ptr::null(), + }, + specialization_map_entries_vk, + specialization_data_vk, + }, + ) + }) + .unzip(); + + for ( + stage_vk, + PerPipelineShaderStageCreateInfo { + name_vk, + specialization_info_vk, + specialization_map_entries_vk, + specialization_data_vk, + }, + ) in (stages_vk.iter_mut()).zip(per_stage_vk.iter_mut()) + { + *stage_vk = ash::vk::PipelineShaderStageCreateInfo { + p_name: name_vk.as_ptr(), + p_specialization_info: specialization_info_vk, + ..*stage_vk + }; + + *specialization_info_vk = ash::vk::SpecializationInfo { + p_map_entries: specialization_map_entries_vk.as_ptr(), + p_data: specialization_data_vk.as_ptr() as _, + ..*specialization_info_vk + }; + } + + let mut vertex_input_state_vk = None; 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; + let mut vertex_binding_divisor_descriptions_vk: SmallVec<[_; 8]> = SmallVec::new(); - if has.vertex_input_state { - // Vertex input state - { - dynamic_state.insert(DynamicState::VertexInput, false); + if let Some(vertex_input_state) = vertex_input_state { + dynamic_state.insert(DynamicState::VertexInput, false); - let VertexInputState { - bindings, - attributes, - } = vertex_input_state; + 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_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, - }, - )); + 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 _, + 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() }); - } - // 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"); + { + 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)) } - 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), + _ => None, + }) + .map(|(binding, divisor)| { + ash::vk::VertexInputBindingDivisorDescriptionEXT { binding, divisor } + }), ); - 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, + // 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 _; } } - - // 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 mut input_assembly_state_vk = None; + + if let Some(input_assembly_state) = 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() + }); + } + + let mut tessellation_state_vk = None; + + if let Some(tessellation_state) = tessellation_state { let &TessellationState { patch_control_points, } = tessellation_state; @@ -3115,8 +2965,11 @@ where }); } - // Viewport state - if has.viewport_state { + let mut viewport_state_vk = None; + let mut viewports_vk: SmallVec<[_; 2]> = SmallVec::new(); + let mut scissors_vk: SmallVec<[_; 2]> = SmallVec::new(); + + if let Some(viewport_state) = viewport_state { let (viewport_count, scissor_count) = match viewport_state { ViewportState::Fixed { data } => { let count = data.len() as u32; @@ -3222,62 +3075,174 @@ where }); } - /* - Fragment shader state - */ + let mut rasterization_state_vk = None; + let mut rasterization_line_state_vk = None; - let mut fragment_shader_specialization_vk = None; - let mut depth_stencil_state_vk = None; + if let Some(rasterization_state) = 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; - 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 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 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 _, - }); + 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() + } + }; - 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()); - } - } + 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); } - 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 _, + 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() }); - fragment_tests_stages = match entry_point.execution() { - ShaderExecution::Fragment(FragmentShaderExecution { - fragment_tests_stages, - .. - }) => Some(*fragment_tests_stages), - _ => unreachable!(), - }; + + 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 _; } } - // Depth/stencil state - if has.depth_stencil_state { + let mut multisample_state_vk = None; + + if let Some(multisample_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() + }); + } + + let mut depth_stencil_state_vk = None; + + if let Some(depth_stencil_state) = depth_stencil_state { let DepthStencilState { depth, depth_bounds, @@ -3463,49 +3428,12 @@ where }); } - /* - 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; + let mut color_blend_attachments_vk: SmallVec<[_; 4]> = SmallVec::new(); + let mut color_write_vk = None; + let mut color_write_enables_vk: SmallVec<[_; 4]> = SmallVec::new(); - 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 { + if let Some(color_blend_state) = color_blend_state { let &ColorBlendState { logic_op, ref attachments, @@ -3603,10 +3531,6 @@ where } } - /* - Dynamic state - */ - let mut dynamic_state_list: SmallVec<[_; 4]> = SmallVec::new(); let mut dynamic_state_vk = None; @@ -3628,6 +3552,76 @@ where } } + let render_pass = render_pass.as_ref().unwrap(); + 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() + }); + } + } + + let mut discard_rectangle_state_vk = None; + let mut discard_rectangles: SmallVec<[_; 2]> = SmallVec::new(); + + if let Some(discard_rectangle_state) = discard_rectangle_state { + 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() + }, + ); + } + /* Create */ @@ -3723,7 +3717,7 @@ where handle, descriptor_binding_requirements, dynamic_state, - stages, + stages2, fragment_tests_stages, )) } @@ -3731,169 +3725,34 @@ where // 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. +impl GraphicsPipelineBuilder { + /// Sets the shader stages 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, - } + /// A vertex shader must always be included. Other stages are optional. + #[inline] + pub fn stages( + mut self, + stages: impl IntoIterator, + ) -> Self { + self.stages = stages.into_iter().collect(); + self } /// Sets the vertex input state. /// - /// The default value is [`VertexInputState::default()`]. - pub fn vertex_input_state( - self, - vertex_input_state: T, - ) -> GraphicsPipelineBuilder<'vs, 'tcs, 'tes, 'gs, 'fs, T, Vss, Tcss, Tess, Gss, Fss> + /// This state is always used, and must be provided. + /// + /// The default value is `None`. + pub fn vertex_input_state(self, vertex_input_state: T) -> GraphicsPipelineBuilder where T: VertexDefinition, { GraphicsPipelineBuilder { render_pass: self.render_pass, cache: self.cache, + stages: self.stages, - vertex_shader: self.vertex_shader, - tessellation_shaders: self.tessellation_shaders, - geometry_shader: self.geometry_shader, - fragment_shader: self.fragment_shader, - - vertex_input_state, + vertex_input_state: Some(vertex_input_state), input_assembly_state: self.input_assembly_state, tessellation_state: self.tessellation_state, viewport_state: self.viewport_state, @@ -3907,635 +3766,113 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> /// Sets the input assembly state. /// - /// The default value is [`InputAssemblyState::default()`]. + /// This state is always used, and must be provided. + /// + /// The default value is `None`. #[inline] pub fn input_assembly_state(mut self, input_assembly_state: InputAssemblyState) -> Self { - self.input_assembly_state = input_assembly_state; + self.input_assembly_state = Some(input_assembly_state); self } - /// Sets the tessellation state. This is required if the pipeline contains tessellation shaders, - /// and ignored otherwise. + /// Sets the tessellation state. /// - /// The default value is [`TessellationState::default()`]. + /// This state is used if `stages` contains tessellation shaders. + /// + /// The default value is `None`. #[inline] pub fn tessellation_state(mut self, tessellation_state: TessellationState) -> Self { - self.tessellation_state = tessellation_state; + self.tessellation_state = Some(tessellation_state); self } /// Sets the viewport state. /// - /// The default value is [`ViewportState::default()`]. + /// This state is used if [rasterizer discarding] is not enabled. + /// + /// The default value is `None`. + /// + /// [rasterizer discarding]: RasterizationState::rasterizer_discard_enable #[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.viewport_state = Some(viewport_state); self } /// Sets the rasterization state. /// - /// The default value is [`RasterizationState::default()`]. + /// This state is always used, and must be provided. + /// + /// The default value is `None`. #[inline] pub fn rasterization_state(mut self, rasterization_state: RasterizationState) -> Self { - self.rasterization_state = rasterization_state; + self.rasterization_state = Some(rasterization_state); self } /// Sets the multisample state. /// - /// The default value is [`MultisampleState::default()`]. + /// This state is used if [rasterizer discarding] is not enabled. + /// + /// The default value is `None`. + /// + /// [rasterizer discarding]: RasterizationState::rasterizer_discard_enable #[inline] pub fn multisample_state(mut self, multisample_state: MultisampleState) -> Self { - self.multisample_state = multisample_state; + self.multisample_state = Some(multisample_state); self } /// Sets the depth/stencil state. /// - /// The default value is [`DepthStencilState::default()`]. + /// This state is used if `render_pass` has depth/stencil attachments, or if + /// [rasterizer discarding] is enabled. + /// + /// The default value is `None`. + /// + /// [rasterizer discarding]: RasterizationState::rasterizer_discard_enable #[inline] pub fn depth_stencil_state(mut self, depth_stencil_state: DepthStencilState) -> Self { - self.depth_stencil_state = depth_stencil_state; + self.depth_stencil_state = Some(depth_stencil_state); self } /// Sets the color blend state. /// - /// The default value is [`ColorBlendState::default()`]. + /// This state is used if `render_pass` has color attachments, and [rasterizer discarding] is + /// not enabled. + /// + /// The default value is `None`. + /// + /// [rasterizer discarding]: RasterizationState::rasterizer_discard_enable #[inline] pub fn color_blend_state(mut self, color_blend_state: ColorBlendState) -> Self { - self.color_blend_state = color_blend_state; - self - } - - /// Sets the tessellation shaders stage as disabled. This is the default. - #[deprecated(since = "0.27.0")] - #[inline] - pub fn tessellation_shaders_disabled(mut self) -> Self { - self.tessellation_shaders = None; - self - } - - /// Sets the geometry shader stage as disabled. This is the default. - #[deprecated(since = "0.27.0")] - #[inline] - pub fn geometry_shader_disabled(mut self) -> Self { - self.geometry_shader = None; - self - } - - /// Sets whether primitive restart is enabled. - #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] - #[inline] - pub fn primitive_restart(mut self, enabled: bool) -> Self { - self.input_assembly_state.primitive_restart_enable = StateMode::Fixed(enabled); - self - } - - /// Sets the topology of the primitives that are expected by the pipeline. - #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] - #[inline] - pub fn primitive_topology(mut self, topology: PrimitiveTopology) -> Self { - self.input_assembly_state.topology = PartialStateMode::Fixed(topology); - self - } - - /// Sets the topology of the primitives to a list of points. - /// - /// > **Note**: This is equivalent to - /// > `self.primitive_topology(PrimitiveTopology::PointList)`. - #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] - #[inline] - pub fn point_list(self) -> Self { - self.primitive_topology(PrimitiveTopology::PointList) - } - - /// Sets the topology of the primitives to a list of lines. - /// - /// > **Note**: This is equivalent to - /// > `self.primitive_topology(PrimitiveTopology::LineList)`. - #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] - #[inline] - pub fn line_list(self) -> Self { - self.primitive_topology(PrimitiveTopology::LineList) - } - - /// Sets the topology of the primitives to a line strip. - /// - /// > **Note**: This is equivalent to - /// > `self.primitive_topology(PrimitiveTopology::LineStrip)`. - #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] - #[inline] - pub fn line_strip(self) -> Self { - self.primitive_topology(PrimitiveTopology::LineStrip) - } - - /// Sets the topology of the primitives to a list of triangles. Note that this is the default. - /// - /// > **Note**: This is equivalent to - /// > `self.primitive_topology(PrimitiveTopology::TriangleList)`. - #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] - #[inline] - pub fn triangle_list(self) -> Self { - self.primitive_topology(PrimitiveTopology::TriangleList) - } - - /// Sets the topology of the primitives to a triangle strip. - /// - /// > **Note**: This is equivalent to - /// > `self.primitive_topology(PrimitiveTopology::TriangleStrip)`. - #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] - #[inline] - pub fn triangle_strip(self) -> Self { - self.primitive_topology(PrimitiveTopology::TriangleStrip) - } - - /// Sets the topology of the primitives to a fan of triangles. - /// - /// > **Note**: This is equivalent to - /// > `self.primitive_topology(PrimitiveTopology::TriangleFan)`. - #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] - #[inline] - pub fn triangle_fan(self) -> Self { - self.primitive_topology(PrimitiveTopology::TriangleFan) - } - - /// Sets the topology of the primitives to a list of lines with adjacency information. - /// - /// > **Note**: This is equivalent to - /// > `self.primitive_topology(PrimitiveTopology::LineListWithAdjacency)`. - #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] - #[inline] - pub fn line_list_with_adjacency(self) -> Self { - self.primitive_topology(PrimitiveTopology::LineListWithAdjacency) - } - - /// Sets the topology of the primitives to a line strip with adjacency information. - /// - /// > **Note**: This is equivalent to - /// > `self.primitive_topology(PrimitiveTopology::LineStripWithAdjacency)`. - #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] - #[inline] - pub fn line_strip_with_adjacency(self) -> Self { - self.primitive_topology(PrimitiveTopology::LineStripWithAdjacency) - } - - /// Sets the topology of the primitives to a list of triangles with adjacency information. - /// - /// > **Note**: This is equivalent to - /// > `self.primitive_topology(PrimitiveTopology::TriangleListWithAdjacency)`. - #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] - #[inline] - pub fn triangle_list_with_adjacency(self) -> Self { - self.primitive_topology(PrimitiveTopology::TriangleListWithAdjacency) - } - - /// Sets the topology of the primitives to a triangle strip with adjacency information` - /// - /// > **Note**: This is equivalent to - /// > `self.primitive_topology(PrimitiveTopology::TriangleStripWithAdjacency)`. - #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] - #[inline] - pub fn triangle_strip_with_adjacency(self) -> Self { - self.primitive_topology(PrimitiveTopology::TriangleStripWithAdjacency) - } - - /// Sets the topology of the primitives to a list of patches. Can only be used and must be used - /// with a tessellation shader. - /// - /// > **Note**: This is equivalent to - /// > `self.primitive_topology(PrimitiveTopology::PatchList)`. - #[deprecated(since = "0.27.0", note = "Use `input_assembly_state` instead")] - #[inline] - pub fn patch_list(self) -> Self { - self.primitive_topology(PrimitiveTopology::PatchList) - } - - /// Sets the viewports to some value, and the scissor boxes to boxes that always cover the - /// whole viewport. - #[deprecated(since = "0.27.0", note = "Use `viewport_state` instead")] - pub fn viewports(self, viewports: I) -> Self - where - I: IntoIterator, - { - self.viewports_scissors(viewports.into_iter().map(|v| (v, Scissor::irrelevant()))) - } - - /// Sets the characteristics of viewports and scissor boxes in advance. - #[deprecated(since = "0.27.0", note = "Use `viewport_state` instead")] - pub fn viewports_scissors(mut self, viewports: I) -> Self - where - I: IntoIterator, - { - self.viewport_state = ViewportState::Fixed { - data: viewports.into_iter().collect(), - }; - self - } - - /// Sets the scissor boxes to some values, and viewports to dynamic. The viewports will - /// need to be set before drawing. - #[deprecated(since = "0.27.0", note = "Use `viewport_state` instead")] - pub fn viewports_dynamic_scissors_fixed(mut self, scissors: I) -> Self - where - I: IntoIterator, - { - self.viewport_state = ViewportState::FixedScissor { - scissors: scissors.into_iter().collect(), - viewport_count_dynamic: false, - }; - self - } - - /// Sets the viewports to dynamic, and the scissor boxes to boxes that always cover the whole - /// viewport. The viewports will need to be set before drawing. - #[deprecated(since = "0.27.0", note = "Use `viewport_state` instead")] - #[inline] - pub fn viewports_dynamic_scissors_irrelevant(mut self, num: u32) -> Self { - self.viewport_state = ViewportState::FixedScissor { - scissors: (0..num).map(|_| Scissor::irrelevant()).collect(), - viewport_count_dynamic: false, - }; - self - } - - /// Sets the viewports to some values, and scissor boxes to dynamic. The scissor boxes will - /// need to be set before drawing. - #[deprecated(since = "0.27.0", note = "Use `viewport_state` instead")] - pub fn viewports_fixed_scissors_dynamic(mut self, viewports: I) -> Self - where - I: IntoIterator, - { - self.viewport_state = ViewportState::FixedViewport { - viewports: viewports.into_iter().collect(), - scissor_count_dynamic: false, - }; - self - } - - /// Sets the viewports and scissor boxes to dynamic. They will both need to be set before - /// drawing. - #[deprecated(since = "0.27.0", note = "Use `viewport_state` instead")] - #[inline] - pub fn viewports_scissors_dynamic(mut self, count: u32) -> Self { - self.viewport_state = ViewportState::Dynamic { - count, - viewport_count_dynamic: false, - scissor_count_dynamic: false, - }; - self - } - - /// If true, then the depth value of the vertices will be clamped to the range `[0.0 ; 1.0]`. - /// If false, fragments whose depth is outside of this range will be discarded before the - /// fragment shader even runs. - #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] - #[inline] - pub fn depth_clamp(mut self, clamp: bool) -> Self { - self.rasterization_state.depth_clamp_enable = clamp; - self - } - - /// Sets the front-facing faces to counter-clockwise faces. This is the default. - /// - /// Triangles whose vertices are oriented counter-clockwise on the screen will be considered - /// as facing their front. Otherwise they will be considered as facing their back. - #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] - #[inline] - pub fn front_face_counter_clockwise(mut self) -> Self { - self.rasterization_state.front_face = StateMode::Fixed(FrontFace::CounterClockwise); - self - } - - /// Sets the front-facing faces to clockwise faces. - /// - /// Triangles whose vertices are oriented clockwise on the screen will be considered - /// as facing their front. Otherwise they will be considered as facing their back. - #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] - #[inline] - pub fn front_face_clockwise(mut self) -> Self { - self.rasterization_state.front_face = StateMode::Fixed(FrontFace::Clockwise); - self - } - - /// Sets backface culling as disabled. This is the default. - #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] - #[inline] - pub fn cull_mode_disabled(mut self) -> Self { - self.rasterization_state.cull_mode = StateMode::Fixed(CullMode::None); - self - } - - /// Sets backface culling to front faces. The front faces (as chosen with the `front_face_*` - /// methods) will be discarded by the GPU when drawing. - #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] - #[inline] - pub fn cull_mode_front(mut self) -> Self { - self.rasterization_state.cull_mode = StateMode::Fixed(CullMode::Front); - self - } - - /// Sets backface culling to back faces. Faces that are not facing the front (as chosen with - /// the `front_face_*` methods) will be discarded by the GPU when drawing. - #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] - #[inline] - pub fn cull_mode_back(mut self) -> Self { - self.rasterization_state.cull_mode = StateMode::Fixed(CullMode::Back); - self - } - - /// Sets backface culling to both front and back faces. All the faces will be discarded. - /// - /// > **Note**: This option exists for the sake of completeness. It has no known practical - /// > usage. - #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] - #[inline] - pub fn cull_mode_front_and_back(mut self) -> Self { - self.rasterization_state.cull_mode = StateMode::Fixed(CullMode::FrontAndBack); - self - } - - /// Sets the polygon mode to "fill". This is the default. - #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] - #[inline] - pub fn polygon_mode_fill(mut self) -> Self { - self.rasterization_state.polygon_mode = PolygonMode::Fill; - self - } - - /// Sets the polygon mode to "line". Triangles will each be turned into three lines. - #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] - #[inline] - pub fn polygon_mode_line(mut self) -> Self { - self.rasterization_state.polygon_mode = PolygonMode::Line; - self - } - - /// Sets the polygon mode to "point". Triangles and lines will each be turned into three points. - #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] - #[inline] - pub fn polygon_mode_point(mut self) -> Self { - self.rasterization_state.polygon_mode = PolygonMode::Point; - self - } - - /// Sets the width of the lines, if the GPU needs to draw lines. The default is `1.0`. - #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] - #[inline] - pub fn line_width(mut self, value: f32) -> Self { - self.rasterization_state.line_width = StateMode::Fixed(value); - self - } - - /// Sets the width of the lines as dynamic, which means that you will need to set this value - /// when drawing. - #[deprecated(since = "0.27.0", note = "Use `rasterization_state` instead")] - #[inline] - pub fn line_width_dynamic(mut self) -> Self { - self.rasterization_state.line_width = StateMode::Dynamic; - self - } - - /// Disables sample shading. The fragment shader will only be run once per fragment (ie. per - /// pixel) and not once by sample. The output will then be copied in all of the covered - /// samples. - /// - /// Sample shading is disabled by default. - #[deprecated(since = "0.27.0", note = "Use `multisample_state` instead")] - #[inline] - pub fn sample_shading_disabled(mut self) -> Self { - self.multisample_state.sample_shading = None; - self - } - - /// Enables sample shading. The fragment shader will be run once per sample at the borders of - /// the object you're drawing. - /// - /// Enabling sampling shading requires the `sample_rate_shading` feature to be enabled on the - /// device. - /// - /// The `min_fract` parameter is the minimum fraction of samples shading. For example if its - /// value is 0.5, then the fragment shader will run for at least half of the samples. The other - /// half of the samples will get their values determined automatically. - /// - /// Sample shading is disabled by default. - /// - /// # Panics - /// - /// - Panics if `min_fract` is not between 0.0 and 1.0. - #[deprecated(since = "0.27.0", note = "Use `multisample_state` instead")] - #[inline] - pub fn sample_shading_enabled(mut self, min_fract: f32) -> Self { - assert!((0.0..=1.0).contains(&min_fract)); - self.multisample_state.sample_shading = Some(min_fract); - self - } - - // TODO: doc - #[deprecated(since = "0.27.0", note = "Use `multisample_state` instead")] - pub fn alpha_to_coverage_disabled(mut self) -> Self { - self.multisample_state.alpha_to_coverage_enable = false; - self - } - - // TODO: doc - #[deprecated(since = "0.27.0", note = "Use `multisample_state` instead")] - pub fn alpha_to_coverage_enabled(mut self) -> Self { - self.multisample_state.alpha_to_coverage_enable = true; - self - } - - /// Disables alpha-to-one. - /// - /// Alpha-to-one is disabled by default. - #[deprecated(since = "0.27.0", note = "Use `multisample_state` instead")] - #[inline] - pub fn alpha_to_one_disabled(mut self) -> Self { - self.multisample_state.alpha_to_one_enable = false; - self - } - - /// Enables alpha-to-one. The alpha component of the first color output of the fragment shader - /// will be replaced by the value `1.0`. - /// - /// Enabling alpha-to-one requires the `alpha_to_one` feature to be enabled on the device. - /// - /// Alpha-to-one is disabled by default. - #[deprecated(since = "0.27.0", note = "Use `multisample_state` instead")] - #[inline] - pub fn alpha_to_one_enabled(mut self) -> Self { - self.multisample_state.alpha_to_one_enable = true; - self - } - - /// Sets the depth/stencil state. - #[deprecated(since = "0.27.0", note = "Use `depth_stencil_state` instead")] - #[inline] - pub fn depth_stencil(self, depth_stencil_state: DepthStencilState) -> Self { - self.depth_stencil_state(depth_stencil_state) - } - - /// Sets the depth/stencil tests as disabled. - /// - /// > **Note**: This is a shortcut for all the other `depth_*` and `depth_stencil_*` methods - /// > of the builder. - #[deprecated(since = "0.27.0", note = "Use `depth_stencil_state` instead")] - #[inline] - pub fn depth_stencil_disabled(mut self) -> Self { - self.depth_stencil_state = DepthStencilState::disabled(); - self - } - - /// Sets the depth/stencil tests as a simple depth test and no stencil test. - /// - /// > **Note**: This is a shortcut for setting the depth test to `Less`, the depth write Into - /// > ` true` and disable the stencil test. - #[deprecated(since = "0.27.0", note = "Use `depth_stencil_state` instead")] - #[inline] - pub fn depth_stencil_simple_depth(mut self) -> Self { - self.depth_stencil_state = DepthStencilState::simple_depth_test(); - self - } - - /// Sets whether the depth buffer will be written. - #[deprecated(since = "0.27.0", note = "Use `depth_stencil_state` instead")] - #[inline] - pub fn depth_write(mut self, write: bool) -> Self { - let depth_state = self - .depth_stencil_state - .depth - .get_or_insert(Default::default()); - depth_state.write_enable = StateMode::Fixed(write); - self - } - - #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] - // TODO: When this method is removed, also remove the special casing in `with_pipeline_layout` - // for self.color_blend_state.attachments.len() == 1 - #[inline] - pub fn blend_collective(mut self, blend: AttachmentBlend) -> Self { - self.color_blend_state.attachments = vec![ColorBlendAttachmentState { - blend: Some(blend), - color_write_mask: ColorComponents::all(), - color_write_enable: StateMode::Fixed(true), - }]; - self - } - - #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] - pub fn blend_individual(mut self, blend: I) -> Self - where - I: IntoIterator, - { - self.color_blend_state.attachments = blend - .into_iter() - .map(|x| ColorBlendAttachmentState { - blend: Some(x), - color_write_mask: ColorComponents::all(), - color_write_enable: StateMode::Fixed(true), - }) - .collect(); - self - } - - /// Each fragment shader output will have its value directly written to the framebuffer - /// attachment. This is the default. - #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] - // TODO: When this method is removed, also remove the special casing in `with_pipeline_layout` - // for self.color_blend_state.attachments.len() == 1 - #[inline] - pub fn blend_pass_through(mut self) -> Self { - self.color_blend_state.attachments = vec![ColorBlendAttachmentState { - blend: None, - color_write_mask: ColorComponents::all(), - color_write_enable: StateMode::Fixed(true), - }]; - self - } - - #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] - // TODO: When this method is removed, also remove the special casing in `with_pipeline_layout` - // for self.color_blend_state.attachments.len() == 1 - #[inline] - pub fn blend_alpha_blending(mut self) -> Self { - self.color_blend_state.attachments = vec![ColorBlendAttachmentState { - blend: Some(AttachmentBlend::alpha()), - color_write_mask: ColorComponents::all(), - color_write_enable: StateMode::Fixed(true), - }]; - self - } - - #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] - #[inline] - pub fn blend_logic_op(mut self, logic_op: LogicOp) -> Self { - self.color_blend_state.logic_op = Some(StateMode::Fixed(logic_op)); - self - } - - /// Sets the logic operation as disabled. This is the default. - #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] - #[inline] - pub fn blend_logic_op_disabled(mut self) -> Self { - self.color_blend_state.logic_op = None; - self - } - - /// Sets the blend constant. The default is `[0.0, 0.0, 0.0, 0.0]`. - /// - /// The blend constant is used for some blending calculations. It is irrelevant otherwise. - #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] - #[inline] - pub fn blend_constants(mut self, constants: [f32; 4]) -> Self { - self.color_blend_state.blend_constants = StateMode::Fixed(constants); - self - } - - /// Sets the blend constant value as dynamic. Its value will need to be set before drawing. - /// - /// The blend constant is used for some blending calculations. It is irrelevant otherwise. - #[deprecated(since = "0.27.0", note = "Use `color_blend_state` instead")] - #[inline] - pub fn blend_constants_dynamic(mut self) -> Self { - self.color_blend_state.blend_constants = StateMode::Dynamic; + self.color_blend_state = Some(color_blend_state); self } /// Sets the render pass subpass to use. - pub fn render_pass(self, render_pass: impl Into) -> Self { - GraphicsPipelineBuilder { - render_pass: Some(render_pass.into()), - cache: self.cache, + /// + /// This state is always used, and must be provided. + /// + /// The default value is `None`. + pub fn render_pass(mut self, render_pass: impl Into) -> Self { + self.render_pass = Some(render_pass.into()); + self + } - 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, - } + /// Sets the discard rectangle state. + /// + /// This state is always used if it is provided. + /// + /// The default value is `None`. + #[inline] + pub fn discard_rectangle_state( + mut self, + discard_rectangle_state: DiscardRectangleState, + ) -> Self { + self.discard_rectangle_state = Some(discard_rectangle_state); + self } /// Enable caching of this pipeline via a PipelineCache object. @@ -4549,37 +3886,3 @@ impl<'vs, 'tcs, 'tes, 'gs, 'fs, Vdef, Vss, Tcss, Tess, Gss, Fss> 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(), - } - } -} diff --git a/vulkano/src/pipeline/graphics/creation_error.rs b/vulkano/src/pipeline/graphics/creation_error.rs index 50a8b36c..e51b31e5 100644 --- a/vulkano/src/pipeline/graphics/creation_error.rs +++ b/vulkano/src/pipeline/graphics/creation_error.rs @@ -12,7 +12,7 @@ use crate::{ descriptor_set::layout::DescriptorSetLayoutCreationError, format::{Format, NumericType}, pipeline::layout::{PipelineLayoutCreationError, PipelineLayoutSupersetError}, - shader::{ShaderInterfaceMismatchError, ShaderScalarType}, + shader::{ShaderInterfaceMismatchError, ShaderScalarType, ShaderStage, SpecializationConstant}, OomError, RequirementNotMet, RequiresOneOf, VulkanError, }; use std::{ @@ -21,7 +21,7 @@ use std::{ }; /// Error that can happen when creating a graphics pipeline. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq)] pub enum GraphicsPipelineCreationError { RequirementNotMet { required_for: &'static str, @@ -47,9 +47,6 @@ pub enum GraphicsPipelineCreationError { /// The pipeline layout is not compatible with what the shaders expect. IncompatiblePipelineLayout(PipelineLayoutSupersetError), - /// The provided specialization constants are not compatible with what the shader expects. - IncompatibleSpecializationConstants, - /// The vertex definition is not compatible with the input of the vertex shader. IncompatibleVertexDefinition(IncompatibleVertexDefinitionError), @@ -157,15 +154,48 @@ pub enum GraphicsPipelineCreationError { /// Not enough memory. OomError(OomError), + /// Only one tessellation shader stage was provided, the other was not. + OtherTessellationShaderStageMissing, + /// Error while creating a descriptor set layout object. DescriptorSetLayoutCreationError(DescriptorSetLayoutCreationError), /// Error while creating the pipeline layout object. PipelineLayoutCreationError(PipelineLayoutCreationError), + /// The value provided for a shader specialization constant has a + /// different type than the constant's default value. + ShaderSpecializationConstantTypeMismatch { + stage_index: usize, + constant_id: u32, + default_value: SpecializationConstant, + provided_value: SpecializationConstant, + }, + + /// A shader stage was provided more than once. + ShaderStageDuplicate { + stage_index: usize, + stage: ShaderStage, + }, + + /// A shader stage is not a graphics shader. + ShaderStageInvalid { + stage_index: usize, + stage: ShaderStage, + }, + + /// The configuration of the pipeline does not use a shader stage, but it was provided. + ShaderStageUnused { stage: ShaderStage }, + /// The output interface of one shader and the input interface of the next shader do not match. ShaderStagesMismatch(ShaderInterfaceMismatchError), + /// The configuration of the pipeline requires a state to be provided, but it was not. + StateMissing { state: &'static str }, + + /// The configuration of the pipeline does not use a state, but it was provided. + StateUnused { state: &'static str }, + /// The stencil attachment has a format that does not support that usage. StencilAttachmentFormatUsageNotSupported, @@ -198,6 +228,9 @@ pub enum GraphicsPipelineCreationError { /// The format specified by a vertex input attribute is not supported for vertex buffers. VertexInputAttributeUnsupportedFormat { location: u32, format: Format }, + /// No vertex shader stage was provided. + VertexShaderStageMissing, + /// The minimum or maximum bounds of viewports have been exceeded. ViewportBoundsExceeded, @@ -261,11 +294,6 @@ impl Display for GraphicsPipelineCreationError { f, "the pipeline layout is not compatible with what the shaders expect", ), - Self::IncompatibleSpecializationConstants => write!( - f, - "the provided specialization constants are not compatible with what the shader \ - expects", - ), Self::IncompatibleVertexDefinition(_) => write!( f, "the vertex definition is not compatible with the input of the vertex shader", @@ -336,17 +364,65 @@ impl Display for GraphicsPipelineCreationError { "the stencil attachment of the render pass does not match the stencil test", ), Self::OomError(_) => write!(f, "not enough memory available"), + Self::OtherTessellationShaderStageMissing => write!( + f, + "only one tessellation shader stage was provided, the other was not", + ), Self::DescriptorSetLayoutCreationError(_) => { write!(f, "error while creating a descriptor set layout object") } Self::PipelineLayoutCreationError(_) => { write!(f, "error while creating the pipeline layout object") } + Self::ShaderSpecializationConstantTypeMismatch { + stage_index, + constant_id, + default_value, + provided_value, + } => write!( + f, + "the value provided for shader {} specialization constant id {} ({:?}) has a \ + different type than the constant's default value ({:?})", + stage_index, constant_id, provided_value, default_value, + ), + Self::ShaderStageDuplicate { + stage_index, + stage, + } => write!( + f, + "the shader stage at index {} (stage: {:?}) was provided more than once", + stage_index, stage, + ), + Self::ShaderStageInvalid { + stage_index, + stage, + } => write!( + f, + "the shader stage at index {} (stage: {:?}) is not a graphics shader", + stage_index, stage, + ), + Self::ShaderStageUnused { + stage, + } => write!( + f, + "the configuration of the pipeline does not use the `{:?}` shader stage, but it was provided", + stage, + ), Self::ShaderStagesMismatch(_) => write!( f, "the output interface of one shader and the input interface of the next shader do \ not match", ), + Self::StateMissing { state } => write!( + f, + "the configuration of the pipeline requires `{}` to be provided, but it was not", + state, + ), + Self::StateUnused { state } => write!( + f, + "the configuration of the pipeline does not use `{}`, but it was provided", + state, + ), Self::StencilAttachmentFormatUsageNotSupported => write!( f, "the stencil attachment has a format that does not support that usage", @@ -391,6 +467,10 @@ impl Display for GraphicsPipelineCreationError { for vertex buffers", format, location, ), + Self::VertexShaderStageMissing => write!( + f, + "no vertex shader stage was provided", + ), Self::ViewportBoundsExceeded => write!( f, "the minimum or maximum bounds of viewports have been exceeded", diff --git a/vulkano/src/pipeline/graphics/mod.rs b/vulkano/src/pipeline/graphics/mod.rs index a85e8b51..b67d944d 100644 --- a/vulkano/src/pipeline/graphics/mod.rs +++ b/vulkano/src/pipeline/graphics/mod.rs @@ -127,19 +127,7 @@ impl GraphicsPipeline { /// Starts the building process of a graphics pipeline. Returns a builder object that you can /// fill with the various parameters. #[inline] - pub fn start() -> GraphicsPipelineBuilder< - 'static, - 'static, - 'static, - 'static, - 'static, - VertexInputState, - (), - (), - (), - (), - (), - > { + pub fn start() -> GraphicsPipelineBuilder { GraphicsPipelineBuilder::new() } diff --git a/vulkano/src/shader/mod.rs b/vulkano/src/shader/mod.rs index 298baad2..f1df1ab7 100644 --- a/vulkano/src/shader/mod.rs +++ b/vulkano/src/shader/mod.rs @@ -136,18 +136,17 @@ use crate::{ device::{Device, DeviceOwned}, format::{Format, NumericType}, image::view::ImageViewType, - macros::{impl_id_counter, vulkan_bitflags_enum}, + macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum}, pipeline::{graphics::input_assembly::PrimitiveTopology, layout::PushConstantRange}, shader::spirv::{Capability, Spirv, SpirvError}, sync::PipelineStages, - DeviceSize, OomError, Version, VulkanError, VulkanObject, + OomError, Version, VulkanError, VulkanObject, }; use ahash::{HashMap, HashSet}; use std::{ borrow::Cow, collections::hash_map::Entry, error::Error, - ffi::{CStr, CString}, fmt::{Display, Error as FmtError, Formatter}, mem, mem::MaybeUninit, @@ -159,6 +158,8 @@ use std::{ pub mod reflect; pub mod spirv; +use bytemuck::bytes_of; +use half::f16; use spirv::ExecutionModel; // Generated by build.rs @@ -170,7 +171,8 @@ pub struct ShaderModule { handle: ash::vk::ShaderModule, device: Arc, id: NonZeroU64, - entry_points: HashMap>, + entry_point_map: HashMap>, + entry_point_infos: Vec, } impl ShaderModule { @@ -184,7 +186,7 @@ impl ShaderModule { pub unsafe fn from_words( device: Arc, words: &[u32], - ) -> Result, ShaderCreationError> { + ) -> Result, ShaderModuleCreationError> { let spirv = Spirv::new(words)?; Self::from_words_with_data( @@ -206,7 +208,7 @@ impl ShaderModule { pub unsafe fn from_bytes( device: Arc, bytes: &[u8], - ) -> Result, ShaderCreationError> { + ) -> Result, ShaderModuleCreationError> { assert!((bytes.len() % 4) == 0); Self::from_words( @@ -232,10 +234,10 @@ impl ShaderModule { spirv_version: Version, spirv_capabilities: impl IntoIterator, spirv_extensions: impl IntoIterator, - entry_points: impl IntoIterator, - ) -> Result, ShaderCreationError> { + entry_points: impl IntoIterator, + ) -> Result, ShaderModuleCreationError> { if let Err(reason) = check_spirv_version(&device, spirv_version) { - return Err(ShaderCreationError::SpirvVersionNotSupported { + return Err(ShaderModuleCreationError::SpirvVersionNotSupported { version: spirv_version, reason, }); @@ -243,7 +245,7 @@ impl ShaderModule { for &capability in spirv_capabilities { if let Err(reason) = check_spirv_capability(&device, capability) { - return Err(ShaderCreationError::SpirvCapabilityNotSupported { + return Err(ShaderModuleCreationError::SpirvCapabilityNotSupported { capability, reason, }); @@ -252,7 +254,7 @@ impl ShaderModule { for extension in spirv_extensions { if let Err(reason) = check_spirv_extension(&device, extension) { - return Err(ShaderCreationError::SpirvExtensionNotSupported { + return Err(ShaderModuleCreationError::SpirvExtensionNotSupported { extension: extension.to_owned(), reason, }); @@ -280,26 +282,17 @@ impl ShaderModule { output.assume_init() }; - let entries = entry_points.into_iter().collect::>(); - let entry_points = entries - .iter() - .map(|(name, _, _)| name) - .collect::>() - .iter() - .map(|name| { - ( - (*name).clone(), - entries - .iter() - .filter_map(|(entry_name, entry_model, info)| { - if &entry_name == name { - Some((*entry_model, info.clone())) - } else { - None - } - }) - .collect::>(), - ) + let mut entry_point_map: HashMap> = + HashMap::default(); + let entry_point_infos: Vec<_> = entry_points + .into_iter() + .enumerate() + .map(|(index, info)| { + entry_point_map + .entry(info.name.clone()) + .or_default() + .insert(ExecutionModel::from(&info.execution), index); + info }) .collect(); @@ -307,7 +300,8 @@ impl ShaderModule { handle, device, id: Self::next_id(), - entry_points, + entry_point_map, + entry_point_infos, })) } @@ -322,8 +316,8 @@ impl ShaderModule { spirv_version: Version, spirv_capabilities: impl IntoIterator, spirv_extensions: impl IntoIterator, - entry_points: impl IntoIterator, - ) -> Result, ShaderCreationError> { + entry_points: impl IntoIterator, + ) -> Result, ShaderModuleCreationError> { assert!((bytes.len() % 4) == 0); Self::from_words_with_data( @@ -343,13 +337,12 @@ impl ShaderModule { /// point with that name exists in the shader module or if multiple entry points with the same /// name exist. #[inline] - pub fn entry_point<'a>(&'a self, name: &str) -> Option> { - self.entry_points.get(name).and_then(|infos| { + pub fn entry_point(self: &Arc, name: &str) -> Option { + self.entry_point_map.get(name).and_then(|infos| { if infos.len() == 1 { - infos.iter().next().map(|(_, info)| EntryPoint { - module: self, - name: CString::new(name).unwrap(), - info, + infos.iter().next().map(|(_, &info_index)| EntryPoint { + module: self.clone(), + info_index, }) } else { None @@ -360,16 +353,15 @@ impl ShaderModule { /// Returns information about the entry point with the provided name and execution model. /// Returns `None` if no entry and execution model exists in the shader module. #[inline] - pub fn entry_point_with_execution<'a>( - &'a self, + pub fn entry_point_with_execution( + self: &Arc, name: &str, execution: ExecutionModel, - ) -> Option> { - self.entry_points.get(name).and_then(|infos| { - infos.get(&execution).map(|info| EntryPoint { - module: self, - name: CString::new(name).unwrap(), - info, + ) -> Option { + self.entry_point_map.get(name).and_then(|infos| { + infos.get(&execution).map(|&info_index| EntryPoint { + module: self.clone(), + info_index, }) }) } @@ -403,102 +395,14 @@ unsafe impl DeviceOwned for ShaderModule { impl_id_counter!(ShaderModule); -/// Error that can happen when creating a new shader module. -#[derive(Clone, Debug)] -pub enum ShaderCreationError { - OomError(OomError), - SpirvCapabilityNotSupported { - capability: Capability, - reason: ShaderSupportError, - }, - SpirvError(SpirvError), - SpirvExtensionNotSupported { - extension: String, - reason: ShaderSupportError, - }, - SpirvVersionNotSupported { - version: Version, - reason: ShaderSupportError, - }, -} - -impl Error for ShaderCreationError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::OomError(err) => Some(err), - Self::SpirvCapabilityNotSupported { reason, .. } => Some(reason), - Self::SpirvError(err) => Some(err), - Self::SpirvExtensionNotSupported { reason, .. } => Some(reason), - Self::SpirvVersionNotSupported { reason, .. } => Some(reason), - } - } -} - -impl Display for ShaderCreationError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::OomError(_) => write!(f, "not enough memory available"), - Self::SpirvCapabilityNotSupported { capability, .. } => write!( - f, - "the SPIR-V capability {:?} enabled by the shader is not supported by the device", - capability, - ), - Self::SpirvError(_) => write!(f, "the SPIR-V module could not be read"), - Self::SpirvExtensionNotSupported { extension, .. } => write!( - f, - "the SPIR-V extension {} enabled by the shader is not supported by the device", - extension, - ), - Self::SpirvVersionNotSupported { version, .. } => write!( - f, - "the shader uses SPIR-V version {}.{}, which is not supported by the device", - version.major, version.minor, - ), - } - } -} - -impl From for ShaderCreationError { - fn from(err: VulkanError) -> Self { - Self::OomError(err.into()) - } -} - -impl From for ShaderCreationError { - fn from(err: SpirvError) -> Self { - Self::SpirvError(err) - } -} - -/// Error that can happen when checking whether a shader is supported by a device. -#[derive(Clone, Copy, Debug)] -pub enum ShaderSupportError { - NotSupportedByVulkan, - RequirementsNotMet(&'static [&'static str]), -} - -impl Error for ShaderSupportError {} - -impl Display for ShaderSupportError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - Self::NotSupportedByVulkan => write!(f, "not supported by Vulkan"), - Self::RequirementsNotMet(requirements) => write!( - f, - "at least one of the following must be available/enabled on the device: {}", - requirements.join(", "), - ), - } - } -} - /// The information associated with a single entry point in a shader. #[derive(Clone, Debug)] pub struct EntryPointInfo { + pub name: String, pub execution: ShaderExecution, pub descriptor_binding_requirements: HashMap<(u32, u32), DescriptorBindingRequirements>, pub push_constant_requirements: Option, - pub specialization_constant_requirements: HashMap, + pub specialization_constants: HashMap, pub input_interface: ShaderInterface, pub output_interface: ShaderInterface, } @@ -507,75 +411,28 @@ pub struct EntryPointInfo { /// /// Can be obtained by calling [`entry_point`](ShaderModule::entry_point) on the shader module. #[derive(Clone, Debug)] -pub struct EntryPoint<'a> { - module: &'a ShaderModule, - name: CString, - info: &'a EntryPointInfo, +pub struct EntryPoint { + module: Arc, + info_index: usize, } -impl<'a> EntryPoint<'a> { +impl EntryPoint { /// Returns the module this entry point comes from. #[inline] - pub fn module(&self) -> &'a ShaderModule { - self.module + pub fn module(&self) -> &Arc { + &self.module } - /// Returns the name of the entry point. + /// Returns information about the entry point. #[inline] - pub fn name(&self) -> &CStr { - &self.name - } - - /// Returns the execution model of the shader. - #[inline] - pub fn execution(&self) -> &ShaderExecution { - &self.info.execution - } - - /// Returns the descriptor binding requirements. - #[inline] - pub fn descriptor_binding_requirements( - &self, - ) -> impl ExactSizeIterator { - self.info - .descriptor_binding_requirements - .iter() - .map(|(k, v)| (*k, v)) - } - - /// Returns the push constant requirements. - #[inline] - pub fn push_constant_requirements(&self) -> Option<&PushConstantRange> { - self.info.push_constant_requirements.as_ref() - } - - /// Returns the specialization constant requirements. - #[inline] - pub fn specialization_constant_requirements( - &self, - ) -> impl ExactSizeIterator { - self.info - .specialization_constant_requirements - .iter() - .map(|(k, v)| (*k, v)) - } - - /// Returns the input attributes used by the shader stage. - #[inline] - pub fn input_interface(&self) -> &ShaderInterface { - &self.info.input_interface - } - - /// Returns the output attributes used by the shader stage. - #[inline] - pub fn output_interface(&self) -> &ShaderInterface { - &self.info.output_interface + pub fn info(&self) -> &EntryPointInfo { + &self.module.entry_point_infos[self.info_index] } } /// The mode in which a shader executes. This includes both information about the shader type/stage, /// and additional data relevant to particular shader types. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum ShaderExecution { Vertex, TessellationControl, @@ -594,6 +451,28 @@ pub enum ShaderExecution { SubpassShading, } +impl From<&ShaderExecution> for ExecutionModel { + fn from(value: &ShaderExecution) -> Self { + match value { + ShaderExecution::Vertex => Self::Vertex, + ShaderExecution::TessellationControl => Self::TessellationControl, + ShaderExecution::TessellationEvaluation => Self::TessellationEvaluation, + ShaderExecution::Geometry(_) => Self::Geometry, + ShaderExecution::Fragment(_) => Self::Fragment, + ShaderExecution::Compute => Self::GLCompute, + ShaderExecution::RayGeneration => Self::RayGenerationKHR, + ShaderExecution::AnyHit => Self::AnyHitKHR, + ShaderExecution::ClosestHit => Self::ClosestHitKHR, + ShaderExecution::Miss => Self::MissKHR, + ShaderExecution::Intersection => Self::IntersectionKHR, + ShaderExecution::Callable => Self::CallableKHR, + ShaderExecution::Task => Self::TaskNV, + ShaderExecution::Mesh => Self::MeshNV, + ShaderExecution::SubpassShading => todo!(), + } + } +} + /*#[derive(Clone, Copy, Debug)] pub struct TessellationShaderExecution { pub num_output_vertices: u32, @@ -851,154 +730,197 @@ impl DescriptorRequirements { } } -/// An error that can be returned when trying to create the intersection of two -/// `DescriptorBindingRequirements` values. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum DescriptorBindingRequirementsIncompatible { - /// The allowed descriptor types of the descriptors do not overlap. - DescriptorType, - /// The descriptors require different formats. - ImageFormat, - /// The descriptors require different scalar types. - ImageScalarType, - /// The multisampling requirements of the descriptors differ. - ImageMultisampled, - /// The descriptors require different image view types. - ImageViewType, -} - -impl Error for DescriptorBindingRequirementsIncompatible {} - -impl Display for DescriptorBindingRequirementsIncompatible { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - match self { - DescriptorBindingRequirementsIncompatible::DescriptorType => write!( - f, - "the allowed descriptor types of the two descriptors do not overlap", - ), - DescriptorBindingRequirementsIncompatible::ImageFormat => { - write!(f, "the descriptors require different formats",) - } - DescriptorBindingRequirementsIncompatible::ImageMultisampled => write!( - f, - "the multisampling requirements of the descriptors differ", - ), - DescriptorBindingRequirementsIncompatible::ImageScalarType => { - write!(f, "the descriptors require different scalar types",) - } - DescriptorBindingRequirementsIncompatible::ImageViewType => { - write!(f, "the descriptors require different image view types",) - } - } - } -} - -/// The requirements imposed by a shader on a specialization constant. -#[derive(Clone, Copy, Debug)] -pub struct SpecializationConstantRequirements { - pub size: DeviceSize, -} - -/// Trait for types that contain specialization data for shaders. -/// -/// Shader modules can contain what is called *specialization constants*. They are the same as -/// constants except that their values can be defined when you create a compute pipeline or a -/// graphics pipeline. Doing so is done by passing a type that implements the -/// `SpecializationConstants` trait and that stores the values in question. The `descriptors()` -/// method of this trait indicates how to grab them. -/// -/// Boolean specialization constants must be stored as 32bits integers, where `0` means `false` and -/// any non-zero value means `true`. Integer and floating-point specialization constants are -/// stored as their Rust equivalent. -/// -/// This trait is implemented on `()` for shaders that don't have any specialization constant. -/// -/// # Examples -/// -/// ```rust -/// use vulkano::shader::SpecializationConstants; -/// use vulkano::shader::SpecializationMapEntry; -/// -/// #[repr(C)] // `#[repr(C)]` guarantees that the struct has a specific layout -/// struct MySpecConstants { -/// my_integer_constant: i32, -/// a_boolean: u32, -/// floating_point: f32, -/// } -/// -/// unsafe impl SpecializationConstants for MySpecConstants { -/// fn descriptors() -> &'static [SpecializationMapEntry] { -/// static DESCRIPTORS: [SpecializationMapEntry; 3] = [ -/// SpecializationMapEntry { -/// constant_id: 0, -/// offset: 0, -/// size: 4, -/// }, -/// SpecializationMapEntry { -/// constant_id: 1, -/// offset: 4, -/// size: 4, -/// }, -/// SpecializationMapEntry { -/// constant_id: 2, -/// offset: 8, -/// size: 4, -/// }, -/// ]; -/// -/// &DESCRIPTORS -/// } -/// } -/// ``` -/// -/// # Safety -/// -/// - The `SpecializationMapEntry` returned must contain valid offsets and sizes. -/// - The size of each `SpecializationMapEntry` must match the size of the corresponding constant -/// (`4` for booleans). -pub unsafe trait SpecializationConstants { - /// Returns descriptors of the struct's layout. - fn descriptors() -> &'static [SpecializationMapEntry]; -} - -unsafe impl SpecializationConstants for () { - #[inline] - fn descriptors() -> &'static [SpecializationMapEntry] { - &[] - } -} - -/// Describes an individual constant to set in the shader. Also a field in the struct. -// Implementation note: has the same memory representation as a `VkSpecializationMapEntry`. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[repr(C)] -pub struct SpecializationMapEntry { - /// Identifier of the constant in the shader that corresponds to this field. +/// Specifies a single shader stage when creating a pipeline. +#[derive(Clone, Debug)] +pub struct PipelineShaderStageCreateInfo { + /// Specifies how to create the shader stage. /// - /// For SPIR-V, this must be the value of the `SpecId` decoration applied to the specialization - /// constant. - /// For GLSL, this must be the value of `N` in the `layout(constant_id = N)` attribute applied - /// to a constant. - pub constant_id: u32, + /// The default value is empty. + pub flags: PipelineShaderStageCreateFlags, - /// Offset within the struct where the data can be found. - pub offset: u32, + /// The shader entry point for the stage. + /// + /// There is no default value. + pub entry_point: EntryPoint, - /// Size of the data in bytes. Must match the size of the constant (`4` for booleans). - pub size: usize, + /// Values for the specialization constants in the shader, indexed by their `constant_id`. + /// + /// Specialization constants are constants whose value can be overridden when you create + /// a pipeline. When provided, they must have the same type as defined in the shader. + /// Constants that are not given a value here will have the default value that was specified + /// for them in the shader code. + /// + /// The default value is empty. + pub specialization_info: HashMap, + + pub _ne: crate::NonExhaustive, } -impl From for ash::vk::SpecializationMapEntry { +impl PipelineShaderStageCreateInfo { + /// Returns a `PipelineShaderStageCreateInfo` with the specified `entry_point`. #[inline] - fn from(val: SpecializationMapEntry) -> Self { + pub fn entry_point(entry_point: EntryPoint) -> Self { Self { - constant_id: val.constant_id, - offset: val.offset, - size: val.size, + flags: PipelineShaderStageCreateFlags::empty(), + entry_point, + specialization_info: HashMap::default(), + _ne: crate::NonExhaustive(()), } } } +vulkan_bitflags! { + #[non_exhaustive] + + /// Flags that control how a pipeline shader stage is created. + PipelineShaderStageCreateFlags = PipelineShaderStageCreateFlags(u32); + + /* TODO: enable + // TODO: document + ALLOW_VARYING_SUBGROUP_SIZE = ALLOW_VARYING_SUBGROUP_SIZE { + api_version: V1_3, + device_extensions: [ext_subgroup_size_control], + }, + */ + + /* TODO: enable + // TODO: document + REQUIRE_FULL_SUBGROUPS = REQUIRE_FULL_SUBGROUPS { + api_version: V1_3, + device_extensions: [ext_subgroup_size_control], + }, + */ +} + +/// The value to provide for a specialization constant, when creating a pipeline. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum SpecializationConstant { + Bool(bool), + I8(i8), + I16(i16), + I32(i32), + I64(i64), + U8(u8), + U16(u16), + U32(u32), + U64(u64), + F16(f16), + F32(f32), + F64(f64), +} + +impl SpecializationConstant { + /// Returns the value as a byte slice. Booleans are expanded to a `VkBool32` value. + #[inline] + pub fn as_bytes(&self) -> &[u8] { + match self { + Self::Bool(false) => bytes_of(&ash::vk::FALSE), + Self::Bool(true) => bytes_of(&ash::vk::TRUE), + Self::I8(value) => bytes_of(value), + Self::I16(value) => bytes_of(value), + Self::I32(value) => bytes_of(value), + Self::I64(value) => bytes_of(value), + Self::U8(value) => bytes_of(value), + Self::U16(value) => bytes_of(value), + Self::U32(value) => bytes_of(value), + Self::U64(value) => bytes_of(value), + Self::F16(value) => bytes_of(value), + Self::F32(value) => bytes_of(value), + Self::F64(value) => bytes_of(value), + } + } + + /// Returns whether `self` and `other` have the same type, ignoring the value. + #[inline] + pub fn eq_type(&self, other: &Self) -> bool { + mem::discriminant(self) == mem::discriminant(other) + } +} + +impl From for SpecializationConstant { + #[inline] + fn from(value: bool) -> Self { + SpecializationConstant::Bool(value) + } +} + +impl From for SpecializationConstant { + #[inline] + fn from(value: i8) -> Self { + SpecializationConstant::I8(value) + } +} + +impl From for SpecializationConstant { + #[inline] + fn from(value: i16) -> Self { + SpecializationConstant::I16(value) + } +} + +impl From for SpecializationConstant { + #[inline] + fn from(value: i32) -> Self { + SpecializationConstant::I32(value) + } +} + +impl From for SpecializationConstant { + #[inline] + fn from(value: i64) -> Self { + SpecializationConstant::I64(value) + } +} + +impl From for SpecializationConstant { + #[inline] + fn from(value: u8) -> Self { + SpecializationConstant::U8(value) + } +} + +impl From for SpecializationConstant { + #[inline] + fn from(value: u16) -> Self { + SpecializationConstant::U16(value) + } +} + +impl From for SpecializationConstant { + #[inline] + fn from(value: u32) -> Self { + SpecializationConstant::U32(value) + } +} + +impl From for SpecializationConstant { + #[inline] + fn from(value: u64) -> Self { + SpecializationConstant::U64(value) + } +} + +impl From for SpecializationConstant { + #[inline] + fn from(value: f16) -> Self { + SpecializationConstant::F16(value) + } +} + +impl From for SpecializationConstant { + #[inline] + fn from(value: f32) -> Self { + SpecializationConstant::F32(value) + } +} + +impl From for SpecializationConstant { + #[inline] + fn from(value: f64) -> Self { + SpecializationConstant::F64(value) + } +} + /// Type that contains the definition of an interface between two shader stages, or between /// the outside and a shader stage. #[derive(Clone, Debug)] @@ -1149,54 +1071,6 @@ impl From for ShaderScalarType { } } -/// Error that can happen when the interface mismatches between two shader stages. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum ShaderInterfaceMismatchError { - /// The number of elements is not the same between the two shader interfaces. - ElementsCountMismatch { - /// Number of elements in the first interface. - self_elements: u32, - /// Number of elements in the second interface. - other_elements: u32, - }, - - /// An element is missing from one of the interfaces. - MissingElement { - /// Location of the missing element. - location: u32, - }, - - /// The type of an element does not match. - TypeMismatch { - /// Location of the element that mismatches. - location: u32, - /// Type in the first interface. - self_ty: ShaderInterfaceEntryType, - /// Type in the second interface. - other_ty: ShaderInterfaceEntryType, - }, -} - -impl Error for ShaderInterfaceMismatchError {} - -impl Display for ShaderInterfaceMismatchError { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { - write!( - f, - "{}", - match self { - ShaderInterfaceMismatchError::ElementsCountMismatch { .. } => { - "the number of elements mismatches" - } - ShaderInterfaceMismatchError::MissingElement { .. } => "an element is missing", - ShaderInterfaceMismatchError::TypeMismatch { .. } => { - "the type of an element does not match" - } - } - ) - } -} - vulkan_bitflags_enum! { #[non_exhaustive] @@ -1282,10 +1156,10 @@ vulkan_bitflags_enum! { }, } -impl From for ShaderStage { +impl From<&ShaderExecution> for ShaderStage { #[inline] - fn from(val: ShaderExecution) -> Self { - match val { + fn from(value: &ShaderExecution) -> Self { + match value { ShaderExecution::Vertex => Self::Vertex, ShaderExecution::TessellationControl => Self::TessellationControl, ShaderExecution::TessellationEvaluation => Self::TessellationEvaluation, @@ -1400,3 +1274,182 @@ fn check_spirv_version(device: &Device, mut version: Version) -> Result<(), Shad } Ok(()) } + +/// Error that can happen when creating a new shader module. +#[derive(Clone, Debug)] +pub enum ShaderModuleCreationError { + OomError(OomError), + SpirvCapabilityNotSupported { + capability: Capability, + reason: ShaderSupportError, + }, + SpirvError(SpirvError), + SpirvExtensionNotSupported { + extension: String, + reason: ShaderSupportError, + }, + SpirvVersionNotSupported { + version: Version, + reason: ShaderSupportError, + }, +} + +impl Error for ShaderModuleCreationError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::OomError(err) => Some(err), + Self::SpirvCapabilityNotSupported { reason, .. } => Some(reason), + Self::SpirvError(err) => Some(err), + Self::SpirvExtensionNotSupported { reason, .. } => Some(reason), + Self::SpirvVersionNotSupported { reason, .. } => Some(reason), + } + } +} + +impl Display for ShaderModuleCreationError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + match self { + Self::OomError(_) => write!(f, "not enough memory available"), + Self::SpirvCapabilityNotSupported { capability, .. } => write!( + f, + "the SPIR-V capability {:?} enabled by the shader is not supported by the device", + capability, + ), + Self::SpirvError(_) => write!(f, "the SPIR-V module could not be read"), + Self::SpirvExtensionNotSupported { extension, .. } => write!( + f, + "the SPIR-V extension {} enabled by the shader is not supported by the device", + extension, + ), + Self::SpirvVersionNotSupported { version, .. } => write!( + f, + "the shader uses SPIR-V version {}.{}, which is not supported by the device", + version.major, version.minor, + ), + } + } +} + +impl From for ShaderModuleCreationError { + fn from(err: VulkanError) -> Self { + Self::OomError(err.into()) + } +} + +impl From for ShaderModuleCreationError { + fn from(err: SpirvError) -> Self { + Self::SpirvError(err) + } +} + +/// Error that can happen when checking whether a shader is supported by a device. +#[derive(Clone, Copy, Debug)] +pub enum ShaderSupportError { + NotSupportedByVulkan, + RequirementsNotMet(&'static [&'static str]), +} + +impl Error for ShaderSupportError {} + +impl Display for ShaderSupportError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + match self { + Self::NotSupportedByVulkan => write!(f, "not supported by Vulkan"), + Self::RequirementsNotMet(requirements) => write!( + f, + "at least one of the following must be available/enabled on the device: {}", + requirements.join(", "), + ), + } + } +} + +/// An error that can be returned when trying to create the intersection of two +/// `DescriptorBindingRequirements` values. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DescriptorBindingRequirementsIncompatible { + /// The allowed descriptor types of the descriptors do not overlap. + DescriptorType, + /// The descriptors require different formats. + ImageFormat, + /// The descriptors require different scalar types. + ImageScalarType, + /// The multisampling requirements of the descriptors differ. + ImageMultisampled, + /// The descriptors require different image view types. + ImageViewType, +} + +impl Error for DescriptorBindingRequirementsIncompatible {} + +impl Display for DescriptorBindingRequirementsIncompatible { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + match self { + DescriptorBindingRequirementsIncompatible::DescriptorType => write!( + f, + "the allowed descriptor types of the two descriptors do not overlap", + ), + DescriptorBindingRequirementsIncompatible::ImageFormat => { + write!(f, "the descriptors require different formats",) + } + DescriptorBindingRequirementsIncompatible::ImageMultisampled => write!( + f, + "the multisampling requirements of the descriptors differ", + ), + DescriptorBindingRequirementsIncompatible::ImageScalarType => { + write!(f, "the descriptors require different scalar types",) + } + DescriptorBindingRequirementsIncompatible::ImageViewType => { + write!(f, "the descriptors require different image view types",) + } + } + } +} + +/// Error that can happen when the interface mismatches between two shader stages. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ShaderInterfaceMismatchError { + /// The number of elements is not the same between the two shader interfaces. + ElementsCountMismatch { + /// Number of elements in the first interface. + self_elements: u32, + /// Number of elements in the second interface. + other_elements: u32, + }, + + /// An element is missing from one of the interfaces. + MissingElement { + /// Location of the missing element. + location: u32, + }, + + /// The type of an element does not match. + TypeMismatch { + /// Location of the element that mismatches. + location: u32, + /// Type in the first interface. + self_ty: ShaderInterfaceEntryType, + /// Type in the second interface. + other_ty: ShaderInterfaceEntryType, + }, +} + +impl Error for ShaderInterfaceMismatchError {} + +impl Display for ShaderInterfaceMismatchError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + write!( + f, + "{}", + match self { + ShaderInterfaceMismatchError::ElementsCountMismatch { .. } => { + "the number of elements mismatches" + } + ShaderInterfaceMismatchError::MissingElement { .. } => "an element is missing", + ShaderInterfaceMismatchError::TypeMismatch { .. } => { + "the type of an element does not match" + } + } + ) + } +} diff --git a/vulkano/src/shader/reflect.rs b/vulkano/src/shader/reflect.rs index a96923ff..21087ddf 100644 --- a/vulkano/src/shader/reflect.rs +++ b/vulkano/src/shader/reflect.rs @@ -21,12 +21,12 @@ use crate::{ }, DescriptorIdentifier, DescriptorRequirements, EntryPointInfo, GeometryShaderExecution, GeometryShaderInput, ShaderExecution, ShaderInterface, ShaderInterfaceEntry, - ShaderInterfaceEntryType, ShaderScalarType, ShaderStage, - SpecializationConstantRequirements, + ShaderInterfaceEntryType, ShaderScalarType, ShaderStage, SpecializationConstant, }, DeviceSize, }; use ahash::{HashMap, HashSet}; +use half::f16; use std::borrow::Cow; /// Returns an iterator of the capabilities used by `spirv`. @@ -53,9 +53,7 @@ pub fn spirv_extensions(spirv: &Spirv) -> impl Iterator { /// Returns an iterator over all entry points in `spirv`, with information about the entry point. #[inline] -pub fn entry_points( - spirv: &Spirv, -) -> impl Iterator + '_ { +pub fn entry_points(spirv: &Spirv) -> impl Iterator + '_ { let interface_variables = interface_variables(spirv); spirv.iter_entry_point().filter_map(move |instruction| { @@ -71,7 +69,7 @@ pub fn entry_points( }; let execution = shader_execution(spirv, execution_model, function_id); - let stage = ShaderStage::from(execution); + let stage = ShaderStage::from(&execution); let descriptor_binding_requirements = inspect_entry_point( &interface_variables.descriptor_binding, @@ -80,7 +78,7 @@ pub fn entry_points( function_id, ); let push_constant_requirements = push_constant_requirements(spirv, stage); - let specialization_constant_requirements = specialization_constant_requirements(spirv); + let specialization_constants = specialization_constants(spirv); let input_interface = shader_interface( spirv, interface, @@ -99,18 +97,15 @@ pub fn entry_points( matches!(execution_model, ExecutionModel::TessellationControl), ); - Some(( - entry_point_name.clone(), - execution_model, - EntryPointInfo { - execution, - descriptor_binding_requirements, - push_constant_requirements, - specialization_constant_requirements, - input_interface, - output_interface, - }, - )) + Some(EntryPointInfo { + name: entry_point_name.clone(), + execution, + descriptor_binding_requirements, + push_constant_requirements, + specialization_constants, + input_interface, + output_interface, + }) }) } @@ -1029,57 +1024,87 @@ fn push_constant_requirements(spirv: &Spirv, stage: ShaderStage) -> Option HashMap { +/// Extracts the `SpecializationConstant` map from `spirv`. +fn specialization_constants(spirv: &Spirv) -> HashMap { + let get_constant_id = |result_id| { + spirv + .id(result_id) + .iter_decoration() + .find_map(|instruction| match *instruction { + Instruction::Decorate { + decoration: + Decoration::SpecId { + specialization_constant_id, + }, + .. + } => Some(specialization_constant_id), + _ => None, + }) + }; + spirv .iter_global() - .filter_map(|instruction| { - match *instruction { - Instruction::SpecConstantTrue { - result_type_id, - result_id, - } - | Instruction::SpecConstantFalse { - result_type_id, - result_id, - } - | Instruction::SpecConstant { - result_type_id, - result_id, - .. - } - | Instruction::SpecConstantComposite { - result_type_id, - result_id, - .. - } => spirv - .id(result_id) - .iter_decoration() - .find_map(|instruction| match *instruction { - Instruction::Decorate { - decoration: - Decoration::SpecId { - specialization_constant_id, - }, - .. - } => Some(specialization_constant_id), - _ => None, - }) - .map(|constant_id| { - let size = match *spirv.id(result_type_id).instruction() { - Instruction::TypeBool { .. } => { - // Translate bool to Bool32 - std::mem::size_of::() as DeviceSize - } - _ => size_of_type(spirv, result_type_id) - .expect("Found runtime-sized specialization constant"), - }; - (constant_id, SpecializationConstantRequirements { size }) - }), - _ => None, - } + .filter_map(|instruction| match *instruction { + Instruction::SpecConstantFalse { result_id, .. } => get_constant_id(result_id) + .map(|constant_id| (constant_id, SpecializationConstant::Bool(false))), + Instruction::SpecConstantTrue { result_id, .. } => get_constant_id(result_id) + .map(|constant_id| (constant_id, SpecializationConstant::Bool(true))), + Instruction::SpecConstant { + result_type_id, + result_id, + ref value, + } => get_constant_id(result_id).map(|constant_id| { + let value = match *spirv.id(result_type_id).instruction() { + Instruction::TypeInt { + width, signedness, .. + } => { + if width == 64 { + assert!(value.len() == 2); + } else { + assert!(value.len() == 1); + } + + match (signedness, width) { + (0, 8) => SpecializationConstant::U8(value[0] as u8), + (0, 16) => SpecializationConstant::U16(value[0] as u16), + (0, 32) => SpecializationConstant::U32(value[0]), + (0, 64) => SpecializationConstant::U64( + (value[0] as u64) | ((value[1] as u64) << 32), + ), + (1, 8) => SpecializationConstant::I8(value[0] as i8), + (1, 16) => SpecializationConstant::I16(value[0] as i16), + (1, 32) => SpecializationConstant::I32(value[0] as i32), + (1, 64) => SpecializationConstant::I64( + (value[0] as i64) | ((value[1] as i64) << 32), + ), + _ => unimplemented!(), + } + } + Instruction::TypeFloat { width, .. } => { + if width == 64 { + assert!(value.len() == 2); + } else { + assert!(value.len() == 1); + } + + match width { + 16 => SpecializationConstant::F16(f16::from_bits(value[0] as u16)), + 32 => SpecializationConstant::F32(f32::from_bits(value[0])), + 64 => SpecializationConstant::F64(f64::from_bits( + (value[0] as u64) | ((value[1] as u64) << 32), + )), + _ => unimplemented!(), + } + } + _ => panic!( + "Specialization constant {} has a non-scalar type", + constant_id + ), + }; + + (constant_id, value) + }), + _ => None, }) .collect() }